├── .gitattributes ├── CustomTransitions.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── ondrejkorol.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── ondrejkorol.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── CustomTransitions ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── appstore1024.png │ │ ├── ipad152.png │ │ ├── ipad76.png │ │ ├── ipadNotification20.png │ │ ├── ipadNotification40.png │ │ ├── ipadPro167.png │ │ ├── ipadSettings29.png │ │ ├── ipadSettings58.png │ │ ├── ipadSpotlight40-1.png │ │ ├── ipadSpotlight40.png │ │ ├── ipadSpotlight80-1.png │ │ ├── ipadSpotlight80.png │ │ ├── iphone120-1.png │ │ ├── iphone120.png │ │ ├── iphone180.png │ │ ├── notification60.png │ │ ├── settings58.png │ │ └── settings87.png │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── DetailViewController.swift ├── HomeViewController.swift ├── Info.plist ├── SceneDelegate.swift ├── Transitions │ ├── ActionSheet │ │ ├── ActionSheetPresentationController.swift │ │ └── ActionSheetTransitionManager.swift │ ├── Alert │ │ ├── AlertPresentationController.swift │ │ ├── AlertTransitionAnimator.swift │ │ └── AlertTransitionManager.swift │ ├── Fancy │ │ ├── FancyPresentationController.swift │ │ ├── FancyTransitionAnimator.swift │ │ └── FancyTransitionManager.swift │ └── Slide │ │ ├── SlideTransitionAnimator.swift │ │ └── SlideTransitionManager.swift ├── UIButton+Extension.swift └── UIColor+Extension.swift └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 773E7C4A26AD6B1800AC1A8A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C4926AD6B1800AC1A8A /* AppDelegate.swift */; }; 11 | 773E7C4C26AD6B1800AC1A8A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C4B26AD6B1800AC1A8A /* SceneDelegate.swift */; }; 12 | 773E7C4E26AD6B1800AC1A8A /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C4D26AD6B1800AC1A8A /* HomeViewController.swift */; }; 13 | 773E7C5326AD6B1A00AC1A8A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 773E7C5226AD6B1A00AC1A8A /* Assets.xcassets */; }; 14 | 773E7C5626AD6B1A00AC1A8A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 773E7C5426AD6B1A00AC1A8A /* LaunchScreen.storyboard */; }; 15 | 773E7C6326AD6B7100AC1A8A /* AlertTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C5F26AD6B7100AC1A8A /* AlertTransitionAnimator.swift */; }; 16 | 773E7C6426AD6B7100AC1A8A /* AlertTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6026AD6B7100AC1A8A /* AlertTransitionManager.swift */; }; 17 | 773E7C6526AD6B7100AC1A8A /* AlertPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6126AD6B7100AC1A8A /* AlertPresentationController.swift */; }; 18 | 773E7C6726AD6B9B00AC1A8A /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6626AD6B9B00AC1A8A /* DetailViewController.swift */; }; 19 | 773E7C6B26AD725800AC1A8A /* ActionSheetTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6A26AD725800AC1A8A /* ActionSheetTransitionManager.swift */; }; 20 | 773E7C6D26AD72B900AC1A8A /* ActionSheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6C26AD72B900AC1A8A /* ActionSheetPresentationController.swift */; }; 21 | 773E7C7026AD7D2400AC1A8A /* SlideTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C6F26AD7D2400AC1A8A /* SlideTransitionManager.swift */; }; 22 | 773E7C7326AD7D5F00AC1A8A /* SlideTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7226AD7D5F00AC1A8A /* SlideTransitionAnimator.swift */; }; 23 | 773E7C7626AD82E900AC1A8A /* FancyTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7526AD82E900AC1A8A /* FancyTransitionManager.swift */; }; 24 | 773E7C7826AD82F400AC1A8A /* FancyTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7726AD82F400AC1A8A /* FancyTransitionAnimator.swift */; }; 25 | 773E7C7A26AD94AD00AC1A8A /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7926AD94AD00AC1A8A /* UIColor+Extension.swift */; }; 26 | 773E7C7C26ADAD2300AC1A8A /* FancyPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7B26ADAD2300AC1A8A /* FancyPresentationController.swift */; }; 27 | 773E7C7E26ADB1A700AC1A8A /* UIButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773E7C7D26ADB1A700AC1A8A /* UIButton+Extension.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 773E7C4626AD6B1800AC1A8A /* CustomTransitions.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomTransitions.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 773E7C4926AD6B1800AC1A8A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33 | 773E7C4B26AD6B1800AC1A8A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 34 | 773E7C4D26AD6B1800AC1A8A /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 35 | 773E7C5226AD6B1A00AC1A8A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 773E7C5526AD6B1A00AC1A8A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 37 | 773E7C5726AD6B1A00AC1A8A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 773E7C5F26AD6B7100AC1A8A /* AlertTransitionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertTransitionAnimator.swift; sourceTree = ""; }; 39 | 773E7C6026AD6B7100AC1A8A /* AlertTransitionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertTransitionManager.swift; sourceTree = ""; }; 40 | 773E7C6126AD6B7100AC1A8A /* AlertPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertPresentationController.swift; sourceTree = ""; }; 41 | 773E7C6626AD6B9B00AC1A8A /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 42 | 773E7C6A26AD725800AC1A8A /* ActionSheetTransitionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSheetTransitionManager.swift; sourceTree = ""; }; 43 | 773E7C6C26AD72B900AC1A8A /* ActionSheetPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSheetPresentationController.swift; sourceTree = ""; }; 44 | 773E7C6F26AD7D2400AC1A8A /* SlideTransitionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideTransitionManager.swift; sourceTree = ""; }; 45 | 773E7C7226AD7D5F00AC1A8A /* SlideTransitionAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideTransitionAnimator.swift; sourceTree = ""; }; 46 | 773E7C7526AD82E900AC1A8A /* FancyTransitionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyTransitionManager.swift; sourceTree = ""; }; 47 | 773E7C7726AD82F400AC1A8A /* FancyTransitionAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyTransitionAnimator.swift; sourceTree = ""; }; 48 | 773E7C7926AD94AD00AC1A8A /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 49 | 773E7C7B26ADAD2300AC1A8A /* FancyPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyPresentationController.swift; sourceTree = ""; }; 50 | 773E7C7D26ADB1A700AC1A8A /* UIButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extension.swift"; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 773E7C4326AD6B1800AC1A8A /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 773E7C3D26AD6B1800AC1A8A = { 65 | isa = PBXGroup; 66 | children = ( 67 | 773E7C4826AD6B1800AC1A8A /* CustomTransitions */, 68 | 773E7C4726AD6B1800AC1A8A /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 773E7C4726AD6B1800AC1A8A /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 773E7C4626AD6B1800AC1A8A /* CustomTransitions.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 773E7C4826AD6B1800AC1A8A /* CustomTransitions */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 773E7C4926AD6B1800AC1A8A /* AppDelegate.swift */, 84 | 773E7C4B26AD6B1800AC1A8A /* SceneDelegate.swift */, 85 | 773E7C4D26AD6B1800AC1A8A /* HomeViewController.swift */, 86 | 773E7C6626AD6B9B00AC1A8A /* DetailViewController.swift */, 87 | 773E7C6E26AD7CEE00AC1A8A /* Transitions */, 88 | 773E7C7926AD94AD00AC1A8A /* UIColor+Extension.swift */, 89 | 773E7C7D26ADB1A700AC1A8A /* UIButton+Extension.swift */, 90 | 773E7C5226AD6B1A00AC1A8A /* Assets.xcassets */, 91 | 773E7C5426AD6B1A00AC1A8A /* LaunchScreen.storyboard */, 92 | 773E7C5726AD6B1A00AC1A8A /* Info.plist */, 93 | ); 94 | path = CustomTransitions; 95 | sourceTree = ""; 96 | }; 97 | 773E7C5D26AD6B7100AC1A8A /* Alert */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 773E7C6026AD6B7100AC1A8A /* AlertTransitionManager.swift */, 101 | 773E7C5F26AD6B7100AC1A8A /* AlertTransitionAnimator.swift */, 102 | 773E7C6126AD6B7100AC1A8A /* AlertPresentationController.swift */, 103 | ); 104 | path = Alert; 105 | sourceTree = ""; 106 | }; 107 | 773E7C6926AD6BAE00AC1A8A /* ActionSheet */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 773E7C6A26AD725800AC1A8A /* ActionSheetTransitionManager.swift */, 111 | 773E7C6C26AD72B900AC1A8A /* ActionSheetPresentationController.swift */, 112 | ); 113 | path = ActionSheet; 114 | sourceTree = ""; 115 | }; 116 | 773E7C6E26AD7CEE00AC1A8A /* Transitions */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 773E7C7126AD7D2B00AC1A8A /* Slide */, 120 | 773E7C6926AD6BAE00AC1A8A /* ActionSheet */, 121 | 773E7C5D26AD6B7100AC1A8A /* Alert */, 122 | 773E7C7426AD82B900AC1A8A /* Fancy */, 123 | ); 124 | path = Transitions; 125 | sourceTree = ""; 126 | }; 127 | 773E7C7126AD7D2B00AC1A8A /* Slide */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 773E7C6F26AD7D2400AC1A8A /* SlideTransitionManager.swift */, 131 | 773E7C7226AD7D5F00AC1A8A /* SlideTransitionAnimator.swift */, 132 | ); 133 | path = Slide; 134 | sourceTree = ""; 135 | }; 136 | 773E7C7426AD82B900AC1A8A /* Fancy */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 773E7C7526AD82E900AC1A8A /* FancyTransitionManager.swift */, 140 | 773E7C7726AD82F400AC1A8A /* FancyTransitionAnimator.swift */, 141 | 773E7C7B26ADAD2300AC1A8A /* FancyPresentationController.swift */, 142 | ); 143 | path = Fancy; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 773E7C4526AD6B1800AC1A8A /* CustomTransitions */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 773E7C5A26AD6B1A00AC1A8A /* Build configuration list for PBXNativeTarget "CustomTransitions" */; 152 | buildPhases = ( 153 | 773E7C4226AD6B1800AC1A8A /* Sources */, 154 | 773E7C4326AD6B1800AC1A8A /* Frameworks */, 155 | 773E7C4426AD6B1800AC1A8A /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = CustomTransitions; 162 | productName = CustomTransitions; 163 | productReference = 773E7C4626AD6B1800AC1A8A /* CustomTransitions.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 773E7C3E26AD6B1800AC1A8A /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | LastSwiftUpdateCheck = 1250; 173 | LastUpgradeCheck = 1250; 174 | TargetAttributes = { 175 | 773E7C4526AD6B1800AC1A8A = { 176 | CreatedOnToolsVersion = 12.5.1; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 773E7C4126AD6B1800AC1A8A /* Build configuration list for PBXProject "CustomTransitions" */; 181 | compatibilityVersion = "Xcode 9.3"; 182 | developmentRegion = en; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 773E7C3D26AD6B1800AC1A8A; 189 | productRefGroup = 773E7C4726AD6B1800AC1A8A /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 773E7C4526AD6B1800AC1A8A /* CustomTransitions */, 194 | ); 195 | }; 196 | /* End PBXProject section */ 197 | 198 | /* Begin PBXResourcesBuildPhase section */ 199 | 773E7C4426AD6B1800AC1A8A /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 773E7C5626AD6B1A00AC1A8A /* LaunchScreen.storyboard in Resources */, 204 | 773E7C5326AD6B1A00AC1A8A /* Assets.xcassets in Resources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 773E7C4226AD6B1800AC1A8A /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 773E7C7326AD7D5F00AC1A8A /* SlideTransitionAnimator.swift in Sources */, 216 | 773E7C7026AD7D2400AC1A8A /* SlideTransitionManager.swift in Sources */, 217 | 773E7C4E26AD6B1800AC1A8A /* HomeViewController.swift in Sources */, 218 | 773E7C7C26ADAD2300AC1A8A /* FancyPresentationController.swift in Sources */, 219 | 773E7C4A26AD6B1800AC1A8A /* AppDelegate.swift in Sources */, 220 | 773E7C7E26ADB1A700AC1A8A /* UIButton+Extension.swift in Sources */, 221 | 773E7C4C26AD6B1800AC1A8A /* SceneDelegate.swift in Sources */, 222 | 773E7C6426AD6B7100AC1A8A /* AlertTransitionManager.swift in Sources */, 223 | 773E7C6326AD6B7100AC1A8A /* AlertTransitionAnimator.swift in Sources */, 224 | 773E7C7A26AD94AD00AC1A8A /* UIColor+Extension.swift in Sources */, 225 | 773E7C6B26AD725800AC1A8A /* ActionSheetTransitionManager.swift in Sources */, 226 | 773E7C6526AD6B7100AC1A8A /* AlertPresentationController.swift in Sources */, 227 | 773E7C6726AD6B9B00AC1A8A /* DetailViewController.swift in Sources */, 228 | 773E7C7626AD82E900AC1A8A /* FancyTransitionManager.swift in Sources */, 229 | 773E7C6D26AD72B900AC1A8A /* ActionSheetPresentationController.swift in Sources */, 230 | 773E7C7826AD82F400AC1A8A /* FancyTransitionAnimator.swift in Sources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXSourcesBuildPhase section */ 235 | 236 | /* Begin PBXVariantGroup section */ 237 | 773E7C5426AD6B1A00AC1A8A /* LaunchScreen.storyboard */ = { 238 | isa = PBXVariantGroup; 239 | children = ( 240 | 773E7C5526AD6B1A00AC1A8A /* Base */, 241 | ); 242 | name = LaunchScreen.storyboard; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXVariantGroup section */ 246 | 247 | /* Begin XCBuildConfiguration section */ 248 | 773E7C5826AD6B1A00AC1A8A /* Debug */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_ANALYZER_NONNULL = YES; 253 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_ENABLE_OBJC_WEAK = YES; 259 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 260 | CLANG_WARN_BOOL_CONVERSION = YES; 261 | CLANG_WARN_COMMA = YES; 262 | CLANG_WARN_CONSTANT_CONVERSION = YES; 263 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INFINITE_RECURSION = YES; 269 | CLANG_WARN_INT_CONVERSION = YES; 270 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 271 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 272 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 274 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 275 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 276 | CLANG_WARN_STRICT_PROTOTYPES = YES; 277 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 278 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 279 | CLANG_WARN_UNREACHABLE_CODE = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | COPY_PHASE_STRIP = NO; 282 | DEBUG_INFORMATION_FORMAT = dwarf; 283 | ENABLE_STRICT_OBJC_MSGSEND = YES; 284 | ENABLE_TESTABILITY = YES; 285 | GCC_C_LANGUAGE_STANDARD = gnu11; 286 | GCC_DYNAMIC_NO_PIC = NO; 287 | GCC_NO_COMMON_BLOCKS = YES; 288 | GCC_OPTIMIZATION_LEVEL = 0; 289 | GCC_PREPROCESSOR_DEFINITIONS = ( 290 | "DEBUG=1", 291 | "$(inherited)", 292 | ); 293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 294 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 295 | GCC_WARN_UNDECLARED_SELECTOR = YES; 296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 297 | GCC_WARN_UNUSED_FUNCTION = YES; 298 | GCC_WARN_UNUSED_VARIABLE = YES; 299 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 300 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 301 | MTL_FAST_MATH = YES; 302 | ONLY_ACTIVE_ARCH = YES; 303 | SDKROOT = iphoneos; 304 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 305 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 306 | }; 307 | name = Debug; 308 | }; 309 | 773E7C5926AD6B1A00AC1A8A /* Release */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 316 | CLANG_CXX_LIBRARY = "libc++"; 317 | CLANG_ENABLE_MODULES = YES; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_ENABLE_OBJC_WEAK = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 336 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 337 | CLANG_WARN_STRICT_PROTOTYPES = YES; 338 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 339 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 340 | CLANG_WARN_UNREACHABLE_CODE = YES; 341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 342 | COPY_PHASE_STRIP = NO; 343 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 344 | ENABLE_NS_ASSERTIONS = NO; 345 | ENABLE_STRICT_OBJC_MSGSEND = YES; 346 | GCC_C_LANGUAGE_STANDARD = gnu11; 347 | GCC_NO_COMMON_BLOCKS = YES; 348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 350 | GCC_WARN_UNDECLARED_SELECTOR = YES; 351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 352 | GCC_WARN_UNUSED_FUNCTION = YES; 353 | GCC_WARN_UNUSED_VARIABLE = YES; 354 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 355 | MTL_ENABLE_DEBUG_INFO = NO; 356 | MTL_FAST_MATH = YES; 357 | SDKROOT = iphoneos; 358 | SWIFT_COMPILATION_MODE = wholemodule; 359 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 360 | VALIDATE_PRODUCT = YES; 361 | }; 362 | name = Release; 363 | }; 364 | 773E7C5B26AD6B1A00AC1A8A /* Debug */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 368 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 369 | CODE_SIGN_STYLE = Automatic; 370 | DEVELOPMENT_TEAM = 3XSV7BVV48; 371 | INFOPLIST_FILE = CustomTransitions/Info.plist; 372 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 373 | LD_RUNPATH_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "@executable_path/Frameworks", 376 | ); 377 | PRODUCT_BUNDLE_IDENTIFIER = com.ondrejkorol.CustomTransitions; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | SWIFT_VERSION = 5.0; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | }; 382 | name = Debug; 383 | }; 384 | 773E7C5C26AD6B1A00AC1A8A /* Release */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 388 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 389 | CODE_SIGN_STYLE = Automatic; 390 | DEVELOPMENT_TEAM = 3XSV7BVV48; 391 | INFOPLIST_FILE = CustomTransitions/Info.plist; 392 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 393 | LD_RUNPATH_SEARCH_PATHS = ( 394 | "$(inherited)", 395 | "@executable_path/Frameworks", 396 | ); 397 | PRODUCT_BUNDLE_IDENTIFIER = com.ondrejkorol.CustomTransitions; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | SWIFT_VERSION = 5.0; 400 | TARGETED_DEVICE_FAMILY = "1,2"; 401 | }; 402 | name = Release; 403 | }; 404 | /* End XCBuildConfiguration section */ 405 | 406 | /* Begin XCConfigurationList section */ 407 | 773E7C4126AD6B1800AC1A8A /* Build configuration list for PBXProject "CustomTransitions" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | 773E7C5826AD6B1A00AC1A8A /* Debug */, 411 | 773E7C5926AD6B1A00AC1A8A /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | defaultConfigurationName = Release; 415 | }; 416 | 773E7C5A26AD6B1A00AC1A8A /* Build configuration list for PBXNativeTarget "CustomTransitions" */ = { 417 | isa = XCConfigurationList; 418 | buildConfigurations = ( 419 | 773E7C5B26AD6B1A00AC1A8A /* Debug */, 420 | 773E7C5C26AD6B1A00AC1A8A /* Release */, 421 | ); 422 | defaultConfigurationIsVisible = 0; 423 | defaultConfigurationName = Release; 424 | }; 425 | /* End XCConfigurationList section */ 426 | }; 427 | rootObject = 773E7C3E26AD6B1800AC1A8A /* Project object */; 428 | } 429 | -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/project.xcworkspace/xcuserdata/ondrejkorol.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions.xcodeproj/project.xcworkspace/xcuserdata/ondrejkorol.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/xcuserdata/ondrejkorol.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /CustomTransitions.xcodeproj/xcuserdata/ondrejkorol.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CustomTransitions.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CustomTransitions/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | // Override point for customization after application launch. 15 | return true 16 | } 17 | 18 | // MARK: UISceneSession Lifecycle 19 | 20 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 21 | // Called when a new scene session is being created. 22 | // Use this method to select a configuration to create the new scene with. 23 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ipadSpotlight40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "notification60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "settings58.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "settings87.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "ipadSpotlight80.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "iphone120-1.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "iphone120.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "iphone180.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "ipadNotification20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "ipadNotification40.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "ipadSettings29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "ipadSettings58.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "ipadSpotlight40-1.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "ipadSpotlight80-1.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "ipad76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "ipad152.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "ipadPro167.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "appstore1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/appstore1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/appstore1024.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipad152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipad152.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipad76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipad76.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadPro167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadPro167.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40-1.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80-1.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone120-1.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone120.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/iphone180.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/notification60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/notification60.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/settings58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/settings58.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/AppIcon.appiconset/settings87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingondrej/Transitions/e2a4ed90eafacda71acde15f61dd1429c5f4793f/CustomTransitions/Assets.xcassets/AppIcon.appiconset/settings87.png -------------------------------------------------------------------------------- /CustomTransitions/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CustomTransitions/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 | -------------------------------------------------------------------------------- /CustomTransitions/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class DetailViewController: UIViewController { 11 | 12 | // MARK: Views 13 | lazy var titleLabel: UILabel = { 14 | let label = UILabel() 15 | label.textColor = .white 16 | label.textAlignment = .center 17 | label.font = UIFont.systemFont(ofSize: 20, weight: .bold) 18 | label.text = title 19 | return label 20 | }() 21 | 22 | lazy var textLabel: UILabel = { 23 | let label = UILabel() 24 | label.textColor = .white 25 | label.textAlignment = .center 26 | label.numberOfLines = 0 27 | label.font = UIFont.systemFont(ofSize: 17, weight: .medium) 28 | label.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." 29 | return label 30 | }() 31 | 32 | lazy var dismissButton: UIButton = { 33 | let button = UIButton.appButton() 34 | button.setTitle("Dismiss", for: .normal) 35 | button.addTarget(self, action: #selector(didTapDismiss), for: .touchUpInside) 36 | return button 37 | }() 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | 42 | view.backgroundColor = .appBlack 43 | 44 | // Subviews 45 | [titleLabel, textLabel, dismissButton].forEach { 46 | view.addSubview($0) 47 | $0.translatesAutoresizingMaskIntoConstraints = false 48 | } 49 | 50 | titleLabel.setContentHuggingPriority(.required, for: .vertical) 51 | 52 | let inset: CGFloat = 24 53 | NSLayoutConstraint.activate([ 54 | titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), 55 | titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: inset), 56 | titleLabel.bottomAnchor.constraint(equalTo: textLabel.topAnchor, constant: -inset), 57 | 58 | textLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: inset), 59 | textLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -inset), 60 | textLabel.bottomAnchor.constraint(equalTo: dismissButton.topAnchor, constant: -inset), 61 | 62 | dismissButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), 63 | dismissButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -inset), 64 | ]) 65 | } 66 | 67 | @objc private func didTapDismiss() { 68 | dismiss(animated: true) 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /CustomTransitions/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewController.swift 3 | // PresentAlert 4 | // 5 | // Created by Ondřej Korol on 20.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | // MARK: Views 13 | private lazy var stackView: UIStackView = { 14 | let stackView = UIStackView() 15 | stackView.axis = .vertical 16 | stackView.spacing = 24 17 | return stackView 18 | }() 19 | 20 | private lazy var defaultButton: UIButton = { 21 | let button = UIButton.appButton() 22 | button.setTitle("Default", for: .normal) 23 | button.addTarget(self, action: #selector(didTapDefault), for: .touchUpInside) 24 | return button 25 | }() 26 | 27 | private lazy var slideButton: UIButton = { 28 | let button = UIButton.appButton() 29 | button.setTitle("Slide", for: .normal) 30 | button.addTarget(self, action: #selector(didTapSlide), for: .touchUpInside) 31 | return button 32 | }() 33 | 34 | private lazy var actionSheetButton: UIButton = { 35 | let button = UIButton.appButton() 36 | button.setTitle("Action Sheet", for: .normal) 37 | button.addTarget(self, action: #selector(didTapActionSheet), for: .touchUpInside) 38 | return button 39 | }() 40 | 41 | private lazy var alertButton: UIButton = { 42 | let button = UIButton.appButton() 43 | button.setTitle("Alert", for: .normal) 44 | button.addTarget(self, action: #selector(didTapAlert), for: .touchUpInside) 45 | return button 46 | }() 47 | 48 | lazy var fancyButton: UIButton = { 49 | let button = UIButton.appButton() 50 | button.setTitle("Fancy", for: .normal) 51 | button.addTarget(self, action: #selector(didTapFancy), for: .touchUpInside) 52 | return button 53 | }() 54 | 55 | // MARK: Transitions 56 | let slideTransitionManager = SlideTransitionManager() 57 | let actionSheetTransitionManager = ActionSheetTransitionManager() 58 | let alertTransitionManager = AlertTransitionManager() 59 | let fancyTransitionManager = FancyTransitionManager() 60 | 61 | // MARK: Lifecycle 62 | override func viewDidLoad() { 63 | super.viewDidLoad() 64 | 65 | view.backgroundColor = .white 66 | 67 | view.addSubview(stackView) 68 | stackView.translatesAutoresizingMaskIntoConstraints = false 69 | NSLayoutConstraint.activate([ 70 | stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), 71 | stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor) 72 | ]) 73 | 74 | [defaultButton, slideButton, actionSheetButton, alertButton, fancyButton].forEach { 75 | stackView.addArrangedSubview($0) 76 | } 77 | } 78 | } 79 | 80 | // MARK: - Private actions 81 | private extension HomeViewController { 82 | @objc func didTapDefault(_ sender: UIButton) { 83 | let detailViewController = DetailViewController() 84 | detailViewController.title = sender.currentTitle 85 | 86 | present(detailViewController, animated: true) 87 | } 88 | 89 | @objc func didTapSlide(_ sender: UIButton) { 90 | let detailViewController = DetailViewController() 91 | detailViewController.title = sender.currentTitle 92 | 93 | detailViewController.modalPresentationStyle = .custom 94 | detailViewController.transitioningDelegate = slideTransitionManager 95 | 96 | present(detailViewController, animated: true) 97 | } 98 | 99 | @objc func didTapActionSheet(_ sender: UIButton) { 100 | let detailViewController = DetailViewController() 101 | detailViewController.title = sender.currentTitle 102 | 103 | detailViewController.modalPresentationStyle = .custom 104 | detailViewController.transitioningDelegate = actionSheetTransitionManager 105 | 106 | present(detailViewController, animated: true) 107 | } 108 | 109 | @objc func didTapAlert(_ sender: UIButton) { 110 | let detailViewController = DetailViewController() 111 | detailViewController.title = sender.currentTitle 112 | 113 | detailViewController.modalPresentationStyle = .custom 114 | detailViewController.transitioningDelegate = alertTransitionManager 115 | 116 | present(detailViewController, animated: true) 117 | } 118 | 119 | @objc func didTapFancy(_ sender: UIButton) { 120 | let detailViewController = DetailViewController() 121 | detailViewController.title = sender.currentTitle 122 | 123 | detailViewController.modalPresentationStyle = .custom 124 | detailViewController.transitioningDelegate = fancyTransitionManager 125 | 126 | present(detailViewController, animated: true) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /CustomTransitions/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Transitions 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /CustomTransitions/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // PresentAlert 4 | // 5 | // Created by Ondřej Korol on 20.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 15 | guard let windowScene = (scene as? UIWindowScene) else { 16 | return 17 | } 18 | 19 | let viewController = HomeViewController() 20 | window = UIWindow(windowScene: windowScene) 21 | window?.rootViewController = viewController 22 | window?.makeKeyAndVisible() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/ActionSheet/ActionSheetPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheetPresentationController.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ActionSheetPresentationController: UIPresentationController { 11 | 12 | // MARK: Constants 13 | private static let actionSheetCornerRadius: CGFloat = 30 14 | private static let dimmingViewMaxAlpha: CGFloat = 1.0 15 | 16 | // MARK: Views 17 | private lazy var dimmingView: UIView = { 18 | let blurEffect = UIBlurEffect(style: .systemMaterialDark) 19 | let view = UIVisualEffectView(effect: blurEffect) 20 | return view 21 | }() 22 | 23 | // MARK: Gesture Recognizers 24 | private let tapGestureRecognizer = UITapGestureRecognizer() 25 | private let panGestureRecognizer = UIPanGestureRecognizer() 26 | 27 | // MARK: Initialization 28 | override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) { 29 | super.init(presentedViewController: presentedViewController, presenting: presentingViewController) 30 | configureGestureRecognizers() 31 | } 32 | 33 | // MARK: Presentation 34 | override var frameOfPresentedViewInContainerView: CGRect { 35 | guard let containerView = containerView else { 36 | return .zero 37 | } 38 | 39 | let targetWidth = containerView.bounds.width 40 | let targetHeight = containerView.bounds.height * 0.6 41 | 42 | let originX: CGFloat = .zero 43 | let originY: CGFloat = containerView.bounds.height - targetHeight 44 | 45 | return CGRect( 46 | x: originX, 47 | y: originY, 48 | width: targetWidth, 49 | height: targetHeight 50 | ) 51 | } 52 | 53 | override func containerViewWillLayoutSubviews() { 54 | super.containerViewWillLayoutSubviews() 55 | presentedView?.frame = frameOfPresentedViewInContainerView 56 | dimmingView.frame = containerView?.bounds ?? .zero 57 | } 58 | 59 | override func presentationTransitionWillBegin() { 60 | super.presentationTransitionWillBegin() 61 | guard 62 | let containerView = containerView, 63 | let coordinator = presentingViewController.transitionCoordinator 64 | else { 65 | return 66 | } 67 | 68 | presentedView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 69 | presentedView?.layer.cornerRadius = Self.actionSheetCornerRadius 70 | 71 | dimmingView.frame = containerView.bounds 72 | dimmingView.alpha = 0 73 | containerView.addSubview(dimmingView) 74 | 75 | coordinator.animate( 76 | alongsideTransition: { _ -> Void in 77 | self.dimmingView.alpha = Self.dimmingViewMaxAlpha 78 | }, 79 | completion: nil 80 | ) 81 | } 82 | 83 | override func dismissalTransitionWillBegin() { 84 | super.dismissalTransitionWillBegin() 85 | guard let coordinator = presentingViewController.transitionCoordinator else { 86 | return 87 | } 88 | 89 | coordinator.animate( 90 | alongsideTransition: { _ -> Void in 91 | self.dimmingView.alpha = 0 92 | }, 93 | completion: nil 94 | ) 95 | } 96 | 97 | override func dismissalTransitionDidEnd(_ completed: Bool) { 98 | super.dismissalTransitionDidEnd(completed) 99 | if completed { 100 | dimmingView.removeFromSuperview() 101 | } 102 | } 103 | } 104 | 105 | // MARK: - Private methods 106 | private extension ActionSheetPresentationController { 107 | func configureGestureRecognizers() { 108 | tapGestureRecognizer.addTarget(self, action: #selector(handleTap)) 109 | dimmingView.addGestureRecognizer(tapGestureRecognizer) 110 | 111 | panGestureRecognizer.addTarget(self, action: #selector(handlePan)) 112 | presentedView?.addGestureRecognizer(panGestureRecognizer) 113 | } 114 | 115 | @objc func handleTap(sender: UITapGestureRecognizer) { 116 | presentedViewController.dismiss(animated: true) 117 | } 118 | 119 | @objc func handlePan(sender: UIPanGestureRecognizer) { 120 | switch sender.state { 121 | case .changed: 122 | guard 123 | let presentedView = presentedView 124 | else { 125 | return 126 | } 127 | 128 | let translationY = sender.translation(in: presentedView).y 129 | let originY = frameOfPresentedViewInContainerView.minY 130 | let updatedOriginY = originY + translationY 131 | 132 | // Update position 133 | presentedView.frame.origin.y = max(updatedOriginY, originY) 134 | 135 | // Update alpha 136 | if updatedOriginY >= originY { 137 | dimmingView.alpha = (1 - (translationY / presentedView.bounds.height)) * Self.dimmingViewMaxAlpha 138 | } 139 | 140 | // Workaround for inconsistent view layout during transition. 141 | // Adjusting safe area because safe area disappears when frame is changed. 142 | presentedViewController.additionalSafeAreaInsets.bottom = updatedOriginY > originY ? 143 | containerView?.safeAreaInsets.bottom ?? 0 : 144 | 0.0 145 | case .ended: 146 | guard let presentedView = presentedView else { 147 | return 148 | } 149 | 150 | let velocityY = sender.velocity(in: presentedView).y 151 | let originY = frameOfPresentedViewInContainerView.minY 152 | let viewHeight = presentedView.frame.height 153 | 154 | // Dismiss only if the gesture is in the correct direction 155 | let directionCheck = velocityY > 0 156 | 157 | // Dismiss only if more than 40% of modal is hidden or there is a significant velocity 158 | let distanceCheck = presentedView.frame.origin.y > originY + (viewHeight * 0.4) 159 | let velocityCheck = velocityY > 500 160 | 161 | if directionCheck, distanceCheck || velocityCheck { 162 | presentedViewController.dismiss(animated: true) 163 | } else { 164 | UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { 165 | self.presentedView?.frame.origin.y = originY 166 | self.dimmingView.alpha = Self.dimmingViewMaxAlpha 167 | self.presentedViewController.additionalSafeAreaInsets.bottom = 0 168 | } 169 | } 170 | default: 171 | break 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/ActionSheet/ActionSheetTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheetTransitionManager.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ActionSheetTransitionManager: NSObject, UIViewControllerTransitioningDelegate { 11 | 12 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 13 | return nil 14 | } 15 | 16 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 17 | return nil 18 | } 19 | 20 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 21 | return ActionSheetPresentationController( 22 | presentedViewController: presented, 23 | presenting: presenting 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Alert/AlertPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertPresentationController.swift 3 | // PresentAlert 4 | // 5 | // Created by Ondřej Korol on 20.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class AlertPresentationController: UIPresentationController { 11 | 12 | // MARK: Constants 13 | private static let alertCornerRadius: CGFloat = 30 14 | private static let alertHorizontalInset: CGFloat = 24 15 | private static let dimmingViewMaxAlpha: CGFloat = 1.0 16 | 17 | // MARK: Views 18 | private lazy var dimmingView: UIView = { 19 | let blurEffect = UIBlurEffect(style: .systemMaterialDark) 20 | let view = UIVisualEffectView(effect: blurEffect) 21 | return view 22 | }() 23 | 24 | // MARK: Presentation 25 | override var frameOfPresentedViewInContainerView: CGRect { 26 | guard 27 | let containerView = containerView, 28 | let presentedView = presentedView 29 | else { 30 | return .zero 31 | } 32 | 33 | let safeAreaFrame = containerView.safeAreaLayoutGuide.layoutFrame 34 | 35 | let targetWidth = safeAreaFrame.width - 2 * Self.alertHorizontalInset 36 | let fittingSize = CGSize( 37 | width: targetWidth, 38 | height: UIView.layoutFittingCompressedSize.height 39 | ) 40 | 41 | let targetHeight = presentedView.systemLayoutSizeFitting( 42 | fittingSize, 43 | withHorizontalFittingPriority: .required, 44 | verticalFittingPriority: .defaultLow 45 | ).height 46 | 47 | return CGRect( 48 | x: containerView.center.x - targetWidth / 2, 49 | y: containerView.center.y - targetHeight / 2, 50 | width: targetWidth, 51 | height: targetHeight 52 | ) 53 | } 54 | 55 | override func containerViewWillLayoutSubviews() { 56 | super.containerViewWillLayoutSubviews() 57 | 58 | presentedViewController.view.setNeedsLayout() 59 | presentedViewController.view.layoutIfNeeded() 60 | presentedView?.frame = frameOfPresentedViewInContainerView 61 | 62 | dimmingView.frame = containerView?.bounds ?? .zero 63 | } 64 | 65 | override func presentationTransitionWillBegin() { 66 | super.presentationTransitionWillBegin() 67 | guard 68 | let containerView = containerView, 69 | let coordinator = presentingViewController.transitionCoordinator 70 | else { 71 | return 72 | } 73 | 74 | presentedView?.layer.cornerRadius = Self.alertCornerRadius 75 | 76 | dimmingView.frame = containerView.bounds 77 | dimmingView.alpha = 0 78 | containerView.addSubview(dimmingView) 79 | 80 | coordinator.animate( 81 | alongsideTransition: { _ -> Void in 82 | self.dimmingView.alpha = Self.dimmingViewMaxAlpha 83 | }, 84 | completion: nil 85 | ) 86 | } 87 | 88 | override func dismissalTransitionWillBegin() { 89 | super.dismissalTransitionWillBegin() 90 | guard let coordinator = presentingViewController.transitionCoordinator else { 91 | return 92 | } 93 | 94 | coordinator.animate( 95 | alongsideTransition: { _ -> Void in 96 | self.dimmingView.alpha = 0 97 | }, 98 | completion: nil 99 | ) 100 | } 101 | 102 | override func dismissalTransitionDidEnd(_ completed: Bool) { 103 | super.dismissalTransitionDidEnd(completed) 104 | if completed { 105 | dimmingView.removeFromSuperview() 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Alert/AlertTransitionAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertTransitionAnimator.swift 3 | // PresentAlert 4 | // 5 | // Created by Ondřej Korol on 20.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class AlertTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { 11 | enum Transition { 12 | case presenting 13 | case dismissing 14 | 15 | var duration: TimeInterval { 16 | switch self { 17 | case .presenting: 18 | return 0.3 19 | case .dismissing: 20 | return 0.2 21 | } 22 | } 23 | } 24 | 25 | private static let scaleTransform = CGAffineTransform(scaleX: 0.8, y: 0.8) 26 | 27 | let transition: Transition 28 | 29 | // MARK: Initialization 30 | init(transition: Transition) { 31 | self.transition = transition 32 | } 33 | 34 | // MARK: UIViewControllerAnimatedTransitioning 35 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 36 | transition.duration 37 | } 38 | 39 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 40 | let key: UITransitionContextViewKey = transition == .presenting ? .to : .from 41 | 42 | guard let viewToAnimate = transitionContext.view(forKey: key) else { 43 | transitionContext.completeTransition(false) 44 | return 45 | } 46 | 47 | switch transition { 48 | case .presenting: 49 | let containerView = transitionContext.containerView 50 | containerView.addSubview(viewToAnimate) 51 | 52 | viewToAnimate.alpha = 0 53 | viewToAnimate.transform = Self.scaleTransform 54 | 55 | UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, usingSpringWithDamping: 0.95, initialSpringVelocity: 0) { 56 | viewToAnimate.alpha = 1 57 | viewToAnimate.transform = .identity 58 | } completion: { _ in 59 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 60 | } 61 | 62 | case .dismissing: 63 | UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, usingSpringWithDamping: 0.95, initialSpringVelocity: 0, animations: { 64 | viewToAnimate.alpha = 0 65 | viewToAnimate.transform = Self.scaleTransform 66 | }) { _ in 67 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Alert/AlertTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertTransitionManager.swift 3 | // PresentAlert 4 | // 5 | // Created by Ondřej Korol on 20.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class AlertTransitionManager: NSObject, UIViewControllerTransitioningDelegate { 11 | 12 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 13 | return AlertTransitionAnimator(transition: .presenting) 14 | } 15 | 16 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 17 | return AlertTransitionAnimator(transition: .dismissing) 18 | } 19 | 20 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 21 | return AlertPresentationController( 22 | presentedViewController: presented, 23 | presenting: presenting 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Fancy/FancyPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FancyPresentationController.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class FancyPresentationController: UIPresentationController { 11 | // MARK: Presentation 12 | /** 13 | If false is returned from `shouldRemovePresentersView`, the view associated with UITransitionContext.view(forKey: from) is nil during presentation. This intended to be a hint that your animator should NOT be manipulating the presenting view controller's view. For a dismissal, the presentedView is returned. 14 | */ 15 | override var shouldRemovePresentersView: Bool { 16 | return true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Fancy/FancyTransitionAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FancyTransitionAnimator.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class FancyTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { 11 | enum Transition { 12 | case presenting 13 | case dismissing 14 | 15 | var duration: TimeInterval { 16 | return 1.5 17 | } 18 | } 19 | 20 | let transition: Transition 21 | 22 | // MARK: Initialization 23 | init(transition: Transition) { 24 | self.transition = transition 25 | } 26 | 27 | // MARK: UIViewControllerAnimatedTransitioning 28 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 29 | transition.duration 30 | } 31 | 32 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 33 | guard 34 | let fromViewController = transitionContext.viewController(forKey: .from), 35 | let toViewController = transitionContext.viewController(forKey: .to), 36 | let fromView = transitionContext.view(forKey: .from), 37 | let toView = transitionContext.view(forKey: .to) 38 | else { 39 | transitionContext.completeTransition(false) 40 | return 41 | } 42 | 43 | let containerView = transitionContext.containerView 44 | let fromColor = fromView.backgroundColor 45 | let toColor = toView.backgroundColor 46 | 47 | // Helper container view 48 | let colorView = UIView() 49 | colorView.alpha = 0 50 | colorView.backgroundColor = fromColor 51 | colorView.frame = fromView.frame 52 | containerView.addSubview(colorView) 53 | 54 | // Destination View 55 | containerView.addSubview(toView) 56 | toView.layoutIfNeeded() 57 | toView.alpha = 0 58 | 59 | // Helper label 60 | let fakeLabel = UILabel() 61 | 62 | let fromLabel = transition == .presenting ? 63 | (fromViewController as? HomeViewController)?.fancyButton.titleLabel : 64 | (fromViewController as? DetailViewController)?.titleLabel 65 | 66 | if let fromLabel = fromLabel { 67 | fakeLabel.text = fromLabel.text 68 | fakeLabel.font = fromLabel.font 69 | fakeLabel.textColor = fromLabel.textColor 70 | colorView.addSubview(fakeLabel) 71 | fakeLabel.frame = fromLabel.convert(fromLabel.bounds, to: nil) 72 | } 73 | 74 | let toLabel = transition == .presenting ? 75 | (toViewController as? DetailViewController)?.titleLabel : 76 | (toViewController as? HomeViewController)?.fancyButton.titleLabel 77 | 78 | var fakeLabelFinalRect: CGRect = .zero 79 | if let toLabel = toLabel { 80 | fakeLabelFinalRect = toLabel.convert(toLabel.bounds, to: nil) 81 | } 82 | 83 | UIView.animateKeyframes(withDuration: transitionDuration(using: transitionContext), delay: 0, animations: { 84 | UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25) { 85 | colorView.alpha = 1 86 | } 87 | 88 | UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.50) { 89 | colorView.backgroundColor = toColor 90 | fakeLabel.frame = fakeLabelFinalRect 91 | } 92 | 93 | UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) { 94 | toView.alpha = 1 95 | } 96 | }) { _ in 97 | // Remove helper view 98 | colorView.removeFromSuperview() 99 | 100 | // Notifies the system that the transition animation is done 101 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Fancy/FancyTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FancyTransitionManager.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class FancyTransitionManager: NSObject, UIViewControllerTransitioningDelegate { 11 | 12 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 13 | return FancyTransitionAnimator(transition: .presenting) 14 | } 15 | 16 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 17 | return FancyTransitionAnimator(transition: .dismissing) 18 | } 19 | 20 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 21 | return FancyPresentationController( 22 | presentedViewController: presented, 23 | presenting: presenting 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Slide/SlideTransitionAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlideTransitionAnimator.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SlideTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { 11 | 12 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 13 | return 1.25 14 | } 15 | 16 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 17 | // 1. Get the view of the presented view controller 18 | guard let toView = transitionContext.view(forKey: .to) else { 19 | transitionContext.completeTransition(false) 20 | return 21 | } 22 | 23 | // 2. Add the view to the transition's container view 24 | let containerView = transitionContext.containerView 25 | containerView.addSubview(toView) 26 | 27 | // 3. Prepare the view for an animation 28 | toView.layoutIfNeeded() 29 | toView.transform = CGAffineTransform(translationX: toView.bounds.width, y: .zero) 30 | 31 | // 4. Perform the actual animation 32 | UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, usingSpringWithDamping: 0.50, initialSpringVelocity: 0, animations: { 33 | toView.transform = .identity 34 | }) { _ in 35 | // 5. Tell UIKit that the transition should end 36 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CustomTransitions/Transitions/Slide/SlideTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlideTransitionManager.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SlideTransitionManager: NSObject, UIViewControllerTransitioningDelegate { 11 | 12 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 13 | return SlideTransitionAnimator() 14 | } 15 | 16 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 17 | return nil 18 | } 19 | 20 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 21 | return nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CustomTransitions/UIButton+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Extension.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIButton { 11 | static func appButton() -> UIButton { 12 | let button = UIButton(type: .system) 13 | button.titleLabel?.font = UIFont.systemFont(ofSize: 20, weight: .bold) 14 | button.tintColor = .appRed 15 | return button 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CustomTransitions/UIColor+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.swift 3 | // CustomTransitions 4 | // 5 | // Created by Ondřej Korol on 25.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | static let appBlack = UIColor(red: 0.07, green: 0.08, blue: 0.09, alpha: 1.00) 12 | static let appRed = UIColor(red: 0.87, green: 0.20, blue: 0.24, alpha: 1.00) 13 | } 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mac256 2 | 3 | # Transitions 4 | A simple iOS app with one default and four custom transitions. 5 | The app uses the same two view controllers for every transition. 6 | 7 | ## Slide 8 | The presented view controller slides in from the right with little overshoot. It uses the default animation provided by UIKit for its dismissal. 9 | 10 | ## Action Sheet 11 | The presented view controller slides in from the bottom with the default animation provided by UIKit. 12 | 13 | Along with the transition, the background (presenting view controller) is dimmed by the blurred view. 14 | 15 | The view controller can be dismissed by tap on the dimmed view or by pulling it down. It uses the default animation provided by UIKit for its dismissal. 16 | 17 | ## Alert 18 | The presented view controller appears in the center by enlarging and fading in. 19 | 20 | Along with the transition, the background (presenting view controller) is dimmed with the blurred view. 21 | 22 | The view controller disappears by shrinking and fading out. 23 | 24 | ## Fancy 25 | The presented view controller fades in while the selected button of the presenting view controller moves across the screen to the position of the presented view controller's title label. Other elements fades out. 26 | 27 | 28 | 29 | ![custom action sheet](https://user-images.githubusercontent.com/18324562/126958186-e73e8b65-3ac2-4c61-a38c-f3d9c4a05048.gif) 30 | 31 | --------------------------------------------------------------------------------