├── README.md ├── TransitioningAnimationDemo1.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── TransitioningAnimationDemo1 ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── PictureBroswerTransitionAnimator.h ├── PictureBroswerTransitionAnimator.m ├── PictureBroswerViewController.h ├── PictureBroswerViewController.m ├── ViewController.h ├── ViewController.m └── main.m └── screenshots └── TransitioningAnimationDemo1.gif /README.md: -------------------------------------------------------------------------------- 1 | # TransitioningAnimationDemo1 2 | 3 | > 转场,从一个场景转换到另一个场景。 4 | 5 | ![TransitioningAnimationDemo1.gif](https://github.com/JiongXing/TransitioningAnimationDemo1/raw/master/screenshots/TransitioningAnimationDemo1.gif) 6 | 7 | # modal动画 8 | 通过`- (void)presentViewController:animated:completion:`方法跳转到另一个页面时,可以自定义modal动画。 9 | > 从代码的角度看,启用动画的入口是这样的: 10 | 11 | ```objc 12 | vc.transitioningDelegate = self; 13 | [self presentViewController:vc animated:YES completion:nil]; 14 | ``` 15 | 给vc的transitioningDelegate属性赋值,为即将跳转的vc指定转场动画代理。简单起见,在这里,当前controller自己充当了代理。而作为代理需要实现``协议。 16 | ```objc 17 | @interface ViewController () 18 | ``` 19 | 20 | 协议中有两个基础方法,分别要求代理返回present时的动画以及dismiss时的动画。 21 | ```objc 22 | - (nullable id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source; 23 | - (nullable id )animationControllerForDismissedController:(UIViewController *)dismissed; 24 | ``` 25 | 剩下的事情,就需要代理去考虑怎么搞出两个实现了``协议的动画对象来。 26 | 27 | > 我们可以写一个类专门来实现``动画协议,作为动画的实现类。然后代理就可以轻松搞出两个动画对象,实现``转场代理协议。 28 | 29 | ```objc 30 | @interface PictureBroswerTransitionAnimator : NSObject 31 | ``` 32 | 在``动画协议中,有两个必须实现的方法: 33 | ```objc 34 | /// 返回动画持续时间 35 | - (NSTimeInterval)transitionDuration:(nullable id )transitionContext; 36 | /// 动画实现过程 37 | - (void)animateTransition:(id )transitionContext; 38 | ``` 39 | 可以看到,其中的关键方法就是`-(void)animateTransition:`,系统会向此方法传入一个参数`transitionContext`,它代表了整个转场环境,包含了转场过程中的关键信息,比如从哪里转到哪里。 40 | 41 | > 实现动画的关键方法 42 | `- (void)animateTransition:(id)transitionContext` 43 | 44 | 获取转场过程的三个视图:containerView、fromView、toView。 45 | containerView是动画过程中提供的暂时容器。 46 | fromView是转场开始页的视图。 47 | toView是转场结束页的视图。 48 | ```objc 49 | UIView *fromView; 50 | UIView *toView; 51 | if ([transitionContext respondsToSelector:@selector(viewForKey:)]) { 52 | // iOS8以上用此方法准确获取 53 | fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; 54 | toView = [transitionContext viewForKey:UITransitionContextToViewKey]; 55 | } 56 | else { 57 | fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view; 58 | toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view; 59 | } 60 | UIView *container = [transitionContext containerView]; 61 | ``` 62 | 转场的过程,大多数情况下我们都是对toView作各种变换操作,例如改变toView的alpha,size,旋转等等。 在对它进行操作前,需要先把它放到container上才能显示出来。 63 | `[container addSubview:toView];` 64 | 65 | 最后需要我们自己弄出个动画来~囧 66 | ```objc 67 | pictureView.transform = startTransform; 68 | pictureView.center = startCenter; 69 | [UIView animateWithDuration:self.duration animations:^{ 70 | pictureView.transform = endTransform; 71 | pictureView.center = endCenter; 72 | } completion:^(BOOL finished) { 73 | BOOL wasCancelled = [transitionContext transitionWasCancelled]; 74 | [transitionContext completeTransition:!wasCancelled]; 75 | }]; 76 | ``` 77 | 这里注意一下,在我们自己写的动画完结时,一定要告诉所在的转场环境对象`transitionContext`,我们的动画完成了,它才会进行动画结束后的收尾工作: 78 | ```objc 79 | [transitionContext completeTransition:YES]; 80 | ``` 81 | 在转场的过程中,动画有可能被各种原因打断,通过`transitionWasCancelled`方法可以知道是否被打断: 82 | ```objc 83 | BOOL wasCancelled = [transitionContext transitionWasCancelled]; 84 | [transitionContext completeTransition:!wasCancelled]; 85 | ``` 86 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9407E44E1D9CECA5009D488F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9407E44D1D9CECA5009D488F /* main.m */; }; 11 | 9407E4511D9CECA5009D488F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9407E4501D9CECA5009D488F /* AppDelegate.m */; }; 12 | 9407E4541D9CECA5009D488F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9407E4531D9CECA5009D488F /* ViewController.m */; }; 13 | 9407E4571D9CECA5009D488F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9407E4551D9CECA5009D488F /* Main.storyboard */; }; 14 | 9407E4591D9CECA5009D488F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9407E4581D9CECA5009D488F /* Assets.xcassets */; }; 15 | 9407E45C1D9CECA5009D488F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9407E45A1D9CECA5009D488F /* LaunchScreen.storyboard */; }; 16 | 9407E4671D9CECE1009D488F /* PictureBroswerTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9407E4641D9CECE1009D488F /* PictureBroswerTransitionAnimator.m */; }; 17 | 9407E4681D9CECE1009D488F /* PictureBroswerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9407E4661D9CECE1009D488F /* PictureBroswerViewController.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 9407E4491D9CECA5009D488F /* TransitioningAnimationDemo1.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TransitioningAnimationDemo1.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 9407E44D1D9CECA5009D488F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 23 | 9407E44F1D9CECA5009D488F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 9407E4501D9CECA5009D488F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 9407E4521D9CECA5009D488F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | 9407E4531D9CECA5009D488F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | 9407E4561D9CECA5009D488F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 9407E4581D9CECA5009D488F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 9407E45B1D9CECA5009D488F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 9407E45D1D9CECA5009D488F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 9407E4631D9CECE1009D488F /* PictureBroswerTransitionAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureBroswerTransitionAnimator.h; sourceTree = ""; }; 32 | 9407E4641D9CECE1009D488F /* PictureBroswerTransitionAnimator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureBroswerTransitionAnimator.m; sourceTree = ""; }; 33 | 9407E4651D9CECE1009D488F /* PictureBroswerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureBroswerViewController.h; sourceTree = ""; }; 34 | 9407E4661D9CECE1009D488F /* PictureBroswerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureBroswerViewController.m; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 9407E4461D9CECA5009D488F /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 9407E4401D9CECA5009D488F = { 49 | isa = PBXGroup; 50 | children = ( 51 | 9407E44B1D9CECA5009D488F /* TransitioningAnimationDemo1 */, 52 | 9407E44A1D9CECA5009D488F /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 9407E44A1D9CECA5009D488F /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 9407E4491D9CECA5009D488F /* TransitioningAnimationDemo1.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | 9407E44B1D9CECA5009D488F /* TransitioningAnimationDemo1 */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 9407E44F1D9CECA5009D488F /* AppDelegate.h */, 68 | 9407E4501D9CECA5009D488F /* AppDelegate.m */, 69 | 9407E4521D9CECA5009D488F /* ViewController.h */, 70 | 9407E4531D9CECA5009D488F /* ViewController.m */, 71 | 9407E4651D9CECE1009D488F /* PictureBroswerViewController.h */, 72 | 9407E4661D9CECE1009D488F /* PictureBroswerViewController.m */, 73 | 9407E4631D9CECE1009D488F /* PictureBroswerTransitionAnimator.h */, 74 | 9407E4641D9CECE1009D488F /* PictureBroswerTransitionAnimator.m */, 75 | 9407E4551D9CECA5009D488F /* Main.storyboard */, 76 | 9407E4581D9CECA5009D488F /* Assets.xcassets */, 77 | 9407E45A1D9CECA5009D488F /* LaunchScreen.storyboard */, 78 | 9407E45D1D9CECA5009D488F /* Info.plist */, 79 | 9407E44C1D9CECA5009D488F /* Supporting Files */, 80 | ); 81 | path = TransitioningAnimationDemo1; 82 | sourceTree = ""; 83 | }; 84 | 9407E44C1D9CECA5009D488F /* Supporting Files */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9407E44D1D9CECA5009D488F /* main.m */, 88 | ); 89 | name = "Supporting Files"; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXNativeTarget section */ 95 | 9407E4481D9CECA5009D488F /* TransitioningAnimationDemo1 */ = { 96 | isa = PBXNativeTarget; 97 | buildConfigurationList = 9407E4601D9CECA5009D488F /* Build configuration list for PBXNativeTarget "TransitioningAnimationDemo1" */; 98 | buildPhases = ( 99 | 9407E4451D9CECA5009D488F /* Sources */, 100 | 9407E4461D9CECA5009D488F /* Frameworks */, 101 | 9407E4471D9CECA5009D488F /* Resources */, 102 | ); 103 | buildRules = ( 104 | ); 105 | dependencies = ( 106 | ); 107 | name = TransitioningAnimationDemo1; 108 | productName = TransitioningAnimationDemo1; 109 | productReference = 9407E4491D9CECA5009D488F /* TransitioningAnimationDemo1.app */; 110 | productType = "com.apple.product-type.application"; 111 | }; 112 | /* End PBXNativeTarget section */ 113 | 114 | /* Begin PBXProject section */ 115 | 9407E4411D9CECA5009D488F /* Project object */ = { 116 | isa = PBXProject; 117 | attributes = { 118 | LastUpgradeCheck = 0800; 119 | ORGANIZATIONNAME = JiongXing; 120 | TargetAttributes = { 121 | 9407E4481D9CECA5009D488F = { 122 | CreatedOnToolsVersion = 8.0; 123 | ProvisioningStyle = Automatic; 124 | }; 125 | }; 126 | }; 127 | buildConfigurationList = 9407E4441D9CECA5009D488F /* Build configuration list for PBXProject "TransitioningAnimationDemo1" */; 128 | compatibilityVersion = "Xcode 3.2"; 129 | developmentRegion = English; 130 | hasScannedForEncodings = 0; 131 | knownRegions = ( 132 | en, 133 | Base, 134 | ); 135 | mainGroup = 9407E4401D9CECA5009D488F; 136 | productRefGroup = 9407E44A1D9CECA5009D488F /* Products */; 137 | projectDirPath = ""; 138 | projectRoot = ""; 139 | targets = ( 140 | 9407E4481D9CECA5009D488F /* TransitioningAnimationDemo1 */, 141 | ); 142 | }; 143 | /* End PBXProject section */ 144 | 145 | /* Begin PBXResourcesBuildPhase section */ 146 | 9407E4471D9CECA5009D488F /* Resources */ = { 147 | isa = PBXResourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 9407E45C1D9CECA5009D488F /* LaunchScreen.storyboard in Resources */, 151 | 9407E4591D9CECA5009D488F /* Assets.xcassets in Resources */, 152 | 9407E4571D9CECA5009D488F /* Main.storyboard in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | 9407E4451D9CECA5009D488F /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 9407E4541D9CECA5009D488F /* ViewController.m in Sources */, 164 | 9407E4511D9CECA5009D488F /* AppDelegate.m in Sources */, 165 | 9407E44E1D9CECA5009D488F /* main.m in Sources */, 166 | 9407E4671D9CECE1009D488F /* PictureBroswerTransitionAnimator.m in Sources */, 167 | 9407E4681D9CECE1009D488F /* PictureBroswerViewController.m in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | 9407E4551D9CECA5009D488F /* Main.storyboard */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | 9407E4561D9CECA5009D488F /* Base */, 178 | ); 179 | name = Main.storyboard; 180 | sourceTree = ""; 181 | }; 182 | 9407E45A1D9CECA5009D488F /* LaunchScreen.storyboard */ = { 183 | isa = PBXVariantGroup; 184 | children = ( 185 | 9407E45B1D9CECA5009D488F /* Base */, 186 | ); 187 | name = LaunchScreen.storyboard; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXVariantGroup section */ 191 | 192 | /* Begin XCBuildConfiguration section */ 193 | 9407E45E1D9CECA5009D488F /* Debug */ = { 194 | isa = XCBuildConfiguration; 195 | buildSettings = { 196 | ALWAYS_SEARCH_USER_PATHS = NO; 197 | CLANG_ANALYZER_NONNULL = YES; 198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 199 | CLANG_CXX_LIBRARY = "libc++"; 200 | CLANG_ENABLE_MODULES = YES; 201 | CLANG_ENABLE_OBJC_ARC = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 205 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INFINITE_RECURSION = YES; 209 | CLANG_WARN_INT_CONVERSION = YES; 210 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 211 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 212 | CLANG_WARN_UNREACHABLE_CODE = YES; 213 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 214 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 215 | COPY_PHASE_STRIP = NO; 216 | DEBUG_INFORMATION_FORMAT = dwarf; 217 | ENABLE_STRICT_OBJC_MSGSEND = YES; 218 | ENABLE_TESTABILITY = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu99; 220 | GCC_DYNAMIC_NO_PIC = NO; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_OPTIMIZATION_LEVEL = 0; 223 | GCC_PREPROCESSOR_DEFINITIONS = ( 224 | "DEBUG=1", 225 | "$(inherited)", 226 | ); 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 234 | MTL_ENABLE_DEBUG_INFO = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = iphoneos; 237 | }; 238 | name = Debug; 239 | }; 240 | 9407E45F1D9CECA5009D488F /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 253 | CLANG_WARN_EMPTY_BODY = YES; 254 | CLANG_WARN_ENUM_CONVERSION = YES; 255 | CLANG_WARN_INFINITE_RECURSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 264 | ENABLE_NS_ASSERTIONS = NO; 265 | ENABLE_STRICT_OBJC_MSGSEND = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu99; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 275 | MTL_ENABLE_DEBUG_INFO = NO; 276 | SDKROOT = iphoneos; 277 | VALIDATE_PRODUCT = YES; 278 | }; 279 | name = Release; 280 | }; 281 | 9407E4611D9CECA5009D488F /* Debug */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 285 | INFOPLIST_FILE = TransitioningAnimationDemo1/Info.plist; 286 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 287 | PRODUCT_BUNDLE_IDENTIFIER = JiongXing.TransitioningAnimationDemo1; 288 | PRODUCT_NAME = "$(TARGET_NAME)"; 289 | }; 290 | name = Debug; 291 | }; 292 | 9407E4621D9CECA5009D488F /* Release */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 296 | INFOPLIST_FILE = TransitioningAnimationDemo1/Info.plist; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | PRODUCT_BUNDLE_IDENTIFIER = JiongXing.TransitioningAnimationDemo1; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | }; 301 | name = Release; 302 | }; 303 | /* End XCBuildConfiguration section */ 304 | 305 | /* Begin XCConfigurationList section */ 306 | 9407E4441D9CECA5009D488F /* Build configuration list for PBXProject "TransitioningAnimationDemo1" */ = { 307 | isa = XCConfigurationList; 308 | buildConfigurations = ( 309 | 9407E45E1D9CECA5009D488F /* Debug */, 310 | 9407E45F1D9CECA5009D488F /* Release */, 311 | ); 312 | defaultConfigurationIsVisible = 0; 313 | defaultConfigurationName = Release; 314 | }; 315 | 9407E4601D9CECA5009D488F /* Build configuration list for PBXNativeTarget "TransitioningAnimationDemo1" */ = { 316 | isa = XCConfigurationList; 317 | buildConfigurations = ( 318 | 9407E4611D9CECA5009D488F /* Debug */, 319 | 9407E4621D9CECA5009D488F /* Release */, 320 | ); 321 | defaultConfigurationIsVisible = 0; 322 | defaultConfigurationName = Release; 323 | }; 324 | /* End XCConfigurationList section */ 325 | }; 326 | rootObject = 9407E4411D9CECA5009D488F /* Project object */; 327 | } 328 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TransitioningAnimationDemo1 4 | // 5 | // Created by JiongXing on 16/9/29. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TransitioningAnimationDemo1 4 | // 5 | // Created by JiongXing on 16/9/29. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/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 | 27 | 28 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/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 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 38 | 39 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/PictureBroswerTransitionAnimator.h: -------------------------------------------------------------------------------- 1 | // 2 | // PictureBroswerTransitionAnimator.h 3 | // TransitioningAnimationDemo 4 | // 5 | // Created by JiongXing on 16/9/28. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface PictureBroswerTransitionAnimator : NSObject 12 | 13 | /// 动画时间 14 | @property (nonatomic, assign) CGFloat duration; 15 | 16 | /// 图片原位置 17 | @property (nonatomic, assign) CGRect originFrame; 18 | 19 | /// 展示或消失 20 | @property (nonatomic, assign) BOOL presenting; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/PictureBroswerTransitionAnimator.m: -------------------------------------------------------------------------------- 1 | // 2 | // PictureBroswerTransitionAnimator.m 3 | // TransitioningAnimationDemo 4 | // 5 | // Created by JiongXing on 16/9/28. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import "PictureBroswerTransitionAnimator.h" 10 | 11 | @implementation PictureBroswerTransitionAnimator 12 | 13 | - (instancetype)init { 14 | if (self = [super init]) { 15 | self.duration = 0.3; 16 | self.presenting = YES; 17 | self.originFrame = CGRectZero; 18 | } 19 | return self; 20 | } 21 | 22 | - (NSTimeInterval)transitionDuration:(id)transitionContext { 23 | return self.duration; 24 | } 25 | 26 | - (void)animateTransition:(id)transitionContext { 27 | UIView *fromView; 28 | UIView *toView; 29 | if ([transitionContext respondsToSelector:@selector(viewForKey:)]) { 30 | // iOS8以上用此方法准确获取 31 | fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; 32 | toView = [transitionContext viewForKey:UITransitionContextToViewKey]; 33 | } 34 | else { 35 | fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view; 36 | toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view; 37 | } 38 | 39 | UIView *pictureView = self.presenting ? toView : fromView; 40 | CGFloat scaleX = CGRectGetWidth(pictureView.frame) ? CGRectGetWidth(self.originFrame) / CGRectGetWidth(pictureView.frame) : 0; 41 | CGFloat scaleY = CGRectGetHeight(pictureView.frame) ? CGRectGetHeight(self.originFrame) / CGRectGetHeight(pictureView.frame) : 0; 42 | CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY); 43 | CGPoint orginCenter = CGPointMake(CGRectGetMidX(self.originFrame), CGRectGetMidY(self.originFrame)); 44 | CGPoint pictureCenter = CGPointMake(CGRectGetMidX(pictureView.frame), CGRectGetMidY(pictureView.frame));; 45 | 46 | CGAffineTransform startTransform; 47 | CGPoint startCenter; 48 | CGAffineTransform endTransform; 49 | CGPoint endCenter; 50 | if (self.presenting) { 51 | startTransform = transform; 52 | startCenter = orginCenter; 53 | endTransform = CGAffineTransformIdentity; 54 | endCenter = pictureCenter; 55 | } 56 | else { 57 | startTransform = CGAffineTransformIdentity; 58 | startCenter = pictureCenter; 59 | endTransform = transform; 60 | endCenter = orginCenter; 61 | } 62 | 63 | UIView *container = [transitionContext containerView]; 64 | [container addSubview:toView]; 65 | [container bringSubviewToFront:pictureView]; 66 | 67 | pictureView.transform = startTransform; 68 | pictureView.center = startCenter; 69 | [UIView animateWithDuration:self.duration animations:^{ 70 | pictureView.transform = endTransform; 71 | pictureView.center = endCenter; 72 | } completion:^(BOOL finished) { 73 | BOOL wasCancelled = [transitionContext transitionWasCancelled]; 74 | [transitionContext completeTransition:!wasCancelled]; 75 | }]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/PictureBroswerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PictureBroswerViewController.h 3 | // TransitioningAnimationDemo 4 | // 5 | // Created by JiongXing on 16/9/28. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PictureBroswerViewController : UIViewController 12 | 13 | @property (nonatomic, strong) UIImage *image; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/PictureBroswerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PictureBroswerViewController.m 3 | // TransitioningAnimationDemo 4 | // 5 | // Created by JiongXing on 16/9/28. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import "PictureBroswerViewController.h" 10 | 11 | @interface PictureBroswerViewController () 12 | 13 | @property (nonatomic, strong) UIImageView *imageView; 14 | 15 | @end 16 | 17 | @implementation PictureBroswerViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | self.view.backgroundColor = [UIColor blackColor]; 22 | 23 | self.imageView.image = self.image; 24 | self.imageView.frame = self.view.bounds; 25 | [self.view addSubview:self.imageView]; 26 | } 27 | 28 | - (UIImageView *)imageView { 29 | if (!_imageView) { 30 | _imageView = [[UIImageView alloc] init]; 31 | 32 | _imageView.userInteractionEnabled = YES; 33 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onImageViewTap:)]; 34 | [_imageView addGestureRecognizer:tap]; 35 | } 36 | return _imageView; 37 | } 38 | 39 | - (void)onImageViewTap:(UITapGestureRecognizer *)tap { 40 | [self dismissViewControllerAnimated:YES completion:nil]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TransitioningAnimationDemo1 4 | // 5 | // Created by JiongXing on 16/9/29. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TransitioningAnimationDemo1 4 | // 5 | // Created by JiongXing on 16/9/29. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "PictureBroswerViewController.h" 11 | #import "PictureBroswerTransitionAnimator.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, weak) UIImageView *selectedView; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | CGFloat margin = 30; 25 | CGFloat size = (CGRectGetWidth(self.view.frame) - margin * 3) / 2.0; 26 | for (NSInteger row = 0; row < 2; row ++) { 27 | for (NSInteger col = 0; col < 2; col ++) { 28 | UIImageView *view = [self generateImageView]; 29 | view.frame = CGRectMake(margin + col * (size + margin), 30 | margin + row * (size + margin) + 64, 31 | size, 32 | size); 33 | [self.view addSubview:view]; 34 | } 35 | } 36 | } 37 | 38 | - (UIImageView *)generateImageView { 39 | UIImageView *view = [[UIImageView alloc] init]; 40 | UIColor *color = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 green:arc4random_uniform(255) / 255.0 blue:arc4random_uniform(255) / 255.0 alpha:1.0]; 41 | view.image = [self generateImageWithColor:color]; 42 | 43 | view.userInteractionEnabled = YES; 44 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onImageViewTap:)]; 45 | [view addGestureRecognizer:tap]; 46 | 47 | return view; 48 | } 49 | 50 | - (UIImage*)generateImageWithColor:(UIColor*)color { 51 | CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); 52 | UIGraphicsBeginImageContext(rect.size); 53 | CGContextRef context = UIGraphicsGetCurrentContext(); 54 | CGContextSetFillColorWithColor(context, [color CGColor]); 55 | CGContextFillRect(context, rect); 56 | UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext(); 57 | UIGraphicsEndImageContext(); 58 | return theImage; 59 | } 60 | 61 | - (void)onImageViewTap:(UITapGestureRecognizer *)tap { 62 | self.selectedView = (UIImageView *)tap.view; 63 | 64 | PictureBroswerViewController *vc = [[PictureBroswerViewController alloc] init]; 65 | vc.image = [self.selectedView image]; 66 | vc.transitioningDelegate = self; 67 | [self presentViewController:vc animated:YES completion:nil]; 68 | } 69 | 70 | - (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { 71 | return [self generateAnimatorWithPresenting:YES]; 72 | } 73 | 74 | - (id)animationControllerForDismissedController:(UIViewController *)dismissed { 75 | return [self generateAnimatorWithPresenting:NO]; 76 | } 77 | 78 | - (PictureBroswerTransitionAnimator *)generateAnimatorWithPresenting:(BOOL)presenting { 79 | PictureBroswerTransitionAnimator *animator = [[PictureBroswerTransitionAnimator alloc] init]; 80 | animator.presenting = presenting; 81 | animator.originFrame = [self.selectedView.superview convertRect:self.selectedView.frame toView:nil]; 82 | return animator; 83 | } 84 | 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /TransitioningAnimationDemo1/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TransitioningAnimationDemo1 4 | // 5 | // Created by JiongXing on 16/9/29. 6 | // Copyright © 2016年 JiongXing. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /screenshots/TransitioningAnimationDemo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiongXing/TransitioningAnimationDemo1/b37f173a30049d1fb38d7a5f6f503aa93eb9881b/screenshots/TransitioningAnimationDemo1.gif --------------------------------------------------------------------------------