├── .gitignore ├── SemiModalOverCurrentTransitioning.xcodeproj └── project.pbxproj ├── SemiModalOverCurrentTransitioning ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DismissAnimator.swift ├── Info.plist ├── ModalPresentationController.swift ├── OverCurrentTransitionable.swift ├── OverCurrentTransitioningInteractor.swift ├── SemiModalViewController.storyboard ├── SemiModalViewController.swift └── ViewController.swift └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | 4 | # Xcode 5 | build/* 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | *.xcworkspace 15 | !default.xcworkspace 16 | xcuserdata 17 | profile 18 | *.moved-aside 19 | UserInterfaceState.xcuserstate 20 | *.generated.swift 21 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9853192D220B095A00378DA5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9853192C220B095A00378DA5 /* AppDelegate.swift */; }; 11 | 9853192F220B095A00378DA5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9853192E220B095A00378DA5 /* ViewController.swift */; }; 12 | 98531932220B095A00378DA5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98531930220B095A00378DA5 /* Main.storyboard */; }; 13 | 98531934220B095B00378DA5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98531933220B095B00378DA5 /* Assets.xcassets */; }; 14 | 98531937220B095B00378DA5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98531935220B095B00378DA5 /* LaunchScreen.storyboard */; }; 15 | 9853193F220B0D0D00378DA5 /* SemiModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9853193E220B0D0C00378DA5 /* SemiModalViewController.swift */; }; 16 | 98531941220B0D2100378DA5 /* SemiModalViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98531940220B0D2100378DA5 /* SemiModalViewController.storyboard */; }; 17 | 98531943220C5EC900378DA5 /* ModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98531942220C5EC900378DA5 /* ModalPresentationController.swift */; }; 18 | 98531945220EBF2500378DA5 /* DismissAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98531944220EBF2400378DA5 /* DismissAnimator.swift */; }; 19 | 98531947220EC92A00378DA5 /* OverCurrentTransitionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98531946220EC92A00378DA5 /* OverCurrentTransitionable.swift */; }; 20 | 98531949220EC9DD00378DA5 /* OverCurrentTransitioningInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98531948220EC9DD00378DA5 /* OverCurrentTransitioningInteractor.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 98531929220B095A00378DA5 /* SemiModalOverCurrentTransitioning.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SemiModalOverCurrentTransitioning.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 9853192C220B095A00378DA5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | 9853192E220B095A00378DA5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | 98531931220B095A00378DA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 98531933220B095B00378DA5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 98531936220B095B00378DA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 98531938220B095B00378DA5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 9853193E220B0D0C00378DA5 /* SemiModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemiModalViewController.swift; sourceTree = ""; }; 32 | 98531940220B0D2100378DA5 /* SemiModalViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SemiModalViewController.storyboard; sourceTree = ""; }; 33 | 98531942220C5EC900378DA5 /* ModalPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationController.swift; sourceTree = ""; }; 34 | 98531944220EBF2400378DA5 /* DismissAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissAnimator.swift; sourceTree = ""; }; 35 | 98531946220EC92A00378DA5 /* OverCurrentTransitionable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverCurrentTransitionable.swift; sourceTree = ""; }; 36 | 98531948220EC9DD00378DA5 /* OverCurrentTransitioningInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverCurrentTransitioningInteractor.swift; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 98531926220B095A00378DA5 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 98531920220B095A00378DA5 = { 51 | isa = PBXGroup; 52 | children = ( 53 | 9853192B220B095A00378DA5 /* SemiModalOverCurrentTransitioning */, 54 | 9853192A220B095A00378DA5 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 9853192A220B095A00378DA5 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 98531929220B095A00378DA5 /* SemiModalOverCurrentTransitioning.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 9853192B220B095A00378DA5 /* SemiModalOverCurrentTransitioning */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 9853192C220B095A00378DA5 /* AppDelegate.swift */, 70 | 98531933220B095B00378DA5 /* Assets.xcassets */, 71 | 98531944220EBF2400378DA5 /* DismissAnimator.swift */, 72 | 98531938220B095B00378DA5 /* Info.plist */, 73 | 98531935220B095B00378DA5 /* LaunchScreen.storyboard */, 74 | 98531930220B095A00378DA5 /* Main.storyboard */, 75 | 98531942220C5EC900378DA5 /* ModalPresentationController.swift */, 76 | 98531946220EC92A00378DA5 /* OverCurrentTransitionable.swift */, 77 | 98531948220EC9DD00378DA5 /* OverCurrentTransitioningInteractor.swift */, 78 | 98531940220B0D2100378DA5 /* SemiModalViewController.storyboard */, 79 | 9853193E220B0D0C00378DA5 /* SemiModalViewController.swift */, 80 | 9853192E220B095A00378DA5 /* ViewController.swift */, 81 | ); 82 | path = SemiModalOverCurrentTransitioning; 83 | sourceTree = ""; 84 | }; 85 | /* End PBXGroup section */ 86 | 87 | /* Begin PBXNativeTarget section */ 88 | 98531928220B095A00378DA5 /* SemiModalOverCurrentTransitioning */ = { 89 | isa = PBXNativeTarget; 90 | buildConfigurationList = 9853193B220B095B00378DA5 /* Build configuration list for PBXNativeTarget "SemiModalOverCurrentTransitioning" */; 91 | buildPhases = ( 92 | 98531925220B095A00378DA5 /* Sources */, 93 | 98531926220B095A00378DA5 /* Frameworks */, 94 | 98531927220B095A00378DA5 /* Resources */, 95 | ); 96 | buildRules = ( 97 | ); 98 | dependencies = ( 99 | ); 100 | name = SemiModalOverCurrentTransitioning; 101 | productName = SemiModalOverCurrentTransitioning; 102 | productReference = 98531929220B095A00378DA5 /* SemiModalOverCurrentTransitioning.app */; 103 | productType = "com.apple.product-type.application"; 104 | }; 105 | /* End PBXNativeTarget section */ 106 | 107 | /* Begin PBXProject section */ 108 | 98531921220B095A00378DA5 /* Project object */ = { 109 | isa = PBXProject; 110 | attributes = { 111 | LastSwiftUpdateCheck = 1010; 112 | LastUpgradeCheck = 1010; 113 | ORGANIZATIONNAME = Yoichi; 114 | TargetAttributes = { 115 | 98531928220B095A00378DA5 = { 116 | CreatedOnToolsVersion = 10.1; 117 | }; 118 | }; 119 | }; 120 | buildConfigurationList = 98531924220B095A00378DA5 /* Build configuration list for PBXProject "SemiModalOverCurrentTransitioning" */; 121 | compatibilityVersion = "Xcode 9.3"; 122 | developmentRegion = en; 123 | hasScannedForEncodings = 0; 124 | knownRegions = ( 125 | en, 126 | Base, 127 | ); 128 | mainGroup = 98531920220B095A00378DA5; 129 | productRefGroup = 9853192A220B095A00378DA5 /* Products */; 130 | projectDirPath = ""; 131 | projectRoot = ""; 132 | targets = ( 133 | 98531928220B095A00378DA5 /* SemiModalOverCurrentTransitioning */, 134 | ); 135 | }; 136 | /* End PBXProject section */ 137 | 138 | /* Begin PBXResourcesBuildPhase section */ 139 | 98531927220B095A00378DA5 /* Resources */ = { 140 | isa = PBXResourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | 98531937220B095B00378DA5 /* LaunchScreen.storyboard in Resources */, 144 | 98531934220B095B00378DA5 /* Assets.xcassets in Resources */, 145 | 98531932220B095A00378DA5 /* Main.storyboard in Resources */, 146 | 98531941220B0D2100378DA5 /* SemiModalViewController.storyboard in Resources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXResourcesBuildPhase section */ 151 | 152 | /* Begin PBXSourcesBuildPhase section */ 153 | 98531925220B095A00378DA5 /* Sources */ = { 154 | isa = PBXSourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 9853193F220B0D0D00378DA5 /* SemiModalViewController.swift in Sources */, 158 | 9853192F220B095A00378DA5 /* ViewController.swift in Sources */, 159 | 9853192D220B095A00378DA5 /* AppDelegate.swift in Sources */, 160 | 98531949220EC9DD00378DA5 /* OverCurrentTransitioningInteractor.swift in Sources */, 161 | 98531945220EBF2500378DA5 /* DismissAnimator.swift in Sources */, 162 | 98531947220EC92A00378DA5 /* OverCurrentTransitionable.swift in Sources */, 163 | 98531943220C5EC900378DA5 /* ModalPresentationController.swift in Sources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXSourcesBuildPhase section */ 168 | 169 | /* Begin PBXVariantGroup section */ 170 | 98531930220B095A00378DA5 /* Main.storyboard */ = { 171 | isa = PBXVariantGroup; 172 | children = ( 173 | 98531931220B095A00378DA5 /* Base */, 174 | ); 175 | name = Main.storyboard; 176 | sourceTree = ""; 177 | }; 178 | 98531935220B095B00378DA5 /* LaunchScreen.storyboard */ = { 179 | isa = PBXVariantGroup; 180 | children = ( 181 | 98531936220B095B00378DA5 /* Base */, 182 | ); 183 | name = LaunchScreen.storyboard; 184 | sourceTree = ""; 185 | }; 186 | /* End PBXVariantGroup section */ 187 | 188 | /* Begin XCBuildConfiguration section */ 189 | 98531939220B095B00378DA5 /* Debug */ = { 190 | isa = XCBuildConfiguration; 191 | buildSettings = { 192 | ALWAYS_SEARCH_USER_PATHS = NO; 193 | CLANG_ANALYZER_NONNULL = YES; 194 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 195 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 196 | CLANG_CXX_LIBRARY = "libc++"; 197 | CLANG_ENABLE_MODULES = YES; 198 | CLANG_ENABLE_OBJC_ARC = YES; 199 | CLANG_ENABLE_OBJC_WEAK = YES; 200 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_COMMA = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 207 | CLANG_WARN_EMPTY_BODY = YES; 208 | CLANG_WARN_ENUM_CONVERSION = YES; 209 | CLANG_WARN_INFINITE_RECURSION = YES; 210 | CLANG_WARN_INT_CONVERSION = YES; 211 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 212 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 213 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 214 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 215 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 216 | CLANG_WARN_STRICT_PROTOTYPES = YES; 217 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 218 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | CODE_SIGN_IDENTITY = "iPhone Developer"; 222 | COPY_PHASE_STRIP = NO; 223 | DEBUG_INFORMATION_FORMAT = dwarf; 224 | ENABLE_STRICT_OBJC_MSGSEND = YES; 225 | ENABLE_TESTABILITY = YES; 226 | GCC_C_LANGUAGE_STANDARD = gnu11; 227 | GCC_DYNAMIC_NO_PIC = NO; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_OPTIMIZATION_LEVEL = 0; 230 | GCC_PREPROCESSOR_DEFINITIONS = ( 231 | "DEBUG=1", 232 | "$(inherited)", 233 | ); 234 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 235 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 236 | GCC_WARN_UNDECLARED_SELECTOR = YES; 237 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 238 | GCC_WARN_UNUSED_FUNCTION = YES; 239 | GCC_WARN_UNUSED_VARIABLE = YES; 240 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 241 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 242 | MTL_FAST_MATH = YES; 243 | ONLY_ACTIVE_ARCH = YES; 244 | SDKROOT = iphoneos; 245 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 246 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 247 | }; 248 | name = Debug; 249 | }; 250 | 9853193A220B095B00378DA5 /* Release */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | CLANG_ANALYZER_NONNULL = YES; 255 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 256 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 257 | CLANG_CXX_LIBRARY = "libc++"; 258 | CLANG_ENABLE_MODULES = YES; 259 | CLANG_ENABLE_OBJC_ARC = YES; 260 | CLANG_ENABLE_OBJC_WEAK = YES; 261 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 262 | CLANG_WARN_BOOL_CONVERSION = YES; 263 | CLANG_WARN_COMMA = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 268 | CLANG_WARN_EMPTY_BODY = YES; 269 | CLANG_WARN_ENUM_CONVERSION = YES; 270 | CLANG_WARN_INFINITE_RECURSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 274 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 276 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 277 | CLANG_WARN_STRICT_PROTOTYPES = YES; 278 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 279 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | CODE_SIGN_IDENTITY = "iPhone Developer"; 283 | COPY_PHASE_STRIP = NO; 284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 285 | ENABLE_NS_ASSERTIONS = NO; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | GCC_C_LANGUAGE_STANDARD = gnu11; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 296 | MTL_ENABLE_DEBUG_INFO = NO; 297 | MTL_FAST_MATH = YES; 298 | SDKROOT = iphoneos; 299 | SWIFT_COMPILATION_MODE = wholemodule; 300 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Release; 304 | }; 305 | 9853193C220B095B00378DA5 /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 309 | CODE_SIGN_STYLE = Automatic; 310 | DEVELOPMENT_TEAM = MYNLSAF27S; 311 | INFOPLIST_FILE = SemiModalOverCurrentTransitioning/Info.plist; 312 | LD_RUNPATH_SEARCH_PATHS = ( 313 | "$(inherited)", 314 | "@executable_path/Frameworks", 315 | ); 316 | PRODUCT_BUNDLE_IDENTIFIER = com.SemiModalVerticalOverCurrentTransitioning.SemiModalOverCurrentTransitioning; 317 | PRODUCT_NAME = "$(TARGET_NAME)"; 318 | SWIFT_VERSION = 4.2; 319 | TARGETED_DEVICE_FAMILY = "1,2"; 320 | }; 321 | name = Debug; 322 | }; 323 | 9853193D220B095B00378DA5 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 327 | CODE_SIGN_STYLE = Automatic; 328 | DEVELOPMENT_TEAM = MYNLSAF27S; 329 | INFOPLIST_FILE = SemiModalOverCurrentTransitioning/Info.plist; 330 | LD_RUNPATH_SEARCH_PATHS = ( 331 | "$(inherited)", 332 | "@executable_path/Frameworks", 333 | ); 334 | PRODUCT_BUNDLE_IDENTIFIER = com.SemiModalVerticalOverCurrentTransitioning.SemiModalOverCurrentTransitioning; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | SWIFT_VERSION = 4.2; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Release; 340 | }; 341 | /* End XCBuildConfiguration section */ 342 | 343 | /* Begin XCConfigurationList section */ 344 | 98531924220B095A00378DA5 /* Build configuration list for PBXProject "SemiModalOverCurrentTransitioning" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | 98531939220B095B00378DA5 /* Debug */, 348 | 9853193A220B095B00378DA5 /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | 9853193B220B095B00378DA5 /* Build configuration list for PBXNativeTarget "SemiModalOverCurrentTransitioning" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | 9853193C220B095B00378DA5 /* Debug */, 357 | 9853193D220B095B00378DA5 /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | /* End XCConfigurationList section */ 363 | }; 364 | rootObject = 98531921220B095A00378DA5 /* Project object */; 365 | } 366 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 10 | // Override point for customization after application launch. 11 | return true 12 | } 13 | 14 | func applicationWillResignActive(_ application: UIApplication) { 15 | // 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. 16 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 17 | } 18 | 19 | func applicationDidEnterBackground(_ application: UIApplication) { 20 | // 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. 21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 22 | } 23 | 24 | func applicationWillEnterForeground(_ application: UIApplication) { 25 | // 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. 26 | } 27 | 28 | func applicationDidBecomeActive(_ application: UIApplication) { 29 | // 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. 30 | } 31 | 32 | func applicationWillTerminate(_ application: UIApplication) { 33 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 34 | } 35 | 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/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 | } -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/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 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/DismissAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class DismissAnimator: NSObject, UIViewControllerAnimatedTransitioning { 4 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 5 | return 0.4 6 | } 7 | 8 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 9 | guard let fromVC = transitionContext.viewController(forKey: .from) else { return } 10 | 11 | let containerView = transitionContext.containerView 12 | 13 | containerView.addSubview(fromVC.view) 14 | 15 | let screenBounds = UIScreen.main.bounds 16 | 17 | let bottomLeftCorner = CGPoint(x: 0, y: screenBounds.height) 18 | let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size) 19 | let option: UIView.AnimationOptions = transitionContext.isInteractive ? .curveLinear : .curveEaseIn 20 | 21 | UIView.animate(withDuration: transitionDuration(using: transitionContext), 22 | delay: 0, 23 | options: [option], 24 | animations: { 25 | fromVC.view.frame = finalFrame 26 | }, 27 | completion: { _ in 28 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 29 | } 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/ModalPresentationController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ModalPresentationController: UIPresentationController { 4 | private let overlayView = UIView() 5 | 6 | override func presentationTransitionWillBegin() { 7 | super.presentationTransitionWillBegin() 8 | 9 | overlayView.frame = containerView!.bounds 10 | overlayView.backgroundColor = .black 11 | overlayView.alpha = 0.0 12 | containerView!.insertSubview(overlayView, at: 0) 13 | presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [unowned self] _ in 14 | self.overlayView.alpha = 0.5 15 | }) 16 | } 17 | 18 | override func dismissalTransitionWillBegin() { 19 | super.dismissalTransitionWillBegin() 20 | 21 | presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [unowned self] _ in 22 | self.overlayView.alpha = 0.0 23 | }) 24 | } 25 | 26 | override func dismissalTransitionDidEnd(_ completed: Bool) { 27 | super.dismissalTransitionDidEnd(completed) 28 | 29 | if completed { 30 | overlayView.removeFromSuperview() 31 | } 32 | } 33 | 34 | override var frameOfPresentedViewInContainerView: CGRect { 35 | return containerView!.bounds 36 | } 37 | 38 | override func containerViewWillLayoutSubviews() { 39 | super.containerViewWillLayoutSubviews() 40 | 41 | overlayView.frame = containerView!.bounds 42 | presentedView!.frame = frameOfPresentedViewInContainerView 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/OverCurrentTransitionable.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | protocol OverCurrentTransitionable where Self: UIViewController { 4 | var percentThreshold: CGFloat { get } 5 | var interactor: OverCurrentTransitioningInteractor { get } 6 | } 7 | 8 | extension OverCurrentTransitionable { 9 | var shouldFinishVerocityY: CGFloat { 10 | return 1200 11 | } 12 | } 13 | 14 | extension OverCurrentTransitionable { 15 | func handleTransitionGesture(_ sender: UIPanGestureRecognizer) { 16 | 17 | switch interactor.state { 18 | case .shouldStart: 19 | interactor.state = .hasStarted 20 | dismiss(animated: true, completion: nil) 21 | case .hasStarted, .shouldFinish: 22 | break 23 | case .none: 24 | return 25 | } 26 | 27 | let translation = sender.translation(in: view) 28 | let verticalMovement = (translation.y - interactor.startInteractionTranslationY) / view.bounds.height 29 | let downwardMovement = fmaxf(Float(verticalMovement), 0.0) 30 | let downwardMovementPercent = fminf(downwardMovement, 1.0) 31 | let progress = CGFloat(downwardMovementPercent) 32 | 33 | switch sender.state { 34 | case .changed: 35 | if progress > percentThreshold || sender.velocity(in: view).y > shouldFinishVerocityY { 36 | interactor.state = .shouldFinish 37 | } else { 38 | interactor.state = .hasStarted 39 | } 40 | interactor.update(progress) 41 | case .cancelled: 42 | interactor.cancel() 43 | interactor.reset() 44 | case .ended: 45 | switch interactor.state { 46 | case .shouldFinish: 47 | interactor.finish() 48 | case .hasStarted, .none, .shouldStart: 49 | interactor.cancel() 50 | } 51 | interactor.reset() 52 | default: 53 | break 54 | } 55 | print("Interactor State: \(interactor.state)") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/OverCurrentTransitioningInteractor.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class OverCurrentTransitioningInteractor: UIPercentDrivenInteractiveTransition { 4 | enum State { 5 | case none 6 | case shouldStart 7 | case hasStarted 8 | case shouldFinish 9 | } 10 | 11 | var state: State = .none 12 | 13 | var startInteractionTranslationY: CGFloat = 0 14 | 15 | var startHandler: (() -> Void)? 16 | 17 | var resetHandler: (() -> Void)? 18 | 19 | override func cancel() { 20 | completionSpeed = percentComplete 21 | super.cancel() 22 | } 23 | 24 | override func finish() { 25 | completionSpeed = 1.0 - percentComplete 26 | super.finish() 27 | } 28 | 29 | func setStartInteractionTranslationY(_ translationY: CGFloat) { 30 | switch state { 31 | case .shouldStart: 32 | /// Interaction開始可能な際にInteraction開始までの間更新し続けることで、開始時のYを保持する 33 | startInteractionTranslationY = translationY 34 | case .hasStarted, .shouldFinish, .none: 35 | break 36 | } 37 | } 38 | 39 | func updateStateShouldStartIfNeeded() { 40 | switch state { 41 | case .none: 42 | state = .shouldStart 43 | startHandler?() 44 | case .shouldStart, .hasStarted, .shouldFinish: 45 | break 46 | } 47 | } 48 | 49 | func reset() { 50 | state = .none 51 | startInteractionTranslationY = 0 52 | resetHandler?() 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/SemiModalViewController.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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/SemiModalViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class SemiModalViewController: UIViewController, OverCurrentTransitionable { 4 | var percentThreshold: CGFloat = 0.3 5 | var interactor = OverCurrentTransitioningInteractor() 6 | 7 | private var tableViewContentOffsetY: CGFloat = 0.0 8 | 9 | @IBOutlet private weak var headerView: UIView! 10 | @IBOutlet private weak var tableView: UITableView! 11 | @IBOutlet private weak var backgroundView: UIView! 12 | 13 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 14 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 15 | transitioningDelegate = self 16 | modalPresentationStyle = .custom 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | super.init(coder: aDecoder) 21 | transitioningDelegate = self 22 | modalPresentationStyle = .custom 23 | } 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | interactor.startHandler = { [weak self] in 29 | self?.tableView.bounces = false 30 | } 31 | interactor.resetHandler = { [weak self] in 32 | self?.tableView.bounces = true 33 | } 34 | 35 | setupViews() 36 | } 37 | 38 | private func setupViews() { 39 | headerView.layer.cornerRadius = 8.0 40 | headerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 41 | let headerGesture = UIPanGestureRecognizer(target: self, action: #selector(headerDidScroll(_:))) 42 | headerView.addGestureRecognizer(headerGesture) 43 | 44 | let gesture = UITapGestureRecognizer(target: self, action: #selector(backgroundDidTap)) 45 | backgroundView.addGestureRecognizer(gesture) 46 | 47 | let tableViewGesture = UIPanGestureRecognizer(target: self, action: #selector(tableViewDidScroll(_:))) 48 | tableViewGesture.delegate = self 49 | tableView.addGestureRecognizer(tableViewGesture) 50 | tableView.delegate = self 51 | tableView.dataSource = self 52 | } 53 | 54 | static func make() -> SemiModalViewController { 55 | let sb = UIStoryboard(name: "SemiModalViewController", bundle: nil) 56 | let vc = sb.instantiateInitialViewController() as! SemiModalViewController 57 | return vc 58 | } 59 | 60 | @objc private func backgroundDidTap() { 61 | dismiss(animated: true, completion: nil) 62 | } 63 | 64 | @objc private func headerDidScroll(_ sender: UIPanGestureRecognizer) { 65 | interactor.updateStateShouldStartIfNeeded() 66 | handleTransitionGesture(sender) 67 | } 68 | 69 | @objc private func tableViewDidScroll(_ sender: UIPanGestureRecognizer) { 70 | if tableViewContentOffsetY <= 0 { 71 | interactor.updateStateShouldStartIfNeeded() 72 | } 73 | interactor.setStartInteractionTranslationY(sender.translation(in: view).y) 74 | handleTransitionGesture(sender) 75 | } 76 | } 77 | 78 | extension SemiModalViewController: UITableViewDelegate, UITableViewDataSource { 79 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 80 | return 30 81 | } 82 | 83 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 84 | let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") 85 | cell.textLabel?.text = String(indexPath.row) 86 | return cell 87 | } 88 | 89 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 90 | tableViewContentOffsetY = scrollView.contentOffset.y 91 | } 92 | } 93 | 94 | extension SemiModalViewController: UIGestureRecognizerDelegate { 95 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 96 | return true 97 | } 98 | } 99 | 100 | extension SemiModalViewController: UIViewControllerTransitioningDelegate { 101 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 102 | return ModalPresentationController(presentedViewController: presented, presenting: presenting) 103 | } 104 | 105 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 106 | return DismissAnimator() 107 | } 108 | 109 | func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 110 | switch interactor.state { 111 | case .hasStarted, .shouldFinish: 112 | return interactor 113 | case .none, .shouldStart: 114 | return nil 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /SemiModalOverCurrentTransitioning/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController { 4 | 5 | @IBAction func buttonDidTap(_ sender: Any) { 6 | let vc = SemiModalViewController.make() 7 | present(vc, animated: true, completion: nil) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Semi Modal Vertical Over Current Transitioning 2 | 3 | This SemiModalView can also be closed by scrolling UITableView. 4 | 5 | * Demo 6 | 7 | ![demo](https://github.com/iincho/SemiModalVerticalOverCurrentTransitioning/blob/media/media/demo.gif) 8 | --------------------------------------------------------------------------------