├── README.md ├── ZFChangeAnimation.gif ├── ZFChangeAnimation.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── macOne.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── macOne.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ZFChangeAnimation.xcscheme │ └── xcschememanagement.plist ├── ZFChangeAnimation ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ChangeAnimationView.h ├── ChangeAnimationView.m ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m └── origin animation.gif /README.md: -------------------------------------------------------------------------------- 1 | # ZFChangeAnimation 2 | 3 | 4 | 5 | ## Example: 6 | 7 |

8 | 9 |

10 | 11 | 12 | 具体详细介绍见http://www.jianshu.com/p/1e2b8ff3519e 13 | -------------------------------------------------------------------------------- /ZFChangeAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WZF-Fei/ZFChangeAnimation/cd6788bf0d7abc7120f204b2369e8242c31615e9/ZFChangeAnimation.gif -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DBB94A1B1C1A635A006BE60A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB94A1A1C1A635A006BE60A /* main.m */; }; 11 | DBB94A1E1C1A635A006BE60A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB94A1D1C1A635A006BE60A /* AppDelegate.m */; }; 12 | DBB94A211C1A635A006BE60A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB94A201C1A635A006BE60A /* ViewController.m */; }; 13 | DBB94A241C1A635A006BE60A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBB94A221C1A635A006BE60A /* Main.storyboard */; }; 14 | DBB94A261C1A635A006BE60A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DBB94A251C1A635A006BE60A /* Assets.xcassets */; }; 15 | DBB94A291C1A635A006BE60A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBB94A271C1A635A006BE60A /* LaunchScreen.storyboard */; }; 16 | DBB94A321C1A6393006BE60A /* ChangeAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB94A311C1A6393006BE60A /* ChangeAnimationView.m */; settings = {ASSET_TAGS = (); }; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | DBB94A161C1A635A006BE60A /* ZFChangeAnimation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZFChangeAnimation.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | DBB94A1A1C1A635A006BE60A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 22 | DBB94A1C1C1A635A006BE60A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23 | DBB94A1D1C1A635A006BE60A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 24 | DBB94A1F1C1A635A006BE60A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 25 | DBB94A201C1A635A006BE60A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 26 | DBB94A231C1A635A006BE60A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | DBB94A251C1A635A006BE60A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | DBB94A281C1A635A006BE60A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | DBB94A2A1C1A635A006BE60A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | DBB94A301C1A6393006BE60A /* ChangeAnimationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChangeAnimationView.h; sourceTree = ""; }; 31 | DBB94A311C1A6393006BE60A /* ChangeAnimationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChangeAnimationView.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | DBB94A131C1A635A006BE60A /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | DBB94A0D1C1A635A006BE60A = { 46 | isa = PBXGroup; 47 | children = ( 48 | DBB94A181C1A635A006BE60A /* ZFChangeAnimation */, 49 | DBB94A171C1A635A006BE60A /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | DBB94A171C1A635A006BE60A /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | DBB94A161C1A635A006BE60A /* ZFChangeAnimation.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | DBB94A181C1A635A006BE60A /* ZFChangeAnimation */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | DBB94A1C1C1A635A006BE60A /* AppDelegate.h */, 65 | DBB94A1D1C1A635A006BE60A /* AppDelegate.m */, 66 | DBB94A1F1C1A635A006BE60A /* ViewController.h */, 67 | DBB94A201C1A635A006BE60A /* ViewController.m */, 68 | DBB94A221C1A635A006BE60A /* Main.storyboard */, 69 | DBB94A251C1A635A006BE60A /* Assets.xcassets */, 70 | DBB94A271C1A635A006BE60A /* LaunchScreen.storyboard */, 71 | DBB94A2A1C1A635A006BE60A /* Info.plist */, 72 | DBB94A191C1A635A006BE60A /* Supporting Files */, 73 | DBB94A301C1A6393006BE60A /* ChangeAnimationView.h */, 74 | DBB94A311C1A6393006BE60A /* ChangeAnimationView.m */, 75 | ); 76 | path = ZFChangeAnimation; 77 | sourceTree = ""; 78 | }; 79 | DBB94A191C1A635A006BE60A /* Supporting Files */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | DBB94A1A1C1A635A006BE60A /* main.m */, 83 | ); 84 | name = "Supporting Files"; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | DBB94A151C1A635A006BE60A /* ZFChangeAnimation */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = DBB94A2D1C1A635A006BE60A /* Build configuration list for PBXNativeTarget "ZFChangeAnimation" */; 93 | buildPhases = ( 94 | DBB94A121C1A635A006BE60A /* Sources */, 95 | DBB94A131C1A635A006BE60A /* Frameworks */, 96 | DBB94A141C1A635A006BE60A /* Resources */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = ZFChangeAnimation; 103 | productName = ZFChangeAnimation; 104 | productReference = DBB94A161C1A635A006BE60A /* ZFChangeAnimation.app */; 105 | productType = "com.apple.product-type.application"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | DBB94A0E1C1A635A006BE60A /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastUpgradeCheck = 0700; 114 | ORGANIZATIONNAME = WZF; 115 | TargetAttributes = { 116 | DBB94A151C1A635A006BE60A = { 117 | CreatedOnToolsVersion = 7.0; 118 | }; 119 | }; 120 | }; 121 | buildConfigurationList = DBB94A111C1A635A006BE60A /* Build configuration list for PBXProject "ZFChangeAnimation" */; 122 | compatibilityVersion = "Xcode 3.2"; 123 | developmentRegion = English; 124 | hasScannedForEncodings = 0; 125 | knownRegions = ( 126 | en, 127 | Base, 128 | ); 129 | mainGroup = DBB94A0D1C1A635A006BE60A; 130 | productRefGroup = DBB94A171C1A635A006BE60A /* Products */; 131 | projectDirPath = ""; 132 | projectRoot = ""; 133 | targets = ( 134 | DBB94A151C1A635A006BE60A /* ZFChangeAnimation */, 135 | ); 136 | }; 137 | /* End PBXProject section */ 138 | 139 | /* Begin PBXResourcesBuildPhase section */ 140 | DBB94A141C1A635A006BE60A /* Resources */ = { 141 | isa = PBXResourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | DBB94A291C1A635A006BE60A /* LaunchScreen.storyboard in Resources */, 145 | DBB94A261C1A635A006BE60A /* Assets.xcassets in Resources */, 146 | DBB94A241C1A635A006BE60A /* Main.storyboard in Resources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXResourcesBuildPhase section */ 151 | 152 | /* Begin PBXSourcesBuildPhase section */ 153 | DBB94A121C1A635A006BE60A /* Sources */ = { 154 | isa = PBXSourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | DBB94A211C1A635A006BE60A /* ViewController.m in Sources */, 158 | DBB94A1E1C1A635A006BE60A /* AppDelegate.m in Sources */, 159 | DBB94A321C1A6393006BE60A /* ChangeAnimationView.m in Sources */, 160 | DBB94A1B1C1A635A006BE60A /* main.m in Sources */, 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | /* End PBXSourcesBuildPhase section */ 165 | 166 | /* Begin PBXVariantGroup section */ 167 | DBB94A221C1A635A006BE60A /* Main.storyboard */ = { 168 | isa = PBXVariantGroup; 169 | children = ( 170 | DBB94A231C1A635A006BE60A /* Base */, 171 | ); 172 | name = Main.storyboard; 173 | sourceTree = ""; 174 | }; 175 | DBB94A271C1A635A006BE60A /* LaunchScreen.storyboard */ = { 176 | isa = PBXVariantGroup; 177 | children = ( 178 | DBB94A281C1A635A006BE60A /* Base */, 179 | ); 180 | name = LaunchScreen.storyboard; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXVariantGroup section */ 184 | 185 | /* Begin XCBuildConfiguration section */ 186 | DBB94A2B1C1A635A006BE60A /* Debug */ = { 187 | isa = XCBuildConfiguration; 188 | buildSettings = { 189 | ALWAYS_SEARCH_USER_PATHS = NO; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_CONSTANT_CONVERSION = YES; 196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 197 | CLANG_WARN_EMPTY_BODY = YES; 198 | CLANG_WARN_ENUM_CONVERSION = YES; 199 | CLANG_WARN_INT_CONVERSION = YES; 200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 201 | CLANG_WARN_UNREACHABLE_CODE = YES; 202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 203 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 204 | COPY_PHASE_STRIP = NO; 205 | DEBUG_INFORMATION_FORMAT = dwarf; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | ENABLE_TESTABILITY = YES; 208 | GCC_C_LANGUAGE_STANDARD = gnu99; 209 | GCC_DYNAMIC_NO_PIC = NO; 210 | GCC_NO_COMMON_BLOCKS = YES; 211 | GCC_OPTIMIZATION_LEVEL = 0; 212 | GCC_PREPROCESSOR_DEFINITIONS = ( 213 | "DEBUG=1", 214 | "$(inherited)", 215 | ); 216 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 217 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 218 | GCC_WARN_UNDECLARED_SELECTOR = YES; 219 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 220 | GCC_WARN_UNUSED_FUNCTION = YES; 221 | GCC_WARN_UNUSED_VARIABLE = YES; 222 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 223 | MTL_ENABLE_DEBUG_INFO = YES; 224 | ONLY_ACTIVE_ARCH = YES; 225 | SDKROOT = iphoneos; 226 | TARGETED_DEVICE_FAMILY = "1,2"; 227 | }; 228 | name = Debug; 229 | }; 230 | DBB94A2C1C1A635A006BE60A /* Release */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 248 | COPY_PHASE_STRIP = NO; 249 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 250 | ENABLE_NS_ASSERTIONS = NO; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu99; 253 | GCC_NO_COMMON_BLOCKS = YES; 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 261 | MTL_ENABLE_DEBUG_INFO = NO; 262 | SDKROOT = iphoneos; 263 | TARGETED_DEVICE_FAMILY = "1,2"; 264 | VALIDATE_PRODUCT = YES; 265 | }; 266 | name = Release; 267 | }; 268 | DBB94A2E1C1A635A006BE60A /* Debug */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 272 | INFOPLIST_FILE = ZFChangeAnimation/Info.plist; 273 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 274 | PRODUCT_BUNDLE_IDENTIFIER = WZF.ZFChangeAnimation; 275 | PRODUCT_NAME = "$(TARGET_NAME)"; 276 | }; 277 | name = Debug; 278 | }; 279 | DBB94A2F1C1A635A006BE60A /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 283 | INFOPLIST_FILE = ZFChangeAnimation/Info.plist; 284 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 285 | PRODUCT_BUNDLE_IDENTIFIER = WZF.ZFChangeAnimation; 286 | PRODUCT_NAME = "$(TARGET_NAME)"; 287 | }; 288 | name = Release; 289 | }; 290 | /* End XCBuildConfiguration section */ 291 | 292 | /* Begin XCConfigurationList section */ 293 | DBB94A111C1A635A006BE60A /* Build configuration list for PBXProject "ZFChangeAnimation" */ = { 294 | isa = XCConfigurationList; 295 | buildConfigurations = ( 296 | DBB94A2B1C1A635A006BE60A /* Debug */, 297 | DBB94A2C1C1A635A006BE60A /* Release */, 298 | ); 299 | defaultConfigurationIsVisible = 0; 300 | defaultConfigurationName = Release; 301 | }; 302 | DBB94A2D1C1A635A006BE60A /* Build configuration list for PBXNativeTarget "ZFChangeAnimation" */ = { 303 | isa = XCConfigurationList; 304 | buildConfigurations = ( 305 | DBB94A2E1C1A635A006BE60A /* Debug */, 306 | DBB94A2F1C1A635A006BE60A /* Release */, 307 | ); 308 | defaultConfigurationIsVisible = 0; 309 | }; 310 | /* End XCConfigurationList section */ 311 | }; 312 | rootObject = DBB94A0E1C1A635A006BE60A /* Project object */; 313 | } 314 | -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/project.xcworkspace/xcuserdata/macOne.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WZF-Fei/ZFChangeAnimation/cd6788bf0d7abc7120f204b2369e8242c31615e9/ZFChangeAnimation.xcodeproj/project.xcworkspace/xcuserdata/macOne.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/xcuserdata/macOne.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/xcuserdata/macOne.xcuserdatad/xcschemes/ZFChangeAnimation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ZFChangeAnimation.xcodeproj/xcuserdata/macOne.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZFChangeAnimation.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DBB94A151C1A635A006BE60A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ZFChangeAnimation/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. 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 | -------------------------------------------------------------------------------- /ZFChangeAnimation/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. 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 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ZFChangeAnimation/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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ZFChangeAnimation/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 | -------------------------------------------------------------------------------- /ZFChangeAnimation/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 | -------------------------------------------------------------------------------- /ZFChangeAnimation/ChangeAnimationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChangeAnimationView.h 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChangeAnimationView : UIView 12 | 13 | 14 | -(void)startAnimation; 15 | 16 | -(void)stopAnimation; 17 | 18 | -(void)resumeAnimation; 19 | 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ZFChangeAnimation/ChangeAnimationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChangeAnimationView.m 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. All rights reserved. 7 | // 8 | 9 | #import "ChangeAnimationView.h" 10 | 11 | @interface ChangeAnimationView () 12 | 13 | @property (strong,nonatomic) CAShapeLayer *topLineLayer; 14 | 15 | @property (strong,nonatomic) CAShapeLayer *bottomLineLayer; 16 | 17 | @property (strong,nonatomic) CAShapeLayer *changedLayer; 18 | 19 | @end 20 | 21 | //small 22 | //static const CGFloat Raduis = 20; 23 | //static const CGFloat lineWidth = 20.0f; 24 | //static const CGFloat lineGapHeight = 5.0f; 25 | //static const CGFloat lineHeight = 2.0f; 26 | 27 | //big 28 | static const CGFloat Raduis = 50.0f; 29 | static const CGFloat lineWidth = 50.0f; 30 | static const CGFloat lineGapHeight = 10.0f; 31 | static const CGFloat lineHeight = 8.0f; 32 | 33 | static const CGFloat kStep1Duration = 0.5; 34 | static const CGFloat kStep2Duration = 0.5; 35 | static const CGFloat kStep3Duration = 5.0; 36 | static const CGFloat kStep4Duration = 5.0; 37 | 38 | #define kTopY Raduis - lineGapHeight 39 | #define kCenterY kTopY + lineGapHeight + lineHeight 40 | #define kBottomY kCenterY + lineGapHeight + lineHeight 41 | #define Radians(x) (M_PI * (x) / 180.0) 42 | 43 | 44 | @implementation ChangeAnimationView 45 | 46 | -(instancetype)initWithFrame:(CGRect)frame{ 47 | 48 | self = [super initWithFrame:frame]; 49 | if (self) { 50 | 51 | self.frame = frame; 52 | self.backgroundColor = [UIColor orangeColor]; 53 | 54 | } 55 | return self; 56 | } 57 | 58 | -(void)startAnimation{ 59 | 60 | [_changedLayer removeAllAnimations]; 61 | [_changedLayer removeFromSuperlayer]; 62 | [_topLineLayer removeFromSuperlayer]; 63 | [_bottomLineLayer removeFromSuperlayer]; 64 | 65 | [self initLayers]; 66 | 67 | [self animationStep1]; 68 | 69 | } 70 | 71 | -(void)resumeAnimation{ 72 | 73 | [self resumeLayer:_topLineLayer]; 74 | [self resumeLayer:_bottomLineLayer]; 75 | [self resumeLayer:_changedLayer]; 76 | } 77 | 78 | -(void)stopAnimation{ 79 | 80 | 81 | [self pauseLayer:_topLineLayer]; 82 | [self pauseLayer:_bottomLineLayer]; 83 | [self pauseLayer:_changedLayer]; 84 | } 85 | 86 | -(void)pauseLayer:(CALayer*)layer 87 | { 88 | CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; 89 | layer.speed = 0.0; 90 | layer.timeOffset = pausedTime; 91 | } 92 | 93 | -(void)resumeLayer:(CALayer*)layer 94 | { 95 | CFTimeInterval pausedTime = [layer timeOffset]; 96 | layer.speed = 1.0; 97 | layer.timeOffset = 0.0; 98 | layer.beginTime = 0.0; 99 | CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 100 | layer.beginTime = timeSincePause; 101 | } 102 | 103 | -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 104 | { 105 | 106 | if ([[anim valueForKey:@"animationName"] isEqualToString:@"animationStep1"]) { 107 | 108 | [self animationStep2]; 109 | 110 | } 111 | else if([[anim valueForKey:@"animationName"] isEqualToString:@"animationStep2"]){ 112 | [_changedLayer removeFromSuperlayer]; 113 | [self animationStep3]; 114 | } 115 | else if ([[anim valueForKey:@"animationName"] isEqualToString:@"animationStep3"]){ 116 | [self cancelAnimation]; 117 | } 118 | else if ([[anim valueForKey:@"animationName"] isEqualToString:@"animationStep4"]){ 119 | 120 | _changedLayer.affineTransform = CGAffineTransformMakeTranslation(5, 0); 121 | //平移x 122 | CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; 123 | translationAnimation.fromValue = [NSNumber numberWithFloat:0]; 124 | translationAnimation.toValue = [NSNumber numberWithFloat:5]; 125 | 126 | translationAnimation.duration = 0.5; 127 | translationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 128 | [_changedLayer addAnimation:translationAnimation forKey:nil]; 129 | } 130 | } 131 | 132 | - (void) animationStep1{ 133 | 134 | _changedLayer.strokeEnd = 0.4; 135 | CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 136 | strokeAnimation.fromValue = [NSNumber numberWithFloat:1.0f]; 137 | strokeAnimation.toValue = [NSNumber numberWithFloat:0.4f]; 138 | 139 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"]; 140 | pathAnimation.fromValue = [NSNumber numberWithFloat:0.0]; 141 | pathAnimation.toValue = [NSNumber numberWithFloat:-10]; 142 | 143 | CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 144 | animationGroup.animations = [NSArray arrayWithObjects:strokeAnimation,pathAnimation, nil]; 145 | animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; 146 | animationGroup.duration = kStep1Duration; 147 | animationGroup.delegate = self; 148 | animationGroup.removedOnCompletion = YES; 149 | [animationGroup setValue:@"animationStep1" forKey:@"animationName"]; 150 | [_changedLayer addAnimation:animationGroup forKey:nil]; 151 | } 152 | 153 | -(void)animationStep2 154 | { 155 | CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; 156 | translationAnimation.fromValue = [NSNumber numberWithFloat:-10]; 157 | //strokeEnd:0.8 剩余的距离toValue = lineWidth * (1 - 0.8); 158 | translationAnimation.toValue = [NSNumber numberWithFloat:0.2 * lineWidth]; 159 | 160 | _changedLayer.strokeEnd = 0.8; 161 | CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 162 | strokeAnimation.fromValue = [NSNumber numberWithFloat:0.4f]; 163 | strokeAnimation.toValue = [NSNumber numberWithFloat:0.8f]; 164 | 165 | CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 166 | animationGroup.animations = [NSArray arrayWithObjects:strokeAnimation,translationAnimation, nil]; 167 | animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 168 | animationGroup.duration = kStep2Duration; 169 | animationGroup.delegate = self; 170 | animationGroup.removedOnCompletion = YES; 171 | [animationGroup setValue:@"animationStep2" forKey:@"animationName"]; 172 | [_changedLayer addAnimation:animationGroup forKey:nil]; 173 | } 174 | 175 | -(void)animationStep3{ 176 | 177 | _changedLayer = [CAShapeLayer layer]; 178 | _changedLayer.fillColor = [UIColor clearColor].CGColor; 179 | _changedLayer.strokeColor = [UIColor whiteColor].CGColor; 180 | _changedLayer.contentsScale = [UIScreen mainScreen].scale; 181 | _changedLayer.lineWidth = lineHeight ; 182 | _changedLayer.lineCap = kCALineCapRound; 183 | [self.layer addSublayer:_changedLayer]; 184 | 185 | 186 | UIBezierPath *path = [UIBezierPath bezierPath]; 187 | 188 | // 画贝塞尔曲线 圆弧 189 | [path moveToPoint:CGPointMake(self.center.x + lineWidth/2.0 , kCenterY)]; 190 | 191 | //30度,经过反复测试,效果最好 192 | CGFloat angle = Radians(30); 193 | 194 | CGFloat endPointX = self.center.x + Raduis * cos(angle); 195 | CGFloat endPointY = kCenterY - Raduis * sin(angle); 196 | 197 | CGFloat startPointX = self.center.x + lineWidth/2.0; 198 | CGFloat startPointY = kCenterY; 199 | 200 | CGFloat controlPointX = self.center.x + Raduis *acos(angle); 201 | CGFloat controlPointY = kCenterY; 202 | 203 | //三点曲线 204 | [path addCurveToPoint:CGPointMake(endPointX, endPointY) 205 | controlPoint1:CGPointMake(startPointX , startPointY) 206 | controlPoint2:CGPointMake(controlPointX , controlPointY)]; 207 | 208 | //组合path 路径 209 | UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY) 210 | radius:Raduis 211 | startAngle:2 * M_PI - angle 212 | endAngle:M_PI + angle 213 | clockwise:NO]; 214 | [path appendPath:path1]; 215 | 216 | UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY) 217 | radius:Raduis 218 | startAngle:M_PI *3/2 - (M_PI_2 -angle) 219 | endAngle:-M_PI_2 - (M_PI_2 -angle) 220 | clockwise:NO]; 221 | 222 | 223 | [path appendPath:path2]; 224 | 225 | _changedLayer.path = path.CGPath; 226 | 227 | 228 | 229 | //平移量 230 | CGFloat toValue = lineWidth *(1- cos(M_PI_4)) /2.0; 231 | //finished 最终状态 232 | CGAffineTransform transform1 = CGAffineTransformMakeRotation(-M_PI_4); 233 | CGAffineTransform transform2 = CGAffineTransformMakeTranslation(-toValue, 0); 234 | CGAffineTransform transform3 = CGAffineTransformMakeRotation(M_PI_4); 235 | 236 | CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2); 237 | _topLineLayer.affineTransform = transform; 238 | transform = CGAffineTransformConcat(transform3, transform2); 239 | _bottomLineLayer.affineTransform = transform; 240 | 241 | 242 | 243 | CGFloat orignPercent = [self calculateCurveLength] / [self calculateTotalLength]; 244 | CGFloat endPercent =([self calculateCurveLength] + Radians(120) *Raduis ) / [self calculateTotalLength]; 245 | 246 | _changedLayer.strokeStart = endPercent; 247 | 248 | CAKeyframeAnimation *startAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeStart"]; 249 | startAnimation.values = @[@0.0,@(endPercent)]; 250 | 251 | CAKeyframeAnimation *EndAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"]; 252 | EndAnimation.values = @[@(orignPercent),@1.0]; 253 | 254 | 255 | CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 256 | animationGroup.animations = [NSArray arrayWithObjects:startAnimation,EndAnimation, nil]; 257 | animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 258 | animationGroup.duration = kStep3Duration; 259 | animationGroup.delegate = self; 260 | animationGroup.removedOnCompletion = YES; 261 | [animationGroup setValue:@"animationStep3" forKey:@"animationName"]; 262 | [_changedLayer addAnimation:animationGroup forKey:nil]; 263 | 264 | //平移x 265 | CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; 266 | translationAnimation.fromValue = [NSNumber numberWithFloat:0]; 267 | translationAnimation.toValue = [NSNumber numberWithFloat:-toValue]; 268 | 269 | //角度关键帧 上横线的关键帧 0 - 10° - (-55°) - (-45°) 270 | CAKeyframeAnimation *rotationAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; 271 | rotationAnimation1.values = @[[NSNumber numberWithFloat:0], 272 | [NSNumber numberWithFloat:Radians(10) ], 273 | [NSNumber numberWithFloat:Radians(-10) - M_PI_4 ], 274 | [NSNumber numberWithFloat:- M_PI_4 ] 275 | ]; 276 | 277 | 278 | CAAnimationGroup *transformGroup1 = [CAAnimationGroup animation]; 279 | transformGroup1.animations = [NSArray arrayWithObjects:rotationAnimation1,translationAnimation, nil]; 280 | transformGroup1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 281 | transformGroup1.duration = kStep3Duration; 282 | transformGroup1.removedOnCompletion = YES; 283 | [_topLineLayer addAnimation:transformGroup1 forKey:nil]; 284 | 285 | //角度关键帧 下横线的关键帧 0 - (-10°) - (55°) - (45°) 286 | CAKeyframeAnimation *rotationAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; 287 | rotationAnimation2.values = @[[NSNumber numberWithFloat:0], 288 | [NSNumber numberWithFloat:Radians(-10) ], 289 | [NSNumber numberWithFloat:Radians(10) + M_PI_4 ], 290 | [NSNumber numberWithFloat: M_PI_4 ] 291 | ]; 292 | 293 | 294 | CAAnimationGroup *transformGroup2 = [CAAnimationGroup animation]; 295 | transformGroup2.animations = [NSArray arrayWithObjects:rotationAnimation2,translationAnimation, nil]; 296 | transformGroup2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 297 | transformGroup2.duration = kStep3Duration ; 298 | transformGroup2.delegate = self; 299 | transformGroup2.removedOnCompletion = YES; 300 | [_bottomLineLayer addAnimation:transformGroup2 forKey:nil]; 301 | 302 | } 303 | 304 | -(void)cancelAnimation 305 | { 306 | //最关键是path路径 307 | 308 | UIBezierPath *path = [UIBezierPath bezierPath]; 309 | //30度,经过反复测试,效果最好 310 | CGFloat angle = Radians(30); 311 | 312 | CGFloat startPointX = self.center.x + Raduis * cos(angle); 313 | CGFloat startPointY = kCenterY - Raduis * sin(angle); 314 | 315 | CGFloat controlPointX = self.center.x + Raduis *acos(angle); 316 | CGFloat controlPointY = kCenterY; 317 | 318 | CGFloat endPointX = self.center.x + lineWidth /2; 319 | CGFloat endPointY = kCenterY; 320 | 321 | //组合path 路径 起点 -150° 顺时针的圆 322 | path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY) 323 | radius:Raduis 324 | startAngle:-M_PI + angle 325 | endAngle:M_PI + angle 326 | clockwise:YES]; 327 | 328 | //起点为 180°-> (360°-30°) 329 | UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY) 330 | radius:Raduis 331 | startAngle:M_PI + angle 332 | endAngle:2 * M_PI - angle 333 | clockwise:YES]; 334 | [path appendPath:path1]; 335 | 336 | //三点曲线 337 | UIBezierPath *path2 = [UIBezierPath bezierPath]; 338 | 339 | [path2 moveToPoint:CGPointMake(startPointX, startPointY)]; 340 | 341 | [path2 addCurveToPoint:CGPointMake(endPointX,endPointY) 342 | controlPoint1:CGPointMake(startPointX, startPointY) 343 | controlPoint2:CGPointMake(controlPointX, controlPointY)]; 344 | 345 | [path appendPath:path2]; 346 | 347 | //比原始状态向左偏移5个像素 348 | UIBezierPath *path3 = [UIBezierPath bezierPath]; 349 | [path3 moveToPoint:CGPointMake(endPointX,endPointY)]; 350 | [path3 addLineToPoint:CGPointMake(self.center.x - lineWidth/2 -5,endPointY)]; 351 | [path appendPath:path3]; 352 | 353 | _changedLayer.path = path.CGPath; 354 | 355 | //平移量 356 | CGFloat toValue = lineWidth *(1- cos(M_PI_4)) /2.0; 357 | //finished 最终状态 358 | CGAffineTransform transform1 = CGAffineTransformMakeRotation(0); 359 | CGAffineTransform transform2 = CGAffineTransformMakeTranslation(0, 0); 360 | CGAffineTransform transform3 = CGAffineTransformMakeRotation(0); 361 | 362 | CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2); 363 | _topLineLayer.affineTransform = transform; 364 | transform = CGAffineTransformConcat(transform3, transform2); 365 | _bottomLineLayer.affineTransform = transform; 366 | 367 | //一个圆的长度比 368 | CGFloat endPercent = 2* M_PI *Raduis / ([self calculateTotalLength] + lineWidth); 369 | 370 | 371 | //横线占总path的长度比 372 | CGFloat percent = lineWidth / ([self calculateTotalLength] + lineWidth); 373 | 374 | _changedLayer.strokeStart = 1.0 -percent; 375 | 376 | CAKeyframeAnimation *startAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeStart"]; 377 | startAnimation.values = @[@0.0,@0.3,@(1.0 -percent)]; 378 | 379 | //在π+ angle 380 | CAKeyframeAnimation *EndAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"]; 381 | EndAnimation.values = @[@(endPercent),@(endPercent),@1.0]; 382 | 383 | 384 | CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 385 | animationGroup.animations = [NSArray arrayWithObjects:startAnimation,EndAnimation, nil]; 386 | animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 387 | animationGroup.duration = kStep4Duration; 388 | animationGroup.delegate = self; 389 | animationGroup.removedOnCompletion = YES; 390 | [animationGroup setValue:@"animationStep4" forKey:@"animationName"]; 391 | [_changedLayer addAnimation:animationGroup forKey:nil]; 392 | 393 | //平移x 394 | CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; 395 | translationAnimation.fromValue = [NSNumber numberWithFloat:-toValue]; 396 | translationAnimation.toValue = [NSNumber numberWithFloat:0]; 397 | 398 | //角度关键帧 上横线的关键帧 (-45°) -> (-55°)-> 10° -> 0 399 | CAKeyframeAnimation *rotationAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; 400 | rotationAnimation1.values = @[[NSNumber numberWithFloat:- M_PI_4 ], 401 | [NSNumber numberWithFloat:- Radians(10) - M_PI_4 ], 402 | [NSNumber numberWithFloat:Radians(10) ], 403 | [NSNumber numberWithFloat:0] 404 | ]; 405 | 406 | 407 | CAAnimationGroup *transformGroup1 = [CAAnimationGroup animation]; 408 | transformGroup1.animations = [NSArray arrayWithObjects:rotationAnimation1,translationAnimation, nil]; 409 | transformGroup1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 410 | transformGroup1.duration = kStep4Duration; 411 | transformGroup1.removedOnCompletion = YES; 412 | [_topLineLayer addAnimation:transformGroup1 forKey:nil]; 413 | 414 | //角度关键帧 下横线的关键帧 (45°)-> (55°)- >(-10°)-> 0 415 | CAKeyframeAnimation *rotationAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; 416 | rotationAnimation2.values = @[[NSNumber numberWithFloat: M_PI_4 ], 417 | [NSNumber numberWithFloat:Radians(10) + M_PI_4 ], 418 | [NSNumber numberWithFloat:-Radians(10) ], 419 | [NSNumber numberWithFloat:0] 420 | ]; 421 | 422 | CAAnimationGroup *transformGroup2 = [CAAnimationGroup animation]; 423 | transformGroup2.animations = [NSArray arrayWithObjects:rotationAnimation2,translationAnimation, nil]; 424 | transformGroup2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 425 | transformGroup2.duration = kStep4Duration; 426 | transformGroup2.delegate = self; 427 | transformGroup2.removedOnCompletion = YES; 428 | [_bottomLineLayer addAnimation:transformGroup2 forKey:nil]; 429 | 430 | } 431 | 432 | //画上下两条横线 433 | -(void)initLayers 434 | { 435 | _topLineLayer = [CAShapeLayer layer]; 436 | _bottomLineLayer = [CAShapeLayer layer]; 437 | _changedLayer = [CAShapeLayer layer]; 438 | 439 | CALayer *Toplayer = [CALayer layer]; 440 | Toplayer.frame = CGRectMake((self.bounds.size.width + lineWidth)/2, kTopY, lineWidth, lineHeight); 441 | [self.layer addSublayer:Toplayer]; 442 | 443 | CALayer *BottomLayer = [CALayer layer]; 444 | BottomLayer.frame = CGRectMake((self.bounds.size.width + lineWidth)/2, kBottomY, lineWidth, lineHeight); 445 | [self.layer addSublayer:BottomLayer]; 446 | 447 | // CALayer *centerLayer = [CALayer layer]; 448 | // centerLayer.frame = CGRectMake((self.bounds.size.width + lineWidth)/2, kCenterY, lineWidth, lineHeight); 449 | // [self.layer addSublayer:centerLayer]; 450 | 451 | CGFloat startOriginX = self.center.x - lineWidth /2.0; 452 | CGFloat endOriginX = self.center.x + lineWidth /2.0; 453 | 454 | [_topLineLayer setStrokeColor:[[UIColor whiteColor] CGColor]]; 455 | _topLineLayer.contentsScale = [UIScreen mainScreen].scale; 456 | _topLineLayer.lineWidth = lineHeight ; 457 | _topLineLayer.lineCap = kCALineCapRound; 458 | _topLineLayer.position = CGPointMake(0,0); 459 | 460 | 461 | [_bottomLineLayer setStrokeColor:[[UIColor whiteColor] CGColor]]; 462 | _bottomLineLayer.contentsScale = [UIScreen mainScreen].scale; 463 | _bottomLineLayer.lineWidth = lineHeight ; 464 | _bottomLineLayer.lineCap = kCALineCapRound; 465 | 466 | [_changedLayer setStrokeColor:[[UIColor whiteColor] CGColor]]; 467 | _changedLayer.fillColor = [UIColor clearColor].CGColor; 468 | _changedLayer.contentsScale = [UIScreen mainScreen].scale; 469 | _changedLayer.lineWidth = lineHeight ; 470 | _changedLayer.lineCap = kCALineCapRound; 471 | 472 | UIBezierPath *path = [UIBezierPath bezierPath]; 473 | [path moveToPoint:CGPointMake(0,0)]; 474 | [path addLineToPoint:CGPointMake(-lineWidth,0)]; 475 | _topLineLayer.path = path.CGPath; 476 | 477 | CGMutablePathRef solidChangedLinePath = CGPathCreateMutable(); 478 | //被改变的layer实线 479 | CGPathMoveToPoint(solidChangedLinePath, NULL, startOriginX, kCenterY); 480 | CGPathAddLineToPoint(solidChangedLinePath, NULL, endOriginX, kCenterY); 481 | [_changedLayer setPath:solidChangedLinePath]; 482 | CGPathRelease(solidChangedLinePath); 483 | 484 | // [path moveToPoint:CGPointMake(0,0)]; 485 | // [path addLineToPoint:CGPointMake(-lineWidth,0)]; 486 | // _changedLayer.path = path.CGPath; 487 | 488 | [path moveToPoint:CGPointMake(0,0)]; 489 | [path addLineToPoint:CGPointMake(-lineWidth,0)]; 490 | _bottomLineLayer.path = path.CGPath; 491 | 492 | [Toplayer addSublayer:_topLineLayer]; 493 | [BottomLayer addSublayer:_bottomLineLayer]; 494 | [self.layer addSublayer:_changedLayer]; 495 | 496 | } 497 | 498 | -(CGFloat)calculateTotalLength 499 | { 500 | 501 | CGFloat curveLength = [self calculateCurveLength]; 502 | 503 | //一个圆 + 120度弧长的 总长度 504 | CGFloat length = (Radians(120) + 2 * M_PI) * Raduis; 505 | CGFloat totalLength = curveLength + length; 506 | 507 | return totalLength; 508 | } 509 | 510 | -(CGFloat)calculateCurveLength{ 511 | 512 | CGFloat angle = Radians(30); 513 | 514 | CGFloat endPointX = self.center.x + Raduis * cos(angle); 515 | CGFloat endPointY = kCenterY - Raduis * sin(angle); 516 | 517 | CGFloat startPointX = self.center.x + lineWidth/2.0; 518 | CGFloat startPointY = kCenterY; 519 | 520 | CGFloat controlPointX = self.center.x + Raduis *acos(angle); 521 | CGFloat controlPointY = kCenterY; 522 | 523 | CGFloat curveLength = [self bezierCurveLengthFromStartPoint:CGPointMake(startPointX, startPointY) 524 | toEndPoint:CGPointMake(endPointX,endPointY) 525 | withControlPoint:CGPointMake(controlPointX, controlPointY)]; 526 | 527 | return curveLength; 528 | } 529 | //求贝塞尔曲线长度 530 | -(CGFloat) bezierCurveLengthFromStartPoint:(CGPoint)start toEndPoint:(CGPoint) end withControlPoint:(CGPoint) control 531 | { 532 | const int kSubdivisions = 50; 533 | const float step = 1.0f/(float)kSubdivisions; 534 | 535 | float totalLength = 0.0f; 536 | CGPoint prevPoint = start; 537 | 538 | // starting from i = 1, since for i = 0 calulated point is equal to start point 539 | for (int i = 1; i <= kSubdivisions; i++) 540 | { 541 | float t = i*step; 542 | 543 | float x = (1.0 - t)*(1.0 - t)*start.x + 2.0*(1.0 - t)*t*control.x + t*t*end.x; 544 | float y = (1.0 - t)*(1.0 - t)*start.y + 2.0*(1.0 - t)*t*control.y + t*t*end.y; 545 | 546 | CGPoint diff = CGPointMake(x - prevPoint.x, y - prevPoint.y); 547 | 548 | totalLength += sqrtf(diff.x*diff.x + diff.y*diff.y); // Pythagorean 549 | 550 | prevPoint = CGPointMake(x, y); 551 | } 552 | 553 | return totalLength; 554 | } 555 | 556 | -(void)dealloc 557 | { 558 | NSLog(@"dealloc"); 559 | } 560 | @end 561 | -------------------------------------------------------------------------------- /ZFChangeAnimation/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ZFChangeAnimation/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ZFChangeAnimation/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ChangeAnimationView.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (strong , nonatomic) ChangeAnimationView *animationView; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view, typically from a nib. 23 | 24 | _animationView = [[ChangeAnimationView alloc] initWithFrame:CGRectMake(0, 150, self.view.bounds.size.width, 120)]; 25 | [self.view addSubview:_animationView]; 26 | 27 | 28 | 29 | UIButton *startButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 30 | startButton.frame = CGRectMake(10, 300, 100, 50); 31 | [startButton addTarget:self action:@selector(startAnimation:) forControlEvents:UIControlEventTouchUpInside]; 32 | [startButton setTitle:@"启动动画" forState:UIControlStateNormal]; 33 | [self.view addSubview:startButton]; 34 | 35 | UIButton *stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 36 | stopButton.frame = CGRectMake(110, 300, 100, 50); 37 | [stopButton addTarget:self action:@selector(stopAnimation:) forControlEvents:UIControlEventTouchUpInside]; 38 | [stopButton setTitle:@"暂停动画" forState:UIControlStateNormal]; 39 | [self.view addSubview:stopButton]; 40 | 41 | UIButton *resumeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 42 | resumeButton.frame = CGRectMake(210, 300, 100, 50); 43 | [resumeButton addTarget:self action:@selector(resumeAnimation:) forControlEvents:UIControlEventTouchUpInside]; 44 | [resumeButton setTitle:@"恢复动画" forState:UIControlStateNormal]; 45 | [self.view addSubview:resumeButton]; 46 | 47 | 48 | } 49 | 50 | -(void)startAnimation:(UIButton *)sender 51 | { 52 | [_animationView startAnimation]; 53 | } 54 | 55 | 56 | -(void)stopAnimation:(UIButton *)sender 57 | { 58 | [_animationView stopAnimation]; 59 | } 60 | 61 | -(void)resumeAnimation:(UIButton *)sender 62 | { 63 | [_animationView resumeAnimation]; 64 | } 65 | @end 66 | -------------------------------------------------------------------------------- /ZFChangeAnimation/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ZFChangeAnimation 4 | // 5 | // Created by macOne on 15/12/11. 6 | // Copyright © 2015年 WZF. 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 | -------------------------------------------------------------------------------- /origin animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WZF-Fei/ZFChangeAnimation/cd6788bf0d7abc7120f204b2369e8242c31615e9/origin animation.gif --------------------------------------------------------------------------------