├── ElasticSpringLoading.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── i_mt.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── i_mt.xcuserdatad │ └── xcschemes │ ├── ElasticSpringLoading.xcscheme │ └── xcschememanagement.plist ├── ElasticSpringLoading ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ElasticSpringLoading │ ├── ElasticSpringLoading.swift │ └── MtTextView.swift ├── Info.plist └── ViewController.swift ├── LICENSE ├── README.md ├── README.zh.md ├── TEMP.md └── bounceBall.gif /ElasticSpringLoading.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 529538A61E97B7040085C6F3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529538A51E97B7040085C6F3 /* AppDelegate.swift */; }; 11 | 529538A81E97B7040085C6F3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529538A71E97B7040085C6F3 /* ViewController.swift */; }; 12 | 529538AB1E97B7040085C6F3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 529538A91E97B7040085C6F3 /* Main.storyboard */; }; 13 | 529538AD1E97B7040085C6F3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 529538AC1E97B7040085C6F3 /* Assets.xcassets */; }; 14 | 529538B01E97B7040085C6F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 529538AE1E97B7040085C6F3 /* LaunchScreen.storyboard */; }; 15 | 529538BE1E98203E0085C6F3 /* ElasticSpringLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529538BC1E98203E0085C6F3 /* ElasticSpringLoading.swift */; }; 16 | 529538BF1E98203E0085C6F3 /* MtTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529538BD1E98203E0085C6F3 /* MtTextView.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 529538A21E97B7040085C6F3 /* ElasticSpringLoading.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ElasticSpringLoading.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 529538A51E97B7040085C6F3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 529538A71E97B7040085C6F3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | 529538AA1E97B7040085C6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 529538AC1E97B7040085C6F3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 529538AF1E97B7040085C6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 529538B11E97B7040085C6F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 529538BC1E98203E0085C6F3 /* ElasticSpringLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElasticSpringLoading.swift; sourceTree = ""; }; 28 | 529538BD1E98203E0085C6F3 /* MtTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MtTextView.swift; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 5295389F1E97B7040085C6F3 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 529538991E97B7040085C6F3 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 529538A41E97B7040085C6F3 /* ElasticSpringLoading */, 46 | 529538A31E97B7040085C6F3 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 529538A31E97B7040085C6F3 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 529538A21E97B7040085C6F3 /* ElasticSpringLoading.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 529538A41E97B7040085C6F3 /* ElasticSpringLoading */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 529538BB1E98203E0085C6F3 /* ElasticSpringLoading */, 62 | 529538A51E97B7040085C6F3 /* AppDelegate.swift */, 63 | 529538A71E97B7040085C6F3 /* ViewController.swift */, 64 | 529538A91E97B7040085C6F3 /* Main.storyboard */, 65 | 529538AC1E97B7040085C6F3 /* Assets.xcassets */, 66 | 529538AE1E97B7040085C6F3 /* LaunchScreen.storyboard */, 67 | 529538B11E97B7040085C6F3 /* Info.plist */, 68 | ); 69 | path = ElasticSpringLoading; 70 | sourceTree = ""; 71 | }; 72 | 529538BB1E98203E0085C6F3 /* ElasticSpringLoading */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 529538BC1E98203E0085C6F3 /* ElasticSpringLoading.swift */, 76 | 529538BD1E98203E0085C6F3 /* MtTextView.swift */, 77 | ); 78 | path = ElasticSpringLoading; 79 | sourceTree = ""; 80 | }; 81 | /* End PBXGroup section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | 529538A11E97B7040085C6F3 /* ElasticSpringLoading */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = 529538B41E97B7040085C6F3 /* Build configuration list for PBXNativeTarget "ElasticSpringLoading" */; 87 | buildPhases = ( 88 | 5295389E1E97B7040085C6F3 /* Sources */, 89 | 5295389F1E97B7040085C6F3 /* Frameworks */, 90 | 529538A01E97B7040085C6F3 /* Resources */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = ElasticSpringLoading; 97 | productName = ElasticSpringLoading; 98 | productReference = 529538A21E97B7040085C6F3 /* ElasticSpringLoading.app */; 99 | productType = "com.apple.product-type.application"; 100 | }; 101 | /* End PBXNativeTarget section */ 102 | 103 | /* Begin PBXProject section */ 104 | 5295389A1E97B7040085C6F3 /* Project object */ = { 105 | isa = PBXProject; 106 | attributes = { 107 | LastSwiftUpdateCheck = 0820; 108 | LastUpgradeCheck = 0820; 109 | ORGANIZATIONNAME = I_MT; 110 | TargetAttributes = { 111 | 529538A11E97B7040085C6F3 = { 112 | CreatedOnToolsVersion = 8.2.1; 113 | DevelopmentTeam = 8QB68XF3YG; 114 | ProvisioningStyle = Automatic; 115 | }; 116 | }; 117 | }; 118 | buildConfigurationList = 5295389D1E97B7040085C6F3 /* Build configuration list for PBXProject "ElasticSpringLoading" */; 119 | compatibilityVersion = "Xcode 3.2"; 120 | developmentRegion = English; 121 | hasScannedForEncodings = 0; 122 | knownRegions = ( 123 | en, 124 | Base, 125 | ); 126 | mainGroup = 529538991E97B7040085C6F3; 127 | productRefGroup = 529538A31E97B7040085C6F3 /* Products */; 128 | projectDirPath = ""; 129 | projectRoot = ""; 130 | targets = ( 131 | 529538A11E97B7040085C6F3 /* ElasticSpringLoading */, 132 | ); 133 | }; 134 | /* End PBXProject section */ 135 | 136 | /* Begin PBXResourcesBuildPhase section */ 137 | 529538A01E97B7040085C6F3 /* Resources */ = { 138 | isa = PBXResourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 529538B01E97B7040085C6F3 /* LaunchScreen.storyboard in Resources */, 142 | 529538AD1E97B7040085C6F3 /* Assets.xcassets in Resources */, 143 | 529538AB1E97B7040085C6F3 /* Main.storyboard in Resources */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXResourcesBuildPhase section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | 5295389E1E97B7040085C6F3 /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 529538A81E97B7040085C6F3 /* ViewController.swift in Sources */, 155 | 529538BF1E98203E0085C6F3 /* MtTextView.swift in Sources */, 156 | 529538BE1E98203E0085C6F3 /* ElasticSpringLoading.swift in Sources */, 157 | 529538A61E97B7040085C6F3 /* AppDelegate.swift in Sources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXSourcesBuildPhase section */ 162 | 163 | /* Begin PBXVariantGroup section */ 164 | 529538A91E97B7040085C6F3 /* Main.storyboard */ = { 165 | isa = PBXVariantGroup; 166 | children = ( 167 | 529538AA1E97B7040085C6F3 /* Base */, 168 | ); 169 | name = Main.storyboard; 170 | sourceTree = ""; 171 | }; 172 | 529538AE1E97B7040085C6F3 /* LaunchScreen.storyboard */ = { 173 | isa = PBXVariantGroup; 174 | children = ( 175 | 529538AF1E97B7040085C6F3 /* Base */, 176 | ); 177 | name = LaunchScreen.storyboard; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXVariantGroup section */ 181 | 182 | /* Begin XCBuildConfiguration section */ 183 | 529538B21E97B7040085C6F3 /* Debug */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_ANALYZER_NONNULL = YES; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_WARN_BOOL_CONVERSION = YES; 193 | CLANG_WARN_CONSTANT_CONVERSION = YES; 194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 195 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 196 | CLANG_WARN_EMPTY_BODY = YES; 197 | CLANG_WARN_ENUM_CONVERSION = YES; 198 | CLANG_WARN_INFINITE_RECURSION = YES; 199 | CLANG_WARN_INT_CONVERSION = YES; 200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNREACHABLE_CODE = YES; 203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 204 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 205 | COPY_PHASE_STRIP = NO; 206 | DEBUG_INFORMATION_FORMAT = dwarf; 207 | ENABLE_STRICT_OBJC_MSGSEND = YES; 208 | ENABLE_TESTABILITY = YES; 209 | GCC_C_LANGUAGE_STANDARD = gnu99; 210 | GCC_DYNAMIC_NO_PIC = NO; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_OPTIMIZATION_LEVEL = 0; 213 | GCC_PREPROCESSOR_DEFINITIONS = ( 214 | "DEBUG=1", 215 | "$(inherited)", 216 | ); 217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_UNDECLARED_SELECTOR = YES; 220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 221 | GCC_WARN_UNUSED_FUNCTION = YES; 222 | GCC_WARN_UNUSED_VARIABLE = YES; 223 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 224 | MTL_ENABLE_DEBUG_INFO = YES; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SDKROOT = iphoneos; 227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 229 | TARGETED_DEVICE_FAMILY = "1,2"; 230 | }; 231 | name = Debug; 232 | }; 233 | 529538B31E97B7040085C6F3 /* Release */ = { 234 | isa = XCBuildConfiguration; 235 | buildSettings = { 236 | ALWAYS_SEARCH_USER_PATHS = NO; 237 | CLANG_ANALYZER_NONNULL = YES; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 239 | CLANG_CXX_LIBRARY = "libc++"; 240 | CLANG_ENABLE_MODULES = YES; 241 | CLANG_ENABLE_OBJC_ARC = YES; 242 | CLANG_WARN_BOOL_CONVERSION = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 245 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 246 | CLANG_WARN_EMPTY_BODY = YES; 247 | CLANG_WARN_ENUM_CONVERSION = YES; 248 | CLANG_WARN_INFINITE_RECURSION = YES; 249 | CLANG_WARN_INT_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 252 | CLANG_WARN_UNREACHABLE_CODE = YES; 253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 254 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 255 | COPY_PHASE_STRIP = NO; 256 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 257 | ENABLE_NS_ASSERTIONS = NO; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 268 | MTL_ENABLE_DEBUG_INFO = NO; 269 | SDKROOT = iphoneos; 270 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 271 | TARGETED_DEVICE_FAMILY = "1,2"; 272 | VALIDATE_PRODUCT = YES; 273 | }; 274 | name = Release; 275 | }; 276 | 529538B51E97B7040085C6F3 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 280 | DEVELOPMENT_TEAM = 8QB68XF3YG; 281 | INFOPLIST_FILE = ElasticSpringLoading/Info.plist; 282 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 283 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 284 | PRODUCT_BUNDLE_IDENTIFIER = MT.ElasticSpringLoading; 285 | PRODUCT_NAME = "$(TARGET_NAME)"; 286 | SWIFT_VERSION = 3.0; 287 | }; 288 | name = Debug; 289 | }; 290 | 529538B61E97B7040085C6F3 /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 294 | DEVELOPMENT_TEAM = 8QB68XF3YG; 295 | INFOPLIST_FILE = ElasticSpringLoading/Info.plist; 296 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | PRODUCT_BUNDLE_IDENTIFIER = MT.ElasticSpringLoading; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | SWIFT_VERSION = 3.0; 301 | }; 302 | name = Release; 303 | }; 304 | /* End XCBuildConfiguration section */ 305 | 306 | /* Begin XCConfigurationList section */ 307 | 5295389D1E97B7040085C6F3 /* Build configuration list for PBXProject "ElasticSpringLoading" */ = { 308 | isa = XCConfigurationList; 309 | buildConfigurations = ( 310 | 529538B21E97B7040085C6F3 /* Debug */, 311 | 529538B31E97B7040085C6F3 /* Release */, 312 | ); 313 | defaultConfigurationIsVisible = 0; 314 | defaultConfigurationName = Release; 315 | }; 316 | 529538B41E97B7040085C6F3 /* Build configuration list for PBXNativeTarget "ElasticSpringLoading" */ = { 317 | isa = XCConfigurationList; 318 | buildConfigurations = ( 319 | 529538B51E97B7040085C6F3 /* Debug */, 320 | 529538B61E97B7040085C6F3 /* Release */, 321 | ); 322 | defaultConfigurationIsVisible = 0; 323 | defaultConfigurationName = Release; 324 | }; 325 | /* End XCConfigurationList section */ 326 | }; 327 | rootObject = 5295389A1E97B7040085C6F3 /* Project object */; 328 | } 329 | -------------------------------------------------------------------------------- /ElasticSpringLoading.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ElasticSpringLoading.xcodeproj/project.xcworkspace/xcuserdata/i_mt.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stitch-Taotao/ElasticSpringLoading/60335056ba08d0204753d74fa6ac28cfc5f8f3ce/ElasticSpringLoading.xcodeproj/project.xcworkspace/xcuserdata/i_mt.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ElasticSpringLoading.xcodeproj/xcuserdata/i_mt.xcuserdatad/xcschemes/ElasticSpringLoading.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 | -------------------------------------------------------------------------------- /ElasticSpringLoading.xcodeproj/xcuserdata/i_mt.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ElasticSpringLoading.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 529538A11E97B7040085C6F3 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ElasticSpringLoading/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ElasticSpringLoading 4 | // 5 | // Created by 涛涛 on 2017/4/7. 6 | // Copyright © 2017年 I_MT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ElasticSpringLoading/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 | } -------------------------------------------------------------------------------- /ElasticSpringLoading/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 | -------------------------------------------------------------------------------- /ElasticSpringLoading/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 | -------------------------------------------------------------------------------- /ElasticSpringLoading/ElasticSpringLoading/ElasticSpringLoading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ElasticSpringLoading.swift 3 | // AnimationStudy 4 | // 5 | // Created by I_MT on 2017/4/5. 6 | // Copyright © 2017年 I_MT. All rights reserved. 7 | 8 | // GitHub : https://github.com/Stitch-Taotao 9 | // 简书 : http://www.jianshu.com/u/59df4965d888 10 | 11 | import UIKit 12 | 13 | let BallDownTime = 0.35 14 | let TextBounceDuration = 0.16 15 | let BallSize = CGSize.init(width: 30, height: 30) 16 | 17 | let isDebug:Bool = false 18 | 19 | class ElasticSpringLoading: UIView ,CAAnimationDelegate{ 20 | private let textView : MtTextView! 21 | 22 | var attriString:NSAttributedString!{ 23 | didSet{ 24 | textView.attributeString = attriString 25 | } 26 | } 27 | 28 | var shapeLayers = [CAShapeLayer]() 29 | let backView = UIView() 30 | 31 | var w:CGFloat! 32 | var h:CGFloat! 33 | override init(frame: CGRect) { 34 | textView = MtTextView(frame: CGRect.init(x: 0, y: frame.size.height-40, width: frame.size.width, height: 40)) 35 | w = frame.size.width 36 | h = frame.size.height 37 | super.init(frame: frame) 38 | setupView() 39 | } 40 | required init?(coder aDecoder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | func setupBallLayers() { 44 | backView.bounds = CGRect.init(origin: CGPoint.zero, size: BallSize) 45 | backView.center = CGPoint.init(x: 1/2.0 * w, y: 20) 46 | self.addSubview(backView) 47 | 48 | let rectAngle = CAShapeLayer() 49 | let triangle = CAShapeLayer() 50 | let pentagram = CAShapeLayer() 51 | let round = CAShapeLayer() 52 | 53 | // Path 54 | rectAngle.path = rectAnglePath().cgPath 55 | triangle.path = trianglePath().cgPath 56 | pentagram.path = pentagramPath().cgPath 57 | round.path = roundPath().cgPath 58 | 59 | // Color 60 | rectAngle.fillColor = Colors.flatRed.cgColor 61 | triangle.fillColor = Colors.flatBlue.cgColor 62 | pentagram.fillColor = Colors.flatGreen.cgColor 63 | round.fillColor = Colors.flatPink.cgColor 64 | 65 | shapeLayers.append(contentsOf: [rectAngle,triangle,pentagram,round]) 66 | 67 | shapeLayers.enums { (any, index) in 68 | guard let layer = any as? CAShapeLayer else{return} 69 | layer.frame = CGRect.init(x: 0, y: 0, width: 40, height: 40) 70 | backView.layer.addSublayer(layer) 71 | } 72 | switchShapLayer() 73 | 74 | // rectAngle.backgroundColor = UIColor.red.cgColor 75 | // triangle.backgroundColor = UIColor.blue.cgColor 76 | // pentagram.backgroundColor = UIColor.yellow.cgColor 77 | 78 | } 79 | 80 | // #MARK: - PATH 81 | func roundPath() ->UIBezierPath { 82 | let ovalPath = UIBezierPath(ovalIn: CGRect.init(x: 4, y: 4, width: 32, height: 32)) 83 | ovalPath.apply(pathTransform) 84 | return ovalPath 85 | } 86 | func rectAnglePath () ->UIBezierPath { 87 | let rectanglePath = UIBezierPath(rect: CGRect.init(x: 4, y: 4, width: 32, height: 32)) 88 | rectanglePath.apply(pathTransform) 89 | return rectanglePath 90 | } 91 | func trianglePath () -> UIBezierPath { 92 | let polygonPath = UIBezierPath() 93 | polygonPath.move(to: CGPoint(x:20,y: 0)) 94 | polygonPath.addLine(to: CGPoint(x:37.32, y:30)) 95 | polygonPath.addLine(to: CGPoint(x:2.68, y:30)) 96 | polygonPath.close() 97 | polygonPath.apply(pathTransform) 98 | return polygonPath 99 | } 100 | func pentagramPath() -> UIBezierPath { 101 | let starPath = UIBezierPath() 102 | starPath.move(to: CGPoint(x:20,y: 0)) 103 | starPath.addLine(to: CGPoint(x:27.05,y: 10.29)) 104 | 105 | starPath.addLine(to: CGPoint(x:39.02, y:13.82)) 106 | starPath.addLine(to: CGPoint(x:31.41, y:23.71)) 107 | starPath.addLine(to: CGPoint(x:31.76, y:36.18)) 108 | starPath.addLine(to: CGPoint(x:20, y:32)) 109 | starPath.addLine(to: CGPoint(x:8.24,y: 36.18)) 110 | starPath.addLine(to: CGPoint(x:8.59,y: 23.71)) 111 | starPath.addLine(to: CGPoint(x:0.98, y:13.82)) 112 | starPath.addLine(to: CGPoint(x:12.95, y:10.29)) 113 | starPath.close() 114 | starPath.apply(pathTransform) 115 | return starPath 116 | } 117 | var pathTransform:CGAffineTransform{ 118 | let scaleX = BallSize.width / 40.0 119 | let scaleY = BallSize.height / 40.0 120 | return CGAffineTransform.identity.scaledBy(x: scaleX, y: scaleY) 121 | } 122 | func setUpTextView() { 123 | if isDebug { textView.backgroundColor = UIColor.gray }else{textView.backgroundColor = UIColor.clear} 124 | // textView.backgroundColor = UIColor.gray 125 | self.addSubview(textView) 126 | 127 | // textView.attributeString = attriString 128 | } 129 | func setupView() { 130 | setUpTextView() 131 | setupBallLayers() 132 | animate() 133 | } 134 | 135 | var timer :Timer? 136 | 137 | func animate() { 138 | bounceBall() 139 | self.textView.animate() 140 | DispatchQueue.global().asyncAfter(deadline: .now() + BallDownTime) { 141 | if #available(iOS 10.0, *) { 142 | self.timer = Timer.scheduledTimer(withTimeInterval: (2 * BallDownTime + TextBounceDuration), repeats: true) { [weak self] (timer) in 143 | self?.switchShapLayer() 144 | } 145 | } else { 146 | self.timer = Timer.scheduledTimer(timeInterval:(2 * BallDownTime + TextBounceDuration) , target: self, selector:#selector(self.switchShapLayer), userInfo: nil, repeats: true) 147 | } 148 | 149 | self.timer!.fire() 150 | RunLoop.current.add(self.timer!, forMode: .commonModes) 151 | RunLoop.current.run() 152 | } 153 | 154 | } 155 | 156 | func stopAnimtate() { 157 | backView.layer.removeAllAnimations() 158 | self.timer?.invalidate() 159 | textView.stopAnimate() 160 | } 161 | 162 | var currentIndex = 0 163 | 164 | func switchShapLayer(){ 165 | currentIndex = currentIndex % (shapeLayers.count == 0 ? 1 :shapeLayers.count) 166 | backView.layer.sublayers?.enums({ (layer, index) in 167 | guard let layer = layer as? CAShapeLayer else{ 168 | return 169 | } 170 | CATransaction.begin() 171 | CATransaction.setDisableActions(true) 172 | if index == currentIndex{ 173 | layer.opacity = 1 174 | }else{ 175 | layer.opacity = 0 176 | } 177 | CATransaction.commit() 178 | }) 179 | currentIndex += 1 180 | } 181 | 182 | 183 | func bounceBall() { 184 | 185 | let period = 2*BallDownTime + TextBounceDuration 186 | let keyAnimation = CAKeyframeAnimation(keyPath: "position.y") 187 | keyAnimation.values = [0,h-20 - backView.bounds.size.height / 2.0,0] 188 | keyAnimation.duration = BallDownTime * 2 189 | keyAnimation.isAdditive = true 190 | keyAnimation.timingFunctions = [CAMediaTimingFunction.init(controlPoints: 0.6, 0.08, 0.91, 0.4),CAMediaTimingFunction.init(controlPoints: 0.08, 0.6, 0.4,0.91)] 191 | keyAnimation.delegate = self 192 | let group1 = CAAnimationGroup() 193 | group1.duration = period 194 | group1.repeatCount = Float.infinity 195 | group1.animations = [keyAnimation] 196 | backView.layer.add(group1, forKey: "drop") 197 | 198 | 199 | let rotateAniamtion = CAKeyframeAnimation(keyPath: "transform.rotation.z") 200 | //时间决定一个合适的角度 201 | rotateAniamtion.values = [0, M_PI * (period / (0.9))] 202 | rotateAniamtion.duration = period 203 | rotateAniamtion.beginTime = CACurrentMediaTime() + BallDownTime 204 | rotateAniamtion.repeatCount = Float.infinity 205 | rotateAniamtion.autoreverses = true 206 | // rotateAniamtion.beginTime = 207 | backView.layer.add(rotateAniamtion, forKey: "rotate") 208 | 209 | 210 | /* 211 | let group = CAAnimationGroup() 212 | group.duration = 2 * BallDownTime + TextBounceDuration 213 | group.animations = [keyAnimation,rotateAniamtion] 214 | group.repeatCount = Float.infinity 215 | 216 | backView.layer.add(group, forKey: "group") 217 | 218 | return 219 | */ 220 | /* 221 | UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 2.0, options:.curveEaseInOut, animations: { 222 | 223 | var point = self.backView.center 224 | point.y = self.h - 20 - self.backView.bounds.size.height / 2.0 225 | self.backView.center = point 226 | 227 | let transform = self.backView.transform 228 | transform.rotated(by: CGFloat(Double(2.0 * M_PI))) 229 | self.backView.transform = transform 230 | 231 | }) { (finished) in 232 | self.textView.animate() 233 | UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 2.0, options:.curveEaseInOut, animations: { 234 | 235 | var point = self.backView.center 236 | point.y = self.backView.bounds.size.height / 2.0 237 | self.backView.center = point 238 | 239 | let transform = self.backView.transform 240 | transform.rotated(by: CGFloat(Double(2.0 * M_PI))) 241 | self.backView.transform = transform 242 | 243 | }) {(finished) in 244 | 245 | self.bounceBall() 246 | 247 | } 248 | 249 | } 250 | */ 251 | /* 252 | let moveAnimation = CABasicAnimation(keyPath: "position") 253 | moveAnimation.toValue = CGPoint.init(x: w * 1/2.0, y: h-20 - backView.bounds.size.height / 2.0) 254 | moveAnimation.duration = BallDownTime + TextBounceDuration 255 | // moveAnimation.autoreverses = true 256 | 257 | let rotateAniamtion = CABasicAnimation(keyPath: "transform.rotation.z") 258 | rotateAniamtion.fromValue = 0 259 | rotateAniamtion.toValue = -M_PI * 2 260 | rotateAniamtion.duration = 5.0 261 | rotateAniamtion.repeatCount = Float.infinity 262 | 263 | // rotateAniamtion.autoreverses = true 264 | let group = CAAnimationGroup() 265 | group.duration = BallDownTime + TextBounceDuration 266 | // group.autoreverses = true 267 | group.animations = [moveAnimation,rotateAniamtion] 268 | group.repeatCount = Float.infinity 269 | 270 | backView.layer.add(group, forKey: "group") 271 | */ 272 | } 273 | deinit { 274 | self.timer?.invalidate() 275 | } 276 | } 277 | 278 | fileprivate extension Array { 279 | func enums(_ block:(_ item:Any,_ index:Int)->()){ 280 | for (i,obj) in self.enumerated() { 281 | block(obj,i) 282 | } 283 | } 284 | } 285 | 286 | fileprivate extension UIColor { 287 | convenience init(rgb: (r: CGFloat, g: CGFloat, b: CGFloat)) { 288 | self.init(red: rgb.r/255, green: rgb.g/255, blue: rgb.b/255, alpha: 1.0) 289 | } 290 | convenience init(hsd:( H:CGFloat ,S:CGFloat,D:CGFloat)) { 291 | self.init(hue: hsd.H, saturation: hsd.S, brightness: hsd.D, alpha: 1.0) 292 | } 293 | } 294 | 295 | fileprivate struct Colors { 296 | 297 | static let flatRed = UIColor(hsd: (0.02,0.74,0.91)) 298 | static let flatBlue = UIColor(hsd: (0.62,0.50,0.63)) 299 | static let flatPink = UIColor(hsd: (0.90,0.49,0.96)) 300 | static let flatGreen = UIColor(hsd: (0.40,0.77,0.80)) 301 | } 302 | 303 | -------------------------------------------------------------------------------- /ElasticSpringLoading/ElasticSpringLoading/MtTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurvyText.swift 3 | // AnimationStudy 4 | // 5 | // Created by I_MT on 2017/4/5. 6 | // Copyright © 2017年 I_MT. All rights reserved. 7 | // 8 | // GitHub : https://github.com/Stitch-Taotao 9 | // 简书 : http://www.jianshu.com/u/59df4965d888 10 | 11 | import UIKit 12 | 13 | 14 | class MtTextView: UIView { 15 | 16 | 17 | var attributeString :NSAttributedString { 18 | set{ 19 | self.textStorage.setAttributedString(newValue) 20 | self.caculatePoints() 21 | // setNeedsDisplay() //心急 22 | } 23 | get{ 24 | return self.textStorage 25 | } 26 | } 27 | 28 | let layoutManager = NSLayoutManager() 29 | let textStorage = NSTextStorage() 30 | let textContainer = NSTextContainer() 31 | 32 | 33 | let startView = UIView() 34 | let endView = UIView() 35 | let controlView = UIView() 36 | 37 | var p0 = CGPoint() 38 | var p1 = CGPoint() 39 | var p2 = CGPoint() 40 | 41 | 42 | var middleTextPoint = CGPoint() 43 | 44 | override init(frame: CGRect) { 45 | super.init(frame: frame) 46 | startView.bounds = CGRect.init(x: 0, y: 0, width: 5, height: 5) 47 | endView.bounds = CGRect.init(x: 0, y: 0, width: 5, height: 5) 48 | controlView.bounds = CGRect.init(x: 0, y: 0, width: 5, height: 5) 49 | self.addSubview(startView) 50 | self.addSubview(endView) 51 | self.addSubview(controlView) 52 | if isDebug { 53 | startView.backgroundColor = UIColor.red 54 | controlView.backgroundColor = UIColor.yellow 55 | endView.backgroundColor = UIColor.green 56 | } 57 | textContainer.lineFragmentPadding = 0.0 58 | textStorage.addLayoutManager(layoutManager) 59 | layoutManager.addTextContainer(textContainer) 60 | caculatePoints() 61 | } 62 | 63 | required init?(coder aDecoder: NSCoder) { 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | 67 | func caculatePoints() { 68 | let suggestSize = sizeForText(mutableStr: attributeString) 69 | //高度设置为40 距离底部15 70 | startView.center.x = bounds.size.width/2.0-suggestSize.width/2.0 71 | startView.center.y = bounds.size.height - 15 72 | 73 | 74 | endView.center.x = bounds.size.width/2.0 + suggestSize.width/2.0 75 | endView.center.y = bounds.size.height - 15 76 | 77 | controlView.center = CGPoint.init(x: bounds.size.width/2.0, y: bounds.size.height - 15) 78 | p0 = startView.center 79 | p1 = controlView.center 80 | p2 = endView.center 81 | updatePoints() 82 | } 83 | 84 | func sizeForText(mutableStr:NSAttributedString) -> CGSize { 85 | //创建CTFramesetterRef实例 86 | let frameSetter = CTFramesetterCreateWithAttributedString(mutableStr) 87 | 88 | //获得要绘制区域的高度 89 | //10000是有参考的: http://stackoverflow.com/questions/3374591/ctframesettersuggestframesizewithconstraints-sometimes-returns-incorrect-size 90 | let restricSize = CGSize.init(width:bounds.size.width, height:10000) 91 | let coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter,CFRangeMake(0, 0), nil, restricSize, nil) 92 | return coreTextSize 93 | } 94 | 95 | func updatePoints() { 96 | if let layer1 = startView.layer.presentation() {p0 = layer1.position} 97 | if let layer2 = controlView.layer.presentation() { p1 = layer2.position} 98 | if let layer3 = endView.layer.presentation(){p2 = layer3.position} 99 | 100 | } 101 | lazy var displayLink:CADisplayLink = { 102 | let link = CADisplayLink(target:self, selector: #selector(displayAction)) 103 | link.add(to: .main, forMode:.commonModes) 104 | return link 105 | }() 106 | func displayAction() { 107 | let oldPoint = p1; 108 | updatePoints() 109 | if oldPoint.y != p1.y { 110 | setNeedsDisplay() 111 | } 112 | } 113 | func startLink(){ 114 | displayLink.isPaused = false 115 | } 116 | func stopLink() { 117 | displayLink.isPaused = true 118 | } 119 | deinit { 120 | displayLink.invalidate() 121 | } 122 | func animate() { 123 | startLink() 124 | 125 | let moveAniamtion = CAKeyframeAnimation(keyPath: "position.y") 126 | moveAniamtion.values = [0,15,0,-10,0]; 127 | moveAniamtion.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 128 | moveAniamtion.isAdditive = true 129 | moveAniamtion.duration = TextBounceDuration 130 | moveAniamtion.beginTime = BallDownTime * 0.85 131 | // moveAniamtion.repeatCount = Float.greatestFiniteMagnitude 132 | 133 | let groupAnimation = CAAnimationGroup() 134 | groupAnimation.duration = 2 * BallDownTime + TextBounceDuration 135 | groupAnimation.animations = [moveAniamtion] 136 | groupAnimation.repeatCount = Float.infinity 137 | controlView.layer.add(groupAnimation, forKey: "move") 138 | } 139 | 140 | func stopAnimate() { 141 | controlView.layer.removeAllAnimations() 142 | setNeedsDisplay() 143 | displayLink.invalidate() 144 | } 145 | 146 | } 147 | 148 | 149 | extension MtTextView{ 150 | override func draw(_ rect: CGRect) { 151 | super.draw(rect) 152 | if isDebug { drawPath()} 153 | drawText() 154 | } 155 | 156 | func drawPath() { 157 | let path = UIBezierPath() 158 | path.move(to: p0) 159 | path.addQuadCurve(to: p2, controlPoint: p1) 160 | UIColor.blue.setStroke() 161 | path.stroke() 162 | } 163 | 164 | func drawText() { 165 | if self.attributeString.length == 0 {return} 166 | 167 | let context = UIGraphicsGetCurrentContext() 168 | var glyphRange = NSRange() 169 | let lineRect = layoutManager.lineFragmentRect(forGlyphAt: 0, effectiveRange: &glyphRange) 170 | 171 | var offset:CGFloat = 0 172 | var lastGlyphPoint = startView.center 173 | var lastX:CGFloat = 0 174 | 175 | for glyphIndex in glyphRange.location..CGFloat{ 198 | let loop1 = (1.0 - t) * (1.0-t) * p0 199 | let loop2 = 2 * (1.0 - t) * t * p1 200 | let loop3 = t * t * p2 201 | return loop1+loop2+loop3 202 | } 203 | class func BezierPrime(startP p0:CGFloat,control p1:CGFloat,endP p2:CGFloat,offset t:CGFloat)->CGFloat{ 204 | let loop1 = (2 * t - 2) * p0 205 | let loop2 = 2.0 * (1.0 - 2.0 * t) * p1 206 | let loop3 = 2.0 * t * p2 207 | return loop1+loop2+loop3 208 | } 209 | class func distance(p1:CGPoint,p2:CGPoint)->CGFloat{ 210 | let dx = p1.x - p2.x 211 | let dy = p1.y - p2.y 212 | return hypot(dx,dy) 213 | } 214 | 215 | func point(forOffset offset:CGFloat) -> CGPoint { 216 | let x = MtTextView.Bezier(startP:p0.x, control:p1.x, endP: p2.x, offset: offset) 217 | let y = MtTextView.Bezier(startP:p0.y, control:p1.y, endP: p2.y, offset: offset) 218 | return CGPoint.init(x: x, y: y) 219 | } 220 | func angle(forOffset offset:CGFloat)-> CGFloat{ 221 | let dx = MtTextView.BezierPrime(startP:p0.x, control:p1.x, endP:p2.x, offset: offset) 222 | let dy = MtTextView.BezierPrime(startP:p0.y, control:p1.y, endP: p2.y, offset: offset) 223 | return atan2(dy, dx) 224 | } 225 | 226 | func offset(atDistance distance:CGFloat ,fromPoint:CGPoint ,offset:CGFloat) -> CGFloat { 227 | //这不是个很好的做法,不过性能在本例中还说得过去 228 | let kStep:CGFloat = 0.001 229 | var newDistance :CGFloat = 0.0 230 | var newOffset = offset + kStep 231 | 232 | while newDistance <= distance && newOffset < 1.0{ 233 | newOffset += kStep 234 | newDistance = MtTextView.distance(p1: fromPoint, p2: self.point(forOffset: newOffset)) 235 | } 236 | return newOffset 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /ElasticSpringLoading/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 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ElasticSpringLoading/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ElasticSpringLoading 4 | // 5 | // Created by 涛涛 on 2017/4/7. 6 | // Copyright © 2017年 I_MT. All rights reserved. 7 | // GitHub : https://github.com/Stitch-Taotao 8 | // 简书 : http://www.jianshu.com/u/59df4965d888 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | let strings = ["我简直信了","Oh,My God!","You can display any text.","Please wait..."] 13 | var animateView :ElasticSpringLoading! 14 | override func viewDidLoad() { 15 | self.view.backgroundColor = UIColor.white 16 | animateView = ElasticSpringLoading(frame: CGRect.init(x: 0, y: 150, width: view.bounds.size.width, height: 100))//You can display text 17 | let attriString = NSMutableAttributedString(string: "我简直信了邪了!!!") 18 | attriString.addAttributes([NSFontAttributeName:UIFont.systemFont(ofSize: 16.0)], range:NSRange.init(location: 0, length: 3)) 19 | attriString.addAttributes([NSForegroundColorAttributeName:UIColor.blue], range: NSRange.init(location: 1, length: 3)) 20 | animateView.attriString = attriString 21 | view.addSubview(animateView) 22 | 23 | Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) 24 | 25 | DispatchQueue.main.asyncAfter(deadline:.now() + 10) { 26 | // self.animateView.stopAnimtate() 27 | } 28 | } 29 | 30 | func timerAction() { 31 | 32 | let index = Int(arc4random()%(UInt32((self.strings.count)))) 33 | let string = strings[index] 34 | let attriString = NSMutableAttributedString(string:string) 35 | self.animateView.attriString = attriString 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 涛涛 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ElasticSpringLoading 2 | --- 3 | 4 | 5 | ElasticSpringLoading is a sample simulated text spring effect . Inspired by [不烂漫的罪名](http://m.zcool.com.cn/work/ZMTI1Nzk3MjA=.html) 6 | 7 | #### ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: 8 | 9 | ##### [中文说明](README.zh.md) 10 | 11 | 12 | ![Screenshots](bounceBall.gif) 13 | 14 | 15 | ## Requirements 16 | 17 | swift3.0,iOS 8.0+ 18 | 19 | ## Usage 20 | 21 | ``` 22 | let animateView = ElasticSpringLoading(frame: CGRect.init(x: 0, y: 100, width: view.bounds.size.width, height: 100))//You can display text 23 | let attriString = NSMutableAttributedString(string: "Your Text")//Your text should not wrap,just support one line 24 | animateView.attriString = attriString 25 | 26 | self.view.addSubview(animateView) 27 | 28 | 29 | ``` 30 | 31 | You can change text at any time in main thread ,just do this : 32 | 33 | ``` 34 | animateView.attriString = newAttriString 35 | 36 | ``` 37 | 38 | ## Install 39 | 40 | **Manually** 41 | 42 | 1. Download the latest code version 43 | 2. Open your project in Xcode,drag the `ElasticSpringLoading` folder into your project. Make sure to select Copy items when asked if you extracted the code archive outside of your project. 44 | 3. Build your project 45 | 46 | 47 | ## Contributing 48 | 49 | Contributors are more than welcome.Pull your request 50 | 51 | If you have some suggestion or find my mistake, hope you point out 52 | 53 | 54 | ## Contacts 55 | 56 | #### If you wish to contact me, email at: xyfqldy@163.com 57 | 58 | #### Blog : [jianshu](http://www.jianshu.com/users/59df4965d888) 59 | 60 | 61 | ## License 62 | 63 | ElasticSpringLoading is released under the [MIT license](LICENSE). See LICENSE for details. 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | ElasticSpringLoading 2 | --- 3 | 4 | ElasticSpringLoading 是一个简单的模拟文字弹簧效果。灵感来自于:[不烂漫的罪名](http://m.zcool.com.cn/work/ZMTI1Nzk3MjA=.html) 5 | 6 | #### [English illustrate](README.md) 7 | 8 | 9 | ![效果图](bounceBall.gif) 10 | 11 | 12 | ## 要求 13 | 14 | 15 | swift3.0,iOS 8.0+ 16 | 17 | 18 | ## 用法 19 | 20 | ``` 21 | let animateView = ElasticSpringLoading(frame: CGRect.init(x: 0, y: 100, width: view.bounds.size.width, height: 100))//You can display text 22 | let attriString = NSMutableAttributedString(string: "Your Text")//Your text should not wrap,just support one line 23 | animateView.attriString = attriString 24 | 25 | self.view.addSubview(animateView) 26 | 27 | ``` 28 | 29 | 你可以在主线程的任何时候改变文字,只需要这样: 30 | 31 | ``` 32 | animateView.attriString = newAttriString 33 | 34 | ``` 35 | 36 | ## 安装 37 | 38 | **目前只支持手动安装233...** 39 | 40 | 1. 下载最新版本的代码 41 | 2. 打开你的工程,把`ElasticSpringLoading`文件夹拖进你的工程。确保你选择Copy item 42 | 3. 重新编译项目 43 | 44 | 45 | ## 参与 46 | 47 | 希望大家多多提些意见啊,项目有什么问题的话请一定要告诉我呀,希望多多指出我的问题,感激不尽。 48 | 另外我的实现方式有几个地方我也知道不足,如果你们有好的修改方案的话一定要Pull request啊,很期待你的参与,再次感激。 49 | 50 | ## 有事儿的话联系我 51 | 52 | 可以关注下[我的简书](http://www.jianshu.com/users/59df4965d888),我发布过的文章有问题的话,评论指出哈,这非常有意义。 53 | 54 | ## 许可 55 | 56 | [MIT license](LICENSE) 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /TEMP.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/JHUD.svg?style=flat)](http://cocoapods.org/pods/JHUD) 4 | [![License](https://img.shields.io/cocoapods/l/JHUD.svg?style=flat)](http://cocoapods.org/pods/JHUD) 5 | [![Platform](https://img.shields.io/cocoapods/p/JHUD.svg?style=flat)](http://cocoapods.org/pods/JHUD) 6 | [![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  7 | ![Language](https://img.shields.io/badge/Language-%20Objective%20C%20-blue.svg) 8 | [![Weibo](https://img.shields.io/badge/Sina微博-@晋先森-yellow.svg?style=flat)](http://weibo.com/3205872327) 9 | [![GitHub stars](https://img.shields.io/github/stars/jinxiansen/JHUD.svg)](https://github.com/jinxiansen/JHUD/stargazers) 10 | 11 | #### `JHUD` is a full screen of the HUD when loading the data (Objective-C) . 12 | 13 | #### ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: 14 | ##### [中文说明](README.zh.md) / [简书介绍](http://www.jianshu.com/p/fc07f027680c) 15 | 16 | 17 | ## screenshots 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ## Requirements 28 | 29 | `JHUD` works on "Xcode 7.3 , iOS 6+ and requires ARC to build. 30 | You will need the latest developer tools in order to build `JHUD`. Old Xcode versions might work, but compatibility will not be explicitly maintained. 31 | 32 | 33 | ## CocoaPods 34 | 35 | CocoaPods is the recommended way to add JHUD to your project. 36 | 37 | Add a pod entry for JHUD to your Podfile. 38 | 39 | ``` 40 | pod 'JHUD' 41 | ``` 42 | Second, install JHUD into your project: 43 | 44 | ``` 45 | pod install 46 | ``` 47 | Include JHUD wherever you need it with `#import "JHUD.h"`. 48 | 49 | ## Manually 50 | 51 | 1. Download the latest code version . 52 | 2. Open your project in Xcode,drag the `JHUD` folder into your project. Make sure to select Copy items when asked if you extracted the code archive outside of your project. 53 | 3. You need it with `#import "JHUD.h"`. 54 | 55 | 56 | ## Usage 57 | 58 | ``` 59 | hudView = [[JHUD alloc]initWithFrame:self.view.bounds]; 60 | 61 | hudView.messageLabel.text = @"hello ,this is a circle animation"; 62 | 63 | //show 64 | [hudView showAtView:self.view hudType:JHUDLoadingTypeCircle]; 65 | 66 | //hide 67 | [hudView hide]; 68 | ``` 69 | 70 | Class method : 71 | 72 | ``` 73 | [JHUD showAtView:self.view message:@"Hello, this is a message"]; 74 | 75 | [JHUD hide]; 76 | ``` 77 | 78 | For more examples, including how to use JHUD , take a look at the bundled demo project. API documentation is provided in the header file (JHUD.h). 79 | 80 | ## Contacts ![](gif/zz.jpg) 81 | 82 | #### If you wish to contact me, email at: hi@jinxiansen.com 83 | 84 | ##### Sina : [@晋先森](http://weibo.com/3205872327) 85 | ##### Twitter : [@jinxiansen](https://twitter.com/jinxiansen) 86 | 87 | ## License 88 | 89 | JHUD is released under the [MIT license](LICENSE). See LICENSE for details. 90 | -------------------------------------------------------------------------------- /bounceBall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stitch-Taotao/ElasticSpringLoading/60335056ba08d0204753d74fa6ac28cfc5f8f3ce/bounceBall.gif --------------------------------------------------------------------------------