├── .DS_Store ├── README.md ├── SuspendViewDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── fanfan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── fanfan.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── SuspendViewDemo ├── .DS_Store ├── 200_200(1).jpg ├── 200_200.jpg ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── payment_close_button.imageset │ ├── Contents.json │ ├── payment_close_button.png │ ├── payment_close_button@2x.png │ └── payment_close_button@3x.png └── payment_tick_icon.imageset │ ├── Contents.json │ ├── payment_tick_icon.png │ ├── payment_tick_icon@2x.png │ └── payment_tick_icon@3x.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── ContentViewController.swift ├── ContentViewController.xib ├── Info.plist ├── Semicircle.swift ├── SuspendTool.swift ├── SuspendWindow.swift ├── UIViewController+FF.swift └── ViewController.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuspendViewDemo 2 | 模拟微信和IOS系统辅助功能悬浮窗的功能 3 | 4 | Imitate suspend window like WeChat and assistive touch in IOS 5 | 6 | 7 | # How to use it 8 | 1. Put three files, "UIViewController+FF.swift", "SuspendTool.swift" and "SuspendWindow.swift" to your project. 9 | 2. Add following code to the controller which need to be suspended. 10 | ``` 11 | self.suspend(coverImageName: imageName, type: suspendType) 12 | ``` 13 | 3. SuspendType include threee types, single, multi and none. 14 | Type single means there will be only one suspend window. 15 | Type multi means there will be several suspend windows. 16 | Type none means to cancel the suspend window. 17 | 4. CoverImageName is the image shown on suspend window. 18 | -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A907CD822105A71A00A17CB9 /* Semicircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A907CD812105A71A00A17CB9 /* Semicircle.swift */; }; 11 | A90879C720F8743400F4A6BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90879C620F8743400F4A6BB /* AppDelegate.swift */; }; 12 | A90879C920F8743400F4A6BB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90879C820F8743400F4A6BB /* ViewController.swift */; }; 13 | A90879CC20F8743400F4A6BB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A90879CA20F8743400F4A6BB /* Main.storyboard */; }; 14 | A90879CE20F8743500F4A6BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A90879CD20F8743500F4A6BB /* Assets.xcassets */; }; 15 | A90879D120F8743500F4A6BB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A90879CF20F8743500F4A6BB /* LaunchScreen.storyboard */; }; 16 | A90879D920F8785800F4A6BB /* SuspendWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90879D820F8785800F4A6BB /* SuspendWindow.swift */; }; 17 | A90879DC20F8790A00F4A6BB /* ContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90879DA20F8790A00F4A6BB /* ContentViewController.swift */; }; 18 | A90879DD20F8790A00F4A6BB /* ContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A90879DB20F8790A00F4A6BB /* ContentViewController.xib */; }; 19 | A90879DF20F87D9200F4A6BB /* 200_200.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A90879DE20F87D9200F4A6BB /* 200_200.jpg */; }; 20 | A979243B20FD8C7B0063069B /* UIViewController+FF.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979243A20FD8C7B0063069B /* UIViewController+FF.swift */; }; 21 | A979243D20FDDA6C0063069B /* SuspendTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979243C20FDDA6C0063069B /* SuspendTool.swift */; }; 22 | A9B9E16621008EAA005B03C9 /* 200_200(1).jpg in Resources */ = {isa = PBXBuildFile; fileRef = A9B9E16521008EAA005B03C9 /* 200_200(1).jpg */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | A907CD812105A71A00A17CB9 /* Semicircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Semicircle.swift; sourceTree = ""; }; 27 | A90879C320F8743400F4A6BB /* SuspendViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SuspendViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | A90879C620F8743400F4A6BB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 29 | A90879C820F8743400F4A6BB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 30 | A90879CB20F8743400F4A6BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 31 | A90879CD20F8743500F4A6BB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | A90879D020F8743500F4A6BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | A90879D220F8743500F4A6BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | A90879D820F8785800F4A6BB /* SuspendWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendWindow.swift; sourceTree = ""; }; 35 | A90879DA20F8790A00F4A6BB /* ContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewController.swift; sourceTree = ""; }; 36 | A90879DB20F8790A00F4A6BB /* ContentViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContentViewController.xib; sourceTree = ""; }; 37 | A90879DE20F87D9200F4A6BB /* 200_200.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 200_200.jpg; sourceTree = ""; }; 38 | A979243A20FD8C7B0063069B /* UIViewController+FF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+FF.swift"; sourceTree = ""; }; 39 | A979243C20FDDA6C0063069B /* SuspendTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendTool.swift; sourceTree = ""; }; 40 | A9B9E16521008EAA005B03C9 /* 200_200(1).jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "200_200(1).jpg"; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | A90879C020F8743400F4A6BB /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | A90879BA20F8743400F4A6BB = { 55 | isa = PBXGroup; 56 | children = ( 57 | A90879C520F8743400F4A6BB /* SuspendViewDemo */, 58 | A90879C420F8743400F4A6BB /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | A90879C420F8743400F4A6BB /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | A90879C320F8743400F4A6BB /* SuspendViewDemo.app */, 66 | ); 67 | name = Products; 68 | sourceTree = ""; 69 | }; 70 | A90879C520F8743400F4A6BB /* SuspendViewDemo */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | A979243A20FD8C7B0063069B /* UIViewController+FF.swift */, 74 | A907CD812105A71A00A17CB9 /* Semicircle.swift */, 75 | A90879C620F8743400F4A6BB /* AppDelegate.swift */, 76 | A979243C20FDDA6C0063069B /* SuspendTool.swift */, 77 | A90879D820F8785800F4A6BB /* SuspendWindow.swift */, 78 | A90879DA20F8790A00F4A6BB /* ContentViewController.swift */, 79 | A90879DB20F8790A00F4A6BB /* ContentViewController.xib */, 80 | A90879C820F8743400F4A6BB /* ViewController.swift */, 81 | A90879CA20F8743400F4A6BB /* Main.storyboard */, 82 | A90879CD20F8743500F4A6BB /* Assets.xcassets */, 83 | A90879CF20F8743500F4A6BB /* LaunchScreen.storyboard */, 84 | A90879D220F8743500F4A6BB /* Info.plist */, 85 | A9B9E16521008EAA005B03C9 /* 200_200(1).jpg */, 86 | A90879DE20F87D9200F4A6BB /* 200_200.jpg */, 87 | ); 88 | path = SuspendViewDemo; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXNativeTarget section */ 94 | A90879C220F8743400F4A6BB /* SuspendViewDemo */ = { 95 | isa = PBXNativeTarget; 96 | buildConfigurationList = A90879D520F8743500F4A6BB /* Build configuration list for PBXNativeTarget "SuspendViewDemo" */; 97 | buildPhases = ( 98 | A90879BF20F8743400F4A6BB /* Sources */, 99 | A90879C020F8743400F4A6BB /* Frameworks */, 100 | A90879C120F8743400F4A6BB /* Resources */, 101 | ); 102 | buildRules = ( 103 | ); 104 | dependencies = ( 105 | ); 106 | name = SuspendViewDemo; 107 | productName = SuspendViewDemo; 108 | productReference = A90879C320F8743400F4A6BB /* SuspendViewDemo.app */; 109 | productType = "com.apple.product-type.application"; 110 | }; 111 | /* End PBXNativeTarget section */ 112 | 113 | /* Begin PBXProject section */ 114 | A90879BB20F8743400F4A6BB /* Project object */ = { 115 | isa = PBXProject; 116 | attributes = { 117 | LastSwiftUpdateCheck = 0940; 118 | LastUpgradeCheck = 0940; 119 | ORGANIZATIONNAME = "冯琦帆"; 120 | TargetAttributes = { 121 | A90879C220F8743400F4A6BB = { 122 | CreatedOnToolsVersion = 9.4; 123 | }; 124 | }; 125 | }; 126 | buildConfigurationList = A90879BE20F8743400F4A6BB /* Build configuration list for PBXProject "SuspendViewDemo" */; 127 | compatibilityVersion = "Xcode 9.3"; 128 | developmentRegion = en; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | en, 132 | Base, 133 | ); 134 | mainGroup = A90879BA20F8743400F4A6BB; 135 | productRefGroup = A90879C420F8743400F4A6BB /* Products */; 136 | projectDirPath = ""; 137 | projectRoot = ""; 138 | targets = ( 139 | A90879C220F8743400F4A6BB /* SuspendViewDemo */, 140 | ); 141 | }; 142 | /* End PBXProject section */ 143 | 144 | /* Begin PBXResourcesBuildPhase section */ 145 | A90879C120F8743400F4A6BB /* Resources */ = { 146 | isa = PBXResourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | A90879DF20F87D9200F4A6BB /* 200_200.jpg in Resources */, 150 | A90879DD20F8790A00F4A6BB /* ContentViewController.xib in Resources */, 151 | A9B9E16621008EAA005B03C9 /* 200_200(1).jpg in Resources */, 152 | A90879D120F8743500F4A6BB /* LaunchScreen.storyboard in Resources */, 153 | A90879CE20F8743500F4A6BB /* Assets.xcassets in Resources */, 154 | A90879CC20F8743400F4A6BB /* Main.storyboard in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | A90879BF20F8743400F4A6BB /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | A90879DC20F8790A00F4A6BB /* ContentViewController.swift in Sources */, 166 | A90879D920F8785800F4A6BB /* SuspendWindow.swift in Sources */, 167 | A979243D20FDDA6C0063069B /* SuspendTool.swift in Sources */, 168 | A907CD822105A71A00A17CB9 /* Semicircle.swift in Sources */, 169 | A979243B20FD8C7B0063069B /* UIViewController+FF.swift in Sources */, 170 | A90879C920F8743400F4A6BB /* ViewController.swift in Sources */, 171 | A90879C720F8743400F4A6BB /* AppDelegate.swift in Sources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXSourcesBuildPhase section */ 176 | 177 | /* Begin PBXVariantGroup section */ 178 | A90879CA20F8743400F4A6BB /* Main.storyboard */ = { 179 | isa = PBXVariantGroup; 180 | children = ( 181 | A90879CB20F8743400F4A6BB /* Base */, 182 | ); 183 | name = Main.storyboard; 184 | sourceTree = ""; 185 | }; 186 | A90879CF20F8743500F4A6BB /* LaunchScreen.storyboard */ = { 187 | isa = PBXVariantGroup; 188 | children = ( 189 | A90879D020F8743500F4A6BB /* Base */, 190 | ); 191 | name = LaunchScreen.storyboard; 192 | sourceTree = ""; 193 | }; 194 | /* End PBXVariantGroup section */ 195 | 196 | /* Begin XCBuildConfiguration section */ 197 | A90879D320F8743500F4A6BB /* Debug */ = { 198 | isa = XCBuildConfiguration; 199 | buildSettings = { 200 | ALWAYS_SEARCH_USER_PATHS = NO; 201 | CLANG_ANALYZER_NONNULL = YES; 202 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 203 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 204 | CLANG_CXX_LIBRARY = "libc++"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_ENABLE_OBJC_WEAK = YES; 208 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_COMMA = YES; 211 | CLANG_WARN_CONSTANT_CONVERSION = YES; 212 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 213 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 214 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 224 | CLANG_WARN_STRICT_PROTOTYPES = YES; 225 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 226 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 227 | CLANG_WARN_UNREACHABLE_CODE = YES; 228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 229 | CODE_SIGN_IDENTITY = "iPhone Developer"; 230 | COPY_PHASE_STRIP = NO; 231 | DEBUG_INFORMATION_FORMAT = dwarf; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | ENABLE_TESTABILITY = YES; 234 | GCC_C_LANGUAGE_STANDARD = gnu11; 235 | GCC_DYNAMIC_NO_PIC = NO; 236 | GCC_NO_COMMON_BLOCKS = YES; 237 | GCC_OPTIMIZATION_LEVEL = 0; 238 | GCC_PREPROCESSOR_DEFINITIONS = ( 239 | "DEBUG=1", 240 | "$(inherited)", 241 | ); 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 249 | MTL_ENABLE_DEBUG_INFO = YES; 250 | ONLY_ACTIVE_ARCH = YES; 251 | SDKROOT = iphoneos; 252 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 253 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 254 | }; 255 | name = Debug; 256 | }; 257 | A90879D420F8743500F4A6BB /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 284 | CLANG_WARN_STRICT_PROTOTYPES = YES; 285 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 286 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | CODE_SIGN_IDENTITY = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu11; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | SDKROOT = iphoneos; 305 | SWIFT_COMPILATION_MODE = wholemodule; 306 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 307 | VALIDATE_PRODUCT = YES; 308 | }; 309 | name = Release; 310 | }; 311 | A90879D620F8743500F4A6BB /* Debug */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 315 | CODE_SIGN_STYLE = Automatic; 316 | DEVELOPMENT_TEAM = XKN5CG9S5B; 317 | INFOPLIST_FILE = SuspendViewDemo/Info.plist; 318 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 319 | LD_RUNPATH_SEARCH_PATHS = ( 320 | "$(inherited)", 321 | "@executable_path/Frameworks", 322 | ); 323 | PRODUCT_BUNDLE_IDENTIFIER = com.SuspendViewDemo; 324 | PRODUCT_NAME = "$(TARGET_NAME)"; 325 | SWIFT_VERSION = 4.0; 326 | TARGETED_DEVICE_FAMILY = "1,2"; 327 | }; 328 | name = Debug; 329 | }; 330 | A90879D720F8743500F4A6BB /* Release */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 334 | CODE_SIGN_STYLE = Automatic; 335 | DEVELOPMENT_TEAM = XKN5CG9S5B; 336 | INFOPLIST_FILE = SuspendViewDemo/Info.plist; 337 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 338 | LD_RUNPATH_SEARCH_PATHS = ( 339 | "$(inherited)", 340 | "@executable_path/Frameworks", 341 | ); 342 | PRODUCT_BUNDLE_IDENTIFIER = com.SuspendViewDemo; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | SWIFT_VERSION = 4.0; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | }; 347 | name = Release; 348 | }; 349 | /* End XCBuildConfiguration section */ 350 | 351 | /* Begin XCConfigurationList section */ 352 | A90879BE20F8743400F4A6BB /* Build configuration list for PBXProject "SuspendViewDemo" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | A90879D320F8743500F4A6BB /* Debug */, 356 | A90879D420F8743500F4A6BB /* Release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = Release; 360 | }; 361 | A90879D520F8743500F4A6BB /* Build configuration list for PBXNativeTarget "SuspendViewDemo" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | A90879D620F8743500F4A6BB /* Debug */, 365 | A90879D720F8743500F4A6BB /* Release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = Release; 369 | }; 370 | /* End XCConfigurationList section */ 371 | }; 372 | rootObject = A90879BB20F8743400F4A6BB /* Project object */; 373 | } 374 | -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/project.xcworkspace/xcuserdata/fanfan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo.xcodeproj/project.xcworkspace/xcuserdata/fanfan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/xcuserdata/fanfan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /SuspendViewDemo.xcodeproj/xcuserdata/fanfan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SuspendViewDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SuspendViewDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/.DS_Store -------------------------------------------------------------------------------- /SuspendViewDemo/200_200(1).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/200_200(1).jpg -------------------------------------------------------------------------------- /SuspendViewDemo/200_200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/200_200.jpg -------------------------------------------------------------------------------- /SuspendViewDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/13. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "payment_close_button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "payment_close_button@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "payment_close_button@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button.png -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button@2x.png -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_close_button.imageset/payment_close_button@3x.png -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "payment_tick_icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "payment_tick_icon@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "payment_tick_icon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon.png -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon@2x.png -------------------------------------------------------------------------------- /SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fanfan2528/SuspendViewDemo/ccbe0706f6522e022879c9c438b46ba3a4522ce7/SuspendViewDemo/Assets.xcassets/payment_tick_icon.imageset/payment_tick_icon@3x.png -------------------------------------------------------------------------------- /SuspendViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SuspendViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /SuspendViewDemo/ContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/13. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContentViewController: UIViewController { 12 | 13 | @IBOutlet fileprivate weak var coverImageView: UIImageView! 14 | @IBOutlet weak var singleImageView: UIImageView! 15 | @IBOutlet weak var multiImageView: UIImageView! 16 | @IBOutlet weak var cancelImageView: UIImageView! 17 | private let imageName: String 18 | private var suspendType: SuspendType = .single 19 | 20 | init(imageName: String) { 21 | self.imageName = imageName 22 | super.init(nibName: nil, bundle: nil) 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | self.coverImageView.image = UIImage.init(named: imageName) 32 | } 33 | 34 | @IBAction func singleSuspendButtonClicked(_ sender: Any) { 35 | self.singleImageView.image = UIImage.init(named: "payment_tick_icon") 36 | self.multiImageView.image = nil 37 | self.cancelImageView.image = nil 38 | self.suspendType = .single 39 | } 40 | 41 | @IBAction func multiSuspendButtonClicked(_ sender: Any) { 42 | self.singleImageView.image = nil 43 | self.multiImageView.image = UIImage.init(named: "payment_tick_icon") 44 | self.cancelImageView.image = nil 45 | self.suspendType = .multi 46 | } 47 | 48 | @IBAction func cancelSuspendButtonClicked(_ sender: Any) { 49 | self.singleImageView.image = nil 50 | self.multiImageView.image = nil 51 | self.cancelImageView.image = UIImage.init(named: "payment_tick_icon") 52 | self.suspendType = .none 53 | } 54 | 55 | @IBAction func closeButtonClicked(_ sender: Any) { 56 | self.suspend(coverImageName: imageName, type: suspendType) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /SuspendViewDemo/ContentViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /SuspendViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | CFBundleDevelopmentRegion 11 | $(DEVELOPMENT_LANGUAGE) 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleVersion 25 | 1 26 | LSRequiresIPhoneOS 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /SuspendViewDemo/Semicircle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Semicircle.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/23. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let semiRadious: CGFloat = 200 12 | 13 | class Semicircle: UIWindow { 14 | 15 | func floatStyle() { 16 | self.isHidden = false 17 | self.windowLevel = UIWindowLevelAlert - 1 18 | 19 | let semicircle = CAShapeLayer() 20 | let path = UIBezierPath() 21 | path.addArc(withCenter: CGPoint.init(x: semiRadious, y: semiRadious), radius: semiRadious, startAngle: CGFloat.pi * 1.5, endAngle: CGFloat.pi, clockwise: false) 22 | path.addLine(to: CGPoint.init(x: semiRadious, y: semiRadious)) 23 | path.addLine(to: CGPoint.init(x: semiRadious, y: 0)) 24 | path.close() 25 | semicircle.path = path.cgPath 26 | self.layer.addSublayer(semicircle) 27 | semicircle.fillColor = UIColor.red.cgColor 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendTool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SuspendTool.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/17. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | enum SuspendType { 13 | case none 14 | case single 15 | case multi 16 | } 17 | 18 | class SuspendTool: NSObject { 19 | 20 | static let sharedInstance = SuspendTool() 21 | private var suspendWindows: [SuspendWindow] = [] 22 | // var semicircle: Semicircle? 23 | var origin: CGPoint = CGPoint.init(x: 10, y: 300) 24 | 25 | static func showSuspendWindow(rootViewController: UIViewController, coverImageName: String) { 26 | let tool = SuspendTool.sharedInstance 27 | let window = SuspendWindow.init(rootViewController: rootViewController, coverImageName: coverImageName, frame: CGRect.init(origin: tool.origin, size: CGSize.init(width: radious, height: radious))) 28 | window.show() 29 | tool.suspendWindows.append(window) 30 | } 31 | 32 | static func replaceSuspendWindow(rootViewController: UIViewController, coverImageName: String) { 33 | let tool = SuspendTool.sharedInstance 34 | tool.suspendWindows.removeAll() 35 | let window = SuspendWindow.init(rootViewController: rootViewController, coverImageName: coverImageName, frame: CGRect.init(origin: tool.origin, size: CGSize.init(width: radious, height: radious))) 36 | window.show() 37 | tool.suspendWindows.append(window) 38 | } 39 | 40 | static func remove(suspendWindow: SuspendWindow) { 41 | UIView.animate(withDuration: 0.25, animations: { 42 | suspendWindow.alpha = 0 43 | }) { (complete) in 44 | if let index = SuspendTool.sharedInstance.suspendWindows.index(of: suspendWindow) { 45 | SuspendTool.sharedInstance.suspendWindows.remove(at: index) 46 | } 47 | } 48 | } 49 | 50 | static func setLatestOrigin(origin: CGPoint) { 51 | SuspendTool.sharedInstance.origin = origin 52 | } 53 | 54 | // static func addSuspendSemiWindow() { 55 | // let tool = SuspendTool.sharedInstance 56 | // tool.semicircle = Semicircle.init(frame: CGRect.init(x: UIScreen.main.bounds.width, y: UIScreen.main.bounds.height, width: semiRadious * 2, height: semiRadious * 2)) 57 | // tool.semicircle?.floatStyle() 58 | // } 59 | // 60 | // static func removeSuspendSemiWindow() { 61 | // let tool = SuspendTool.sharedInstance 62 | // tool.semicircle = nil 63 | // } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /SuspendViewDemo/SuspendWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SuspendWindow.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/13. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let radious: CGFloat = 100 12 | 13 | class SuspendWindow: UIWindow { 14 | 15 | fileprivate let coverImageName: String 16 | fileprivate let space: CGFloat = 10 17 | 18 | init(rootViewController: UIViewController ,coverImageName: String, frame: CGRect) { 19 | self.coverImageName = coverImageName 20 | super.init(frame: frame) 21 | self.rootViewController = rootViewController 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | func show() { 29 | self.backgroundColor = UIColor.clear 30 | self.windowLevel = UIWindowLevelAlert - 1 31 | self.screen = UIScreen.main 32 | self.isHidden = false 33 | 34 | let iconImageView = UIImageView.init(image: UIImage.init(named: coverImageName)) 35 | iconImageView.isUserInteractionEnabled = true 36 | iconImageView.frame = self.bounds 37 | iconImageView.layer.cornerRadius = radious / 2.0 38 | iconImageView.layer.borderColor = UIColor.lightGray.cgColor 39 | iconImageView.layer.borderWidth = 5 40 | iconImageView.layer.masksToBounds = true 41 | self.addSubview(iconImageView) 42 | 43 | let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(didPan(_:))) 44 | self.addGestureRecognizer(panGesture) 45 | 46 | let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(didTap(_:))) 47 | self.addGestureRecognizer(tapGesture) 48 | } 49 | 50 | @objc fileprivate func didTap(_ tapGesture: UITapGestureRecognizer) { 51 | SuspendTool.sharedInstance.origin = self.frame.origin 52 | self.rootViewController?.spread(from: self.self.frame.origin) 53 | SuspendTool.remove(suspendWindow: self) 54 | } 55 | 56 | @objc fileprivate func didPan(_ panGesture: UIPanGestureRecognizer) { 57 | let point = panGesture.translation(in: panGesture.view) 58 | var originX = self.frame.origin.x + point.x 59 | if originX < space { 60 | originX = space 61 | } else if originX > UIScreen.main.bounds.width - radious - space { 62 | originX = UIScreen.main.bounds.width - radious - space 63 | } 64 | var originY = self.frame.origin.y + point.y 65 | if originY < space { 66 | originY = space 67 | } else if originY > UIScreen.main.bounds.height - radious - space { 68 | originY = UIScreen.main.bounds.height - radious - space 69 | } 70 | self.frame = CGRect.init(x: originX, y: originY, width: self.bounds.width, height: self.bounds.height) 71 | if panGesture.state == UIGestureRecognizerState.cancelled || panGesture.state == UIGestureRecognizerState.ended || panGesture.state == UIGestureRecognizerState.failed { 72 | self.adjustFrameAfterPan() 73 | } 74 | panGesture.setTranslation(CGPoint.zero, in: self) 75 | } 76 | 77 | fileprivate func adjustFrameAfterPan() { 78 | var originX: CGFloat = space 79 | if self.center.x < UIScreen.main.bounds.width / 2 { 80 | originX = space 81 | } else if self.center.x >= UIScreen.main.bounds.width / 2 { 82 | originX = UIScreen.main.bounds.width - radious - space 83 | } 84 | UIView.animate(withDuration: 0.25, animations: { 85 | self.frame = CGRect.init(x: originX, y: self.frame.origin.y, width: self.frame.size.width, height: self.frame.size.height) 86 | }) { (complete) in 87 | SuspendTool.setLatestOrigin(origin: self.frame.origin) 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /SuspendViewDemo/UIViewController+FF.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+FF.swift 3 | // SideSlideDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/17. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIViewController { 13 | 14 | func suspend(coverImageName: String, type: SuspendType) { 15 | if type == .none { 16 | self.navigationController?.popViewController(animated: true) 17 | return 18 | } 19 | self.view.layer.masksToBounds = true 20 | UIView.animate(withDuration: 0.25, animations: { 21 | self.view.layer.cornerRadius = radious / 2.0 22 | self.view.frame = CGRect.init(origin: SuspendTool.sharedInstance.origin, size: CGSize.init(width: radious, height: radious)) 23 | self.view.layoutIfNeeded() 24 | }) { (complete) in 25 | self.navigationController?.popViewController(animated: false) 26 | if type == .single { 27 | SuspendTool.replaceSuspendWindow(rootViewController: self, coverImageName: coverImageName) 28 | } else { 29 | SuspendTool.showSuspendWindow(rootViewController: self, coverImageName: coverImageName) 30 | } 31 | } 32 | } 33 | 34 | func spread(from point: CGPoint) { 35 | if let isContain = self.navigationController?.viewControllers.contains(self), isContain { 36 | return 37 | } 38 | self.view.frame = CGRect.init(origin: point, size: CGSize.init(width: radious, height: radious)) 39 | UIViewController.currentViewController().navigationController?.pushViewController(self, animated: false) 40 | UIView.animate(withDuration: 0.25, animations: { 41 | self.view.layer.cornerRadius = 0 42 | self.view.frame = UIScreen.main.bounds 43 | self.view.layoutIfNeeded() 44 | }) 45 | } 46 | 47 | static func currentViewController() -> UIViewController { 48 | var rootViewController: UIViewController? = nil 49 | for window in UIApplication.shared.windows { 50 | if (window.rootViewController != nil) { 51 | rootViewController = window.rootViewController 52 | break 53 | } 54 | } 55 | var viewController = rootViewController 56 | while (true) { 57 | if viewController?.presentedViewController != nil { 58 | viewController = viewController!.presentedViewController 59 | } else if viewController!.isKind(of: UINavigationController.self) { 60 | viewController = (viewController as! UINavigationController).visibleViewController 61 | } else if viewController!.isKind(of: UITabBarController.self) { 62 | viewController = (viewController as! UITabBarController).selectedViewController 63 | } else { 64 | break 65 | } 66 | } 67 | return viewController! 68 | } 69 | 70 | } 71 | 72 | //extension UIViewController: UIGestureRecognizerDelegate { 73 | // func addNavigationPopListener() { 74 | // self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true 75 | // self.navigationController?.interactivePopGestureRecognizer?.delegate = self 76 | // let gesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleNavigationTransition(_:))) 77 | // gesture.edges = .left 78 | // self.view.addGestureRecognizer(gesture) 79 | // SuspendTool.addSuspendSemiWindow() 80 | // } 81 | // 82 | // @objc public func handleNavigationTransition(_ gesture: UIGestureRecognizer) -> Bool { 83 | // let location = gesture.location(in: self.view) 84 | // print(location) 85 | // let tool = SuspendTool.sharedInstance 86 | // if location.x <= 0 { 87 | // let percent = 1 - (-location.x) / UIScreen.main.bounds.width 88 | // tool.semicircle?.frame = CGRect.init(x: UIScreen.main.bounds.width - semiRadious * percent, y: UIScreen.main.bounds.height - semiRadious * percent, width: semiRadious * 2, height: semiRadious * 2) 89 | // } else { 90 | // tool.semicircle?.frame = CGRect.init(x: UIScreen.main.bounds.width, y: UIScreen.main.bounds.height, width: semiRadious * 2, height: semiRadious * 2) 91 | // } 92 | // if gesture.state == UIGestureRecognizerState.ended || gesture.state == UIGestureRecognizerState.cancelled || gesture.state == UIGestureRecognizerState.failed { 93 | // UIView.animate(withDuration: 0.25, animations: { 94 | // tool.semicircle?.frame = CGRect.init(x: UIScreen.main.bounds.width, y: UIScreen.main.bounds.height, width: semiRadious * 2, height: semiRadious * 2) 95 | // }) 96 | // } 97 | // return true 98 | // } 99 | // 100 | // public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 101 | // return true 102 | // } 103 | // 104 | //} 105 | -------------------------------------------------------------------------------- /SuspendViewDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SuspendViewDemo 4 | // 5 | // Created by 冯琦帆 on 2018/7/13. 6 | // Copyright © 2018年 冯琦帆. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | 18 | 19 | @IBAction func article1ButtonClicked(_ sender: Any) { 20 | let controller = ContentViewController.init(imageName: "200_200.jpg") 21 | self.navigationController?.pushViewController(controller, animated: true) 22 | } 23 | 24 | @IBAction func article2ButtonClicked(_ sender: Any) { 25 | let controller = ContentViewController.init(imageName: "200_200(1).jpg") 26 | self.navigationController?.pushViewController(controller, animated: true) 27 | } 28 | 29 | } 30 | 31 | --------------------------------------------------------------------------------