├── LabelAnimationDemo ├── LabelAnimationDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── jerryliu.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── jerryliu.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── LabelAnimationDemo.xcscheme │ │ └── xcschememanagement.plist ├── LabelAnimationDemo │ ├── AnimationLabelView.h │ ├── AnimationLabelView.m │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── LabelAnimationHelper.h │ ├── LabelAnimationHelper.m │ ├── UIView+Frame.h │ ├── UIView+Frame.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LabelAnimationDemoTests │ ├── Info.plist │ └── LabelAnimationDemoTests.m └── LabelAnimationDemoUITests │ ├── Info.plist │ └── LabelAnimationDemoUITests.m ├── README.md └── demoAnimation.gif /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 70BF167F1EDC1B22006C385E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF167E1EDC1B22006C385E /* main.m */; }; 11 | 70BF16821EDC1B22006C385E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16811EDC1B22006C385E /* AppDelegate.m */; }; 12 | 70BF16851EDC1B22006C385E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16841EDC1B22006C385E /* ViewController.m */; }; 13 | 70BF16881EDC1B22006C385E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70BF16861EDC1B22006C385E /* Main.storyboard */; }; 14 | 70BF168A1EDC1B22006C385E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70BF16891EDC1B22006C385E /* Assets.xcassets */; }; 15 | 70BF168D1EDC1B22006C385E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70BF168B1EDC1B22006C385E /* LaunchScreen.storyboard */; }; 16 | 70BF16981EDC1B22006C385E /* LabelAnimationDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16971EDC1B22006C385E /* LabelAnimationDemoTests.m */; }; 17 | 70BF16A31EDC1B22006C385E /* LabelAnimationDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16A21EDC1B22006C385E /* LabelAnimationDemoUITests.m */; }; 18 | 70BF16B41EDC1B63006C385E /* LabelAnimationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16B31EDC1B63006C385E /* LabelAnimationHelper.m */; }; 19 | 70BF16BA1EDC2041006C385E /* AnimationLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16B91EDC2041006C385E /* AnimationLabelView.m */; }; 20 | 70BF16BD1EDC213B006C385E /* UIView+Frame.m in Sources */ = {isa = PBXBuildFile; fileRef = 70BF16BC1EDC213B006C385E /* UIView+Frame.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 70BF16941EDC1B22006C385E /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 70BF16721EDC1B22006C385E /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 70BF16791EDC1B22006C385E; 29 | remoteInfo = LabelAnimationDemo; 30 | }; 31 | 70BF169F1EDC1B22006C385E /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 70BF16721EDC1B22006C385E /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 70BF16791EDC1B22006C385E; 36 | remoteInfo = LabelAnimationDemo; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 70BF167A1EDC1B22006C385E /* LabelAnimationDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LabelAnimationDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 70BF167E1EDC1B22006C385E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 43 | 70BF16801EDC1B22006C385E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 44 | 70BF16811EDC1B22006C385E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 45 | 70BF16831EDC1B22006C385E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | 70BF16841EDC1B22006C385E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | 70BF16871EDC1B22006C385E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 70BF16891EDC1B22006C385E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 70BF168C1EDC1B22006C385E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 70BF168E1EDC1B22006C385E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 70BF16931EDC1B22006C385E /* LabelAnimationDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LabelAnimationDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 70BF16971EDC1B22006C385E /* LabelAnimationDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LabelAnimationDemoTests.m; sourceTree = ""; }; 53 | 70BF16991EDC1B22006C385E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 70BF169E1EDC1B22006C385E /* LabelAnimationDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LabelAnimationDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 70BF16A21EDC1B22006C385E /* LabelAnimationDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LabelAnimationDemoUITests.m; sourceTree = ""; }; 56 | 70BF16A41EDC1B22006C385E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 70BF16B21EDC1B63006C385E /* LabelAnimationHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelAnimationHelper.h; sourceTree = ""; }; 58 | 70BF16B31EDC1B63006C385E /* LabelAnimationHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabelAnimationHelper.m; sourceTree = ""; }; 59 | 70BF16B81EDC2041006C385E /* AnimationLabelView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimationLabelView.h; sourceTree = ""; }; 60 | 70BF16B91EDC2041006C385E /* AnimationLabelView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnimationLabelView.m; sourceTree = ""; }; 61 | 70BF16BB1EDC213B006C385E /* UIView+Frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Frame.h"; sourceTree = ""; }; 62 | 70BF16BC1EDC213B006C385E /* UIView+Frame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Frame.m"; sourceTree = ""; }; 63 | /* End PBXFileReference section */ 64 | 65 | /* Begin PBXFrameworksBuildPhase section */ 66 | 70BF16771EDC1B22006C385E /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 70BF16901EDC1B22006C385E /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 70BF169B1EDC1B22006C385E /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 70BF16711EDC1B22006C385E = { 91 | isa = PBXGroup; 92 | children = ( 93 | 70BF167C1EDC1B22006C385E /* LabelAnimationDemo */, 94 | 70BF16961EDC1B22006C385E /* LabelAnimationDemoTests */, 95 | 70BF16A11EDC1B22006C385E /* LabelAnimationDemoUITests */, 96 | 70BF167B1EDC1B22006C385E /* Products */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 70BF167B1EDC1B22006C385E /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 70BF167A1EDC1B22006C385E /* LabelAnimationDemo.app */, 104 | 70BF16931EDC1B22006C385E /* LabelAnimationDemoTests.xctest */, 105 | 70BF169E1EDC1B22006C385E /* LabelAnimationDemoUITests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 70BF167C1EDC1B22006C385E /* LabelAnimationDemo */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 70BF16801EDC1B22006C385E /* AppDelegate.h */, 114 | 70BF16811EDC1B22006C385E /* AppDelegate.m */, 115 | 70BF16B71EDC2029006C385E /* View */, 116 | 70BF16B11EDC1B4A006C385E /* Controller */, 117 | 70BF16B01EDC1B3A006C385E /* Manager */, 118 | 70BF16861EDC1B22006C385E /* Main.storyboard */, 119 | 70BF16891EDC1B22006C385E /* Assets.xcassets */, 120 | 70BF168B1EDC1B22006C385E /* LaunchScreen.storyboard */, 121 | 70BF168E1EDC1B22006C385E /* Info.plist */, 122 | 70BF167D1EDC1B22006C385E /* Supporting Files */, 123 | ); 124 | path = LabelAnimationDemo; 125 | sourceTree = ""; 126 | }; 127 | 70BF167D1EDC1B22006C385E /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 70BF167E1EDC1B22006C385E /* main.m */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 70BF16961EDC1B22006C385E /* LabelAnimationDemoTests */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 70BF16971EDC1B22006C385E /* LabelAnimationDemoTests.m */, 139 | 70BF16991EDC1B22006C385E /* Info.plist */, 140 | ); 141 | path = LabelAnimationDemoTests; 142 | sourceTree = ""; 143 | }; 144 | 70BF16A11EDC1B22006C385E /* LabelAnimationDemoUITests */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 70BF16A21EDC1B22006C385E /* LabelAnimationDemoUITests.m */, 148 | 70BF16A41EDC1B22006C385E /* Info.plist */, 149 | ); 150 | path = LabelAnimationDemoUITests; 151 | sourceTree = ""; 152 | }; 153 | 70BF16B01EDC1B3A006C385E /* Manager */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 70BF16B21EDC1B63006C385E /* LabelAnimationHelper.h */, 157 | 70BF16B31EDC1B63006C385E /* LabelAnimationHelper.m */, 158 | ); 159 | name = Manager; 160 | sourceTree = ""; 161 | }; 162 | 70BF16B11EDC1B4A006C385E /* Controller */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 70BF16831EDC1B22006C385E /* ViewController.h */, 166 | 70BF16841EDC1B22006C385E /* ViewController.m */, 167 | ); 168 | name = Controller; 169 | sourceTree = ""; 170 | }; 171 | 70BF16B71EDC2029006C385E /* View */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 70BF16BB1EDC213B006C385E /* UIView+Frame.h */, 175 | 70BF16BC1EDC213B006C385E /* UIView+Frame.m */, 176 | 70BF16B81EDC2041006C385E /* AnimationLabelView.h */, 177 | 70BF16B91EDC2041006C385E /* AnimationLabelView.m */, 178 | ); 179 | name = View; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXNativeTarget section */ 185 | 70BF16791EDC1B22006C385E /* LabelAnimationDemo */ = { 186 | isa = PBXNativeTarget; 187 | buildConfigurationList = 70BF16A71EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemo" */; 188 | buildPhases = ( 189 | 70BF16761EDC1B22006C385E /* Sources */, 190 | 70BF16771EDC1B22006C385E /* Frameworks */, 191 | 70BF16781EDC1B22006C385E /* Resources */, 192 | ); 193 | buildRules = ( 194 | ); 195 | dependencies = ( 196 | ); 197 | name = LabelAnimationDemo; 198 | productName = LabelAnimationDemo; 199 | productReference = 70BF167A1EDC1B22006C385E /* LabelAnimationDemo.app */; 200 | productType = "com.apple.product-type.application"; 201 | }; 202 | 70BF16921EDC1B22006C385E /* LabelAnimationDemoTests */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = 70BF16AA1EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemoTests" */; 205 | buildPhases = ( 206 | 70BF168F1EDC1B22006C385E /* Sources */, 207 | 70BF16901EDC1B22006C385E /* Frameworks */, 208 | 70BF16911EDC1B22006C385E /* Resources */, 209 | ); 210 | buildRules = ( 211 | ); 212 | dependencies = ( 213 | 70BF16951EDC1B22006C385E /* PBXTargetDependency */, 214 | ); 215 | name = LabelAnimationDemoTests; 216 | productName = LabelAnimationDemoTests; 217 | productReference = 70BF16931EDC1B22006C385E /* LabelAnimationDemoTests.xctest */; 218 | productType = "com.apple.product-type.bundle.unit-test"; 219 | }; 220 | 70BF169D1EDC1B22006C385E /* LabelAnimationDemoUITests */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 70BF16AD1EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemoUITests" */; 223 | buildPhases = ( 224 | 70BF169A1EDC1B22006C385E /* Sources */, 225 | 70BF169B1EDC1B22006C385E /* Frameworks */, 226 | 70BF169C1EDC1B22006C385E /* Resources */, 227 | ); 228 | buildRules = ( 229 | ); 230 | dependencies = ( 231 | 70BF16A01EDC1B22006C385E /* PBXTargetDependency */, 232 | ); 233 | name = LabelAnimationDemoUITests; 234 | productName = LabelAnimationDemoUITests; 235 | productReference = 70BF169E1EDC1B22006C385E /* LabelAnimationDemoUITests.xctest */; 236 | productType = "com.apple.product-type.bundle.ui-testing"; 237 | }; 238 | /* End PBXNativeTarget section */ 239 | 240 | /* Begin PBXProject section */ 241 | 70BF16721EDC1B22006C385E /* Project object */ = { 242 | isa = PBXProject; 243 | attributes = { 244 | LastUpgradeCheck = 0830; 245 | ORGANIZATIONNAME = JerryLiu; 246 | TargetAttributes = { 247 | 70BF16791EDC1B22006C385E = { 248 | CreatedOnToolsVersion = 8.3.2; 249 | DevelopmentTeam = W755NUT7GJ; 250 | ProvisioningStyle = Automatic; 251 | }; 252 | 70BF16921EDC1B22006C385E = { 253 | CreatedOnToolsVersion = 8.3.2; 254 | DevelopmentTeam = W755NUT7GJ; 255 | ProvisioningStyle = Automatic; 256 | TestTargetID = 70BF16791EDC1B22006C385E; 257 | }; 258 | 70BF169D1EDC1B22006C385E = { 259 | CreatedOnToolsVersion = 8.3.2; 260 | DevelopmentTeam = W755NUT7GJ; 261 | ProvisioningStyle = Automatic; 262 | TestTargetID = 70BF16791EDC1B22006C385E; 263 | }; 264 | }; 265 | }; 266 | buildConfigurationList = 70BF16751EDC1B22006C385E /* Build configuration list for PBXProject "LabelAnimationDemo" */; 267 | compatibilityVersion = "Xcode 3.2"; 268 | developmentRegion = English; 269 | hasScannedForEncodings = 0; 270 | knownRegions = ( 271 | en, 272 | Base, 273 | ); 274 | mainGroup = 70BF16711EDC1B22006C385E; 275 | productRefGroup = 70BF167B1EDC1B22006C385E /* Products */; 276 | projectDirPath = ""; 277 | projectRoot = ""; 278 | targets = ( 279 | 70BF16791EDC1B22006C385E /* LabelAnimationDemo */, 280 | 70BF16921EDC1B22006C385E /* LabelAnimationDemoTests */, 281 | 70BF169D1EDC1B22006C385E /* LabelAnimationDemoUITests */, 282 | ); 283 | }; 284 | /* End PBXProject section */ 285 | 286 | /* Begin PBXResourcesBuildPhase section */ 287 | 70BF16781EDC1B22006C385E /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | 70BF168D1EDC1B22006C385E /* LaunchScreen.storyboard in Resources */, 292 | 70BF168A1EDC1B22006C385E /* Assets.xcassets in Resources */, 293 | 70BF16881EDC1B22006C385E /* Main.storyboard in Resources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | 70BF16911EDC1B22006C385E /* Resources */ = { 298 | isa = PBXResourcesBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | 70BF169C1EDC1B22006C385E /* Resources */ = { 305 | isa = PBXResourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | /* End PBXResourcesBuildPhase section */ 312 | 313 | /* Begin PBXSourcesBuildPhase section */ 314 | 70BF16761EDC1B22006C385E /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 70BF16BD1EDC213B006C385E /* UIView+Frame.m in Sources */, 319 | 70BF16851EDC1B22006C385E /* ViewController.m in Sources */, 320 | 70BF16B41EDC1B63006C385E /* LabelAnimationHelper.m in Sources */, 321 | 70BF16821EDC1B22006C385E /* AppDelegate.m in Sources */, 322 | 70BF16BA1EDC2041006C385E /* AnimationLabelView.m in Sources */, 323 | 70BF167F1EDC1B22006C385E /* main.m in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | 70BF168F1EDC1B22006C385E /* Sources */ = { 328 | isa = PBXSourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | 70BF16981EDC1B22006C385E /* LabelAnimationDemoTests.m in Sources */, 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | 70BF169A1EDC1B22006C385E /* Sources */ = { 336 | isa = PBXSourcesBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | 70BF16A31EDC1B22006C385E /* LabelAnimationDemoUITests.m in Sources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | /* End PBXSourcesBuildPhase section */ 344 | 345 | /* Begin PBXTargetDependency section */ 346 | 70BF16951EDC1B22006C385E /* PBXTargetDependency */ = { 347 | isa = PBXTargetDependency; 348 | target = 70BF16791EDC1B22006C385E /* LabelAnimationDemo */; 349 | targetProxy = 70BF16941EDC1B22006C385E /* PBXContainerItemProxy */; 350 | }; 351 | 70BF16A01EDC1B22006C385E /* PBXTargetDependency */ = { 352 | isa = PBXTargetDependency; 353 | target = 70BF16791EDC1B22006C385E /* LabelAnimationDemo */; 354 | targetProxy = 70BF169F1EDC1B22006C385E /* PBXContainerItemProxy */; 355 | }; 356 | /* End PBXTargetDependency section */ 357 | 358 | /* Begin PBXVariantGroup section */ 359 | 70BF16861EDC1B22006C385E /* Main.storyboard */ = { 360 | isa = PBXVariantGroup; 361 | children = ( 362 | 70BF16871EDC1B22006C385E /* Base */, 363 | ); 364 | name = Main.storyboard; 365 | sourceTree = ""; 366 | }; 367 | 70BF168B1EDC1B22006C385E /* LaunchScreen.storyboard */ = { 368 | isa = PBXVariantGroup; 369 | children = ( 370 | 70BF168C1EDC1B22006C385E /* Base */, 371 | ); 372 | name = LaunchScreen.storyboard; 373 | sourceTree = ""; 374 | }; 375 | /* End PBXVariantGroup section */ 376 | 377 | /* Begin XCBuildConfiguration section */ 378 | 70BF16A51EDC1B22006C385E /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | ALWAYS_SEARCH_USER_PATHS = NO; 382 | CLANG_ANALYZER_NONNULL = YES; 383 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 384 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 385 | CLANG_CXX_LIBRARY = "libc++"; 386 | CLANG_ENABLE_MODULES = YES; 387 | CLANG_ENABLE_OBJC_ARC = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 392 | CLANG_WARN_EMPTY_BODY = YES; 393 | CLANG_WARN_ENUM_CONVERSION = YES; 394 | CLANG_WARN_INFINITE_RECURSION = YES; 395 | CLANG_WARN_INT_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 401 | COPY_PHASE_STRIP = NO; 402 | DEBUG_INFORMATION_FORMAT = dwarf; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | ENABLE_TESTABILITY = YES; 405 | GCC_C_LANGUAGE_STANDARD = gnu99; 406 | GCC_DYNAMIC_NO_PIC = NO; 407 | GCC_NO_COMMON_BLOCKS = YES; 408 | GCC_OPTIMIZATION_LEVEL = 0; 409 | GCC_PREPROCESSOR_DEFINITIONS = ( 410 | "DEBUG=1", 411 | "$(inherited)", 412 | ); 413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 415 | GCC_WARN_UNDECLARED_SELECTOR = YES; 416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 417 | GCC_WARN_UNUSED_FUNCTION = YES; 418 | GCC_WARN_UNUSED_VARIABLE = YES; 419 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 420 | MTL_ENABLE_DEBUG_INFO = YES; 421 | ONLY_ACTIVE_ARCH = YES; 422 | SDKROOT = iphoneos; 423 | }; 424 | name = Debug; 425 | }; 426 | 70BF16A61EDC1B22006C385E /* Release */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | ALWAYS_SEARCH_USER_PATHS = NO; 430 | CLANG_ANALYZER_NONNULL = YES; 431 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 432 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 433 | CLANG_CXX_LIBRARY = "libc++"; 434 | CLANG_ENABLE_MODULES = YES; 435 | CLANG_ENABLE_OBJC_ARC = YES; 436 | CLANG_WARN_BOOL_CONVERSION = YES; 437 | CLANG_WARN_CONSTANT_CONVERSION = YES; 438 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 439 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 440 | CLANG_WARN_EMPTY_BODY = YES; 441 | CLANG_WARN_ENUM_CONVERSION = YES; 442 | CLANG_WARN_INFINITE_RECURSION = YES; 443 | CLANG_WARN_INT_CONVERSION = YES; 444 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 445 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 446 | CLANG_WARN_UNREACHABLE_CODE = YES; 447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 448 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 449 | COPY_PHASE_STRIP = NO; 450 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 451 | ENABLE_NS_ASSERTIONS = NO; 452 | ENABLE_STRICT_OBJC_MSGSEND = YES; 453 | GCC_C_LANGUAGE_STANDARD = gnu99; 454 | GCC_NO_COMMON_BLOCKS = YES; 455 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 456 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 457 | GCC_WARN_UNDECLARED_SELECTOR = YES; 458 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 459 | GCC_WARN_UNUSED_FUNCTION = YES; 460 | GCC_WARN_UNUSED_VARIABLE = YES; 461 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 462 | MTL_ENABLE_DEBUG_INFO = NO; 463 | SDKROOT = iphoneos; 464 | VALIDATE_PRODUCT = YES; 465 | }; 466 | name = Release; 467 | }; 468 | 70BF16A81EDC1B22006C385E /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 472 | DEVELOPMENT_TEAM = W755NUT7GJ; 473 | INFOPLIST_FILE = LabelAnimationDemo/Info.plist; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 475 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemo; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | }; 478 | name = Debug; 479 | }; 480 | 70BF16A91EDC1B22006C385E /* Release */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 484 | DEVELOPMENT_TEAM = W755NUT7GJ; 485 | INFOPLIST_FILE = LabelAnimationDemo/Info.plist; 486 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 487 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemo; 488 | PRODUCT_NAME = "$(TARGET_NAME)"; 489 | }; 490 | name = Release; 491 | }; 492 | 70BF16AB1EDC1B22006C385E /* Debug */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | BUNDLE_LOADER = "$(TEST_HOST)"; 496 | DEVELOPMENT_TEAM = W755NUT7GJ; 497 | INFOPLIST_FILE = LabelAnimationDemoTests/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 499 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemoTests; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LabelAnimationDemo.app/LabelAnimationDemo"; 502 | }; 503 | name = Debug; 504 | }; 505 | 70BF16AC1EDC1B22006C385E /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | BUNDLE_LOADER = "$(TEST_HOST)"; 509 | DEVELOPMENT_TEAM = W755NUT7GJ; 510 | INFOPLIST_FILE = LabelAnimationDemoTests/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemoTests; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LabelAnimationDemo.app/LabelAnimationDemo"; 515 | }; 516 | name = Release; 517 | }; 518 | 70BF16AE1EDC1B22006C385E /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | DEVELOPMENT_TEAM = W755NUT7GJ; 522 | INFOPLIST_FILE = LabelAnimationDemoUITests/Info.plist; 523 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 524 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemoUITests; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | TEST_TARGET_NAME = LabelAnimationDemo; 527 | }; 528 | name = Debug; 529 | }; 530 | 70BF16AF1EDC1B22006C385E /* Release */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | DEVELOPMENT_TEAM = W755NUT7GJ; 534 | INFOPLIST_FILE = LabelAnimationDemoUITests/Info.plist; 535 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 536 | PRODUCT_BUNDLE_IDENTIFIER = Finalno9.LabelAnimationDemoUITests; 537 | PRODUCT_NAME = "$(TARGET_NAME)"; 538 | TEST_TARGET_NAME = LabelAnimationDemo; 539 | }; 540 | name = Release; 541 | }; 542 | /* End XCBuildConfiguration section */ 543 | 544 | /* Begin XCConfigurationList section */ 545 | 70BF16751EDC1B22006C385E /* Build configuration list for PBXProject "LabelAnimationDemo" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 70BF16A51EDC1B22006C385E /* Debug */, 549 | 70BF16A61EDC1B22006C385E /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | 70BF16A71EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemo" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 70BF16A81EDC1B22006C385E /* Debug */, 558 | 70BF16A91EDC1B22006C385E /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | }; 562 | 70BF16AA1EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemoTests" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 70BF16AB1EDC1B22006C385E /* Debug */, 566 | 70BF16AC1EDC1B22006C385E /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | }; 570 | 70BF16AD1EDC1B22006C385E /* Build configuration list for PBXNativeTarget "LabelAnimationDemoUITests" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 70BF16AE1EDC1B22006C385E /* Debug */, 574 | 70BF16AF1EDC1B22006C385E /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | }; 578 | /* End XCConfigurationList section */ 579 | }; 580 | rootObject = 70BF16721EDC1B22006C385E /* Project object */; 581 | } 582 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/project.xcworkspace/xcuserdata/jerryliu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerryliurui/LabelAnimation/406830c7a23b1e65bd562e2d2d48cda051521e0d/LabelAnimationDemo/LabelAnimationDemo.xcodeproj/project.xcworkspace/xcuserdata/jerryliu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/xcuserdata/jerryliu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/xcuserdata/jerryliu.xcuserdatad/xcschemes/LabelAnimationDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo.xcodeproj/xcuserdata/jerryliu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LabelAnimationDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 70BF16791EDC1B22006C385E 16 | 17 | primary 18 | 19 | 20 | 70BF16921EDC1B22006C385E 21 | 22 | primary 23 | 24 | 25 | 70BF169D1EDC1B22006C385E 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/AnimationLabelView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationLabelView.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^GotoCommentBlock)(); 12 | 13 | @interface AnimationLabelView : UIView 14 | 15 | //点击整个view 16 | @property (nonatomic,copy) void(^ gotoCommentBlock)(void); 17 | //返回字体 18 | - (UIFont *)labelFont; 19 | - (void)changeCommentViewWithPlay:(BOOL)willPlay with:(NSString *)commentText; 20 | - (void)updateColorTheme; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/AnimationLabelView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationLabelView.m 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import "AnimationLabelView.h" 10 | #import "UIView+Frame.h" 11 | 12 | #define ONE_CHAR_HEIGHT 16 13 | #define FONT_SIZE 12.5 14 | #define CHAR_STRING_TOP 15 15 | 16 | @interface AnimationLabelView () 17 | 18 | @property (nonatomic,copy) NSString *commentText; 19 | @property (nonatomic,assign) BOOL willPlay; 20 | @property (nonatomic,strong) NSMutableArray *labelsArray; 21 | @property (nonatomic,strong) NSArray *commentTextCharArray; 22 | @property (nonatomic,strong) UIImageView *commentIconImage; 23 | @property (nonatomic,assign) NSInteger animationTimes; 24 | @end 25 | 26 | @implementation AnimationLabelView 27 | 28 | -(instancetype)init { 29 | self = [super init]; 30 | if (self) { 31 | self.backgroundColor = [UIColor clearColor]; 32 | _labelsArray = [NSMutableArray new]; 33 | _commentTextCharArray = [NSMutableArray new]; 34 | [self buildBaseViews]; 35 | [self addGotoCommentTap]; 36 | } 37 | return self; 38 | } 39 | 40 | #pragma mark - UI 41 | - (void)buildBaseViews { 42 | _commentIconImage = [[UIImageView alloc] initWithFrame:CGRectMake(4, 13, 20, 20)]; 43 | _commentIconImage.backgroundColor = [UIColor clearColor]; 44 | [self addSubview:_commentIconImage]; 45 | 46 | [self configIconImagesView]; 47 | } 48 | 49 | - (void)configIconImagesView { 50 | UIImage *image = nil; 51 | image = [UIImage imageNamed:@"lalala"]; 52 | _commentIconImage.image = image; 53 | } 54 | 55 | - (void)updateColorTheme { 56 | [self configIconImagesView]; 57 | 58 | for (UILabel *label in _labelsArray) { 59 | [label setTextColor:[UIColor colorWithRed:(225)/255.0 green:(0)/255.0 blue:(0)/255.0 alpha:1]]; 60 | } 61 | } 62 | 63 | - (void)layoutCommentViewsWithAnimationTimes:(NSInteger)times withCharArray:(NSArray *)charArray { 64 | if (!charArray) { 65 | return; 66 | } 67 | 68 | NSInteger labelCountDiff = charArray.count - _labelsArray.count; 69 | CGFloat width = [_commentText boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[self labelFont]} context:nil].size.width; 70 | CGFloat oneCharWidth = width / charArray.count; 71 | if (labelCountDiff > 0) { 72 | //进位 73 | //调整其他label位置 74 | for (int i = 0; i < _labelsArray.count ; i ++) { 75 | UILabel *label = _labelsArray[i]; 76 | label.frame = CGRectMake(_commentIconImage.right + 2 + (labelCountDiff * oneCharWidth) + i * oneCharWidth, CHAR_STRING_TOP, oneCharWidth, ONE_CHAR_HEIGHT); 77 | } 78 | //补label 79 | for (int i = 0; i < labelCountDiff ; i ++) { 80 | UILabel *charLabel = [[UILabel alloc] initWithFrame:CGRectMake(_commentIconImage.right + 2 + i * oneCharWidth, CHAR_STRING_TOP, oneCharWidth, ONE_CHAR_HEIGHT)]; 81 | [charLabel setTextColor:[UIColor colorWithRed:(225)/255.0 green:(0)/255.0 blue:(0)/255.0 alpha:1]]; 82 | charLabel.textAlignment = NSTextAlignmentLeft; 83 | UIFont *font = [self labelFont]; 84 | [charLabel setFont:font]; 85 | if (i < charArray.count) { 86 | [charLabel setText:charArray[i]]; 87 | } 88 | [charLabel sizeToFit]; 89 | [self addSubview:charLabel]; 90 | charLabel.backgroundColor = [UIColor clearColor]; 91 | [_labelsArray insertObject:charLabel atIndex:i]; 92 | } 93 | }else if(labelCountDiff < 0){ 94 | //减位 95 | labelCountDiff = -labelCountDiff; 96 | for (int i = 0; i < labelCountDiff ; i ++) { 97 | if (i >= 0 && i < _labelsArray.count) { 98 | UILabel *label = _labelsArray[i]; 99 | [label removeFromSuperview]; 100 | [_labelsArray removeObject:label]; 101 | } 102 | } 103 | //调整其他label位置 104 | for (int i = 0; i < _labelsArray.count ; i ++) { 105 | UILabel *label = _labelsArray[i]; 106 | label.frame = CGRectMake(_commentIconImage.right + 2 + i * oneCharWidth, CHAR_STRING_TOP, oneCharWidth, ONE_CHAR_HEIGHT); 107 | } 108 | }else { 109 | for (int i = 0; i < _labelsArray.count ; i ++) { 110 | UILabel *label = _labelsArray[i]; 111 | label.frame = CGRectMake(_commentIconImage.right + 2 + i * oneCharWidth, CHAR_STRING_TOP, oneCharWidth, ONE_CHAR_HEIGHT); 112 | } 113 | } 114 | 115 | //经过上边的操作,现在label中就是新的label数组 116 | if (_willPlay) { 117 | //进行动画 118 | [self playCommentIconAnimation]; 119 | if (times <= _labelsArray.count) {//动画次数是不可能大于现在的label数目的 120 | NSInteger index = charArray.count - 1; 121 | if ([self legalChangeValueAndLabelScopeWith:(int)index]) { 122 | UILabel *label = (UILabel *)_labelsArray[index]; 123 | [self startLabelAnimation:label withLabelText:charArray[index] withIndex:index]; 124 | } 125 | } 126 | }else { 127 | //不进行动画,普通赋值 128 | for (int i = 0 ; i < _labelsArray.count; i ++) { 129 | if ([self legalChangeValueAndLabelScopeWith:i]) { 130 | UILabel *label = (UILabel *)_labelsArray[i]; 131 | NSString *oneCharString = _commentTextCharArray[i]; 132 | [label setText:oneCharString]; 133 | [label sizeToFit]; 134 | } 135 | } 136 | } 137 | } 138 | 139 | #pragma mark - Tap to comment List 140 | - (void)addGotoCommentTap { 141 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGoToCommentView:)]; 142 | [self addGestureRecognizer:tap]; 143 | } 144 | 145 | - (void)tapGoToCommentView:(id)sender { 146 | if (self.gotoCommentBlock) { 147 | self.gotoCommentBlock(); 148 | } 149 | } 150 | 151 | -(void)setGotoCommentBlock:(void (^)(void))gotoCommentBlock { 152 | _gotoCommentBlock = gotoCommentBlock; 153 | } 154 | 155 | #pragma mark - ConfigProperty 156 | - (void)changeCommentViewWithPlay:(BOOL)willPlay with:(NSString *)commentText { 157 | if (!commentText) { 158 | return; 159 | } 160 | 161 | _willPlay = willPlay; 162 | 163 | //第一次赋值的时候 164 | if (!_commentText) { 165 | _animationTimes = 0; 166 | }else {//非第一次赋值 167 | _animationTimes = [self calculateAnimationTimesWithNewCommentText:commentText]; 168 | } 169 | 170 | _commentTextCharArray = [self configCommentTextCharArrayWith:commentText]; 171 | _commentText = commentText; 172 | 173 | [self layoutCommentViewsWithAnimationTimes:_animationTimes withCharArray:_commentTextCharArray]; 174 | } 175 | 176 | #pragma mark - Animation 177 | - (void)playCommentIconAnimation { 178 | //1.播放gif 179 | if (_commentIconImage) { 180 | if (!_commentIconImage.isAnimating) { 181 | [_commentIconImage startAnimating]; 182 | } 183 | } 184 | } 185 | 186 | - (void)startLabelAnimation:(UILabel *)label withLabelText:(NSString *)text withIndex:(NSInteger)index { 187 | if (index < (_labelsArray.count - _animationTimes)) { 188 | return; 189 | } 190 | 191 | if (index < 0 || index > _labelsArray.count - 1 || index > _commentTextCharArray.count - 1) { 192 | return; 193 | } 194 | 195 | if (label && text) { 196 | [label setText:text]; 197 | } 198 | 199 | for (UILabel *label in _labelsArray) { 200 | [label sizeToFit]; 201 | } 202 | 203 | CGPoint originalP = label.layer.position; 204 | CAKeyframeAnimation *posAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 205 | NSArray *values = @[[NSValue valueWithCGPoint:originalP], 206 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y - 1.0)], 207 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y - 3.0)], 208 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y - 6.0)], 209 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y - 3.0)], 210 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y - 1.0)], 211 | [NSValue valueWithCGPoint:CGPointMake(originalP.x, originalP.y + 0.5)], 212 | [NSValue valueWithCGPoint:originalP]]; 213 | posAnim.values = values; 214 | posAnim.keyTimes = @[@(0.0), @(0.05), @(0.4) , @(0.5) , @(0.75) , @(0.9) , @(0.95) , @(1.0)]; 215 | posAnim.duration = 0.2; 216 | [label.layer addAnimation:posAnim forKey:@"labelAnimation"]; 217 | 218 | NSInteger newIndex = index - 1; 219 | 220 | if (newIndex < 0 || newIndex > _labelsArray.count - 1 || newIndex > _commentTextCharArray.count - 1) { 221 | return; 222 | } 223 | 224 | UILabel *nextLabel = (UILabel *)_labelsArray[newIndex]; 225 | NSString *nextString = _commentTextCharArray[newIndex]; 226 | 227 | dispatch_async(dispatch_get_main_queue(), ^{ 228 | double delayInSeconds = 0.16; 229 | dispatch_time_t jumpTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 230 | dispatch_after(jumpTime, dispatch_get_main_queue(), ^{ 231 | [self startLabelAnimation:nextLabel withLabelText:nextString withIndex:newIndex]; 232 | }); 233 | }); 234 | } 235 | 236 | #pragma mark - Func 237 | - (NSInteger )calculateAnimationTimesWithNewCommentText:(NSString *)increasedCommentText{ 238 | //非法的新commentText 返回0 239 | if (!increasedCommentText) { 240 | return 0; 241 | } 242 | 243 | NSArray *increaseArray = [self configCommentTextCharArrayWith:increasedCommentText]; 244 | if (_commentTextCharArray && increaseArray) { 245 | 246 | if (increaseArray.count != _commentTextCharArray.count) {//进位或者减位的情况,新view的每一位都要跳动 247 | return increaseArray.count; 248 | }else{//没有进位,具体判断 249 | NSInteger highestLocation = 0; 250 | for (int i = 0 ; i < increaseArray.count; i ++) { 251 | if (![increaseArray[i] isEqualToString:_commentTextCharArray[i]]) { 252 | highestLocation = increaseArray.count - i; 253 | break; 254 | } 255 | } 256 | return highestLocation; 257 | } 258 | }else { 259 | return 0; 260 | } 261 | } 262 | 263 | - (NSArray *)configCommentTextCharArrayWith:(NSString *)commentText { 264 | if(commentText){ 265 | NSMutableArray *temArray = [NSMutableArray new]; 266 | 267 | while (commentText.length != 0) { 268 | NSString *oneCharString = [commentText substringToIndex:1]; 269 | [temArray addObject:oneCharString]; 270 | commentText = [commentText substringFromIndex:1]; 271 | } 272 | 273 | return temArray; 274 | }else { 275 | return nil; 276 | } 277 | } 278 | 279 | - (UIFont *)labelFont { 280 | CGFloat commentFontSize = FONT_SIZE; 281 | UIFont *commentFont = [UIFont systemFontOfSize:commentFontSize]; 282 | return commentFont; 283 | } 284 | 285 | - (BOOL)legalChangeValueAndLabelScopeWith:(int)i { 286 | if (i >= 0 && i < _labelsArray.count && i < _commentTextCharArray.count) { 287 | return YES; 288 | }else { 289 | return NO; 290 | } 291 | } 292 | 293 | @end 294 | 295 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. 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 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/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 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/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 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/LabelAnimationHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // LabelAnimationHelper.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #define COMMENT_COUNT_THRESHOLD 250//动画数字最少(业务相关) 13 | #define COMMENT_COUNT_RANDOM_PERCENT 0.1//取原母数的百分之X作为随机数 14 | #define COMMENT_RANDOM_COUNT 10 //分为10组差值增长 15 | #define ANIMATION_TIMER_TIMES 10//定时器启动次数 16 | #define ANIMATION_TIME_INTERVAL 3//动画间隔 17 | 18 | typedef NS_ENUM(NSInteger, LabelAnimationHelperChangeValueType) { 19 | LabelAnimationHelperChangeValueTypeGroupAnimation, 20 | LabelAnimationHelperChangeValueTypeOnceAnimation, 21 | LabelAnimationHelperChangeValueTypeNoneAnimation 22 | }; 23 | 24 | @interface LabelAnimationHelper : NSObject 25 | 26 | @property (nonatomic , assign) BOOL commentViewPlaying; 27 | @property (nonatomic , assign) BOOL overThreshold; 28 | 29 | @property (nonatomic , strong) NSArray *randomIncreaseCountArray; 30 | @property (nonatomic , assign) NSInteger randomReplyCount; 31 | @property (nonatomic , assign) NSInteger currentTimes; 32 | 33 | + (instancetype)sharedInstance; 34 | 35 | /** 36 | * 将randomReplyCount分成count个随机数 37 | */ 38 | - (void)configRandomArrayWithArrayCount:(NSInteger)count; 39 | 40 | /** 41 | * 将oriCount取百分之XX, 计算出随机母数randomReplyCount 42 | */ 43 | - (void)calculateRandomCountWithOri: (NSInteger)oriCount withPercentage: (CGFloat)percent; 44 | 45 | /** 46 | * 是否满足业务要求,例如功能是否打开 47 | */ 48 | - (BOOL)isOpened; 49 | 50 | /** 51 | * 重置一下 52 | */ 53 | - (void)resetAllProperty; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/LabelAnimationHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // LabelAnimationHelper.m 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import "LabelAnimationHelper.h" 10 | 11 | @interface LabelAnimationHelper () 12 | 13 | @property (nonatomic , assign) BOOL commentAnimationHallyOpened; 14 | @end 15 | 16 | @implementation LabelAnimationHelper 17 | 18 | + (instancetype)sharedInstance { 19 | static dispatch_once_t onceToken; 20 | __strong static id _sharedObject = nil; 21 | dispatch_once(&onceToken, ^{ 22 | _sharedObject = [[self alloc] init]; 23 | }); 24 | return _sharedObject; 25 | } 26 | 27 | -(instancetype)init { 28 | if (self = [super init]) { 29 | _randomIncreaseCountArray = [NSArray new]; 30 | _commentAnimationHallyOpened = [self halleyIsOpen]; 31 | } 32 | return self; 33 | 34 | } 35 | 36 | - (void)configRandomArrayWithArrayCount:(NSInteger)count { 37 | //排重和排序 都需要count - 1个随机数 38 | count = count - 1; 39 | //count要大于等于1,下边两种情况特殊处理 40 | if (count == 0) { 41 | _randomIncreaseCountArray = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInteger:_randomReplyCount], nil]; 42 | return; 43 | } 44 | 45 | if (count < 0) { 46 | _randomIncreaseCountArray = nil; 47 | return; 48 | } 49 | 50 | if (_randomReplyCount <= count) { 51 | _randomIncreaseCountArray = nil; 52 | return; 53 | } 54 | 55 | //保证每一个随机数都不为0:这里减去多少(>0)都可以,只是为了保证最后一个随机数不为0 56 | _randomReplyCount -= count; 57 | 58 | //排重 59 | NSMutableArray *randomArray = [NSMutableArray new]; 60 | while (randomArray.count < count) { 61 | int value = arc4random() % _randomReplyCount + 1; 62 | NSNumber *valueNumber = [NSNumber numberWithInt:value]; 63 | 64 | if (![randomArray containsObject:valueNumber]) { 65 | [randomArray addObject:valueNumber]; 66 | } 67 | } 68 | 69 | //排序 70 | NSArray *sortedArray = [NSArray new]; 71 | sortedArray = [randomArray sortedArrayUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) { 72 | NSInteger loc1 = [obj1 integerValue]; 73 | NSInteger loc2 = [obj2 integerValue]; 74 | if (loc1 < loc2) { return NSOrderedAscending; } 75 | else if (loc1 > loc2){ return NSOrderedDescending; } 76 | else { return NSOrderedSame; } 77 | }]; 78 | 79 | //算出增长值 80 | NSMutableArray *diffArray = [NSMutableArray new]; 81 | for (int i = 0; i < count; i ++) { 82 | if (i == 0) { 83 | diffArray[0] = [NSNumber numberWithInteger:[sortedArray[0] integerValue]]; 84 | }else { 85 | diffArray[i] = [NSNumber numberWithInteger:[sortedArray[i] integerValue] - [sortedArray[i - 1] integerValue]]; 86 | } 87 | } 88 | 89 | //还原sum 90 | _randomReplyCount += count; 91 | diffArray[count] = [NSNumber numberWithInteger:_randomReplyCount - [sortedArray[count - 1] integerValue]]; 92 | 93 | if (diffArray) { 94 | _randomIncreaseCountArray = diffArray; 95 | }else{ 96 | _randomIncreaseCountArray = nil; 97 | } 98 | } 99 | 100 | - (void)calculateRandomCountWithOri: (NSInteger)oriCount withPercentage: (CGFloat)percent { 101 | NSInteger randomCount = 0; 102 | if (percent > 1 || percent <= 0) { 103 | randomCount = 0; 104 | }else if (percent == 1) { 105 | randomCount = oriCount; 106 | }else { 107 | float remainder = oriCount % (int)(percent * 100); 108 | randomCount = oriCount * percent + remainder; 109 | } 110 | _randomReplyCount = randomCount; 111 | } 112 | 113 | - (BOOL)halleyIsOpen { 114 | return YES; 115 | } 116 | 117 | - (BOOL)isOpened { 118 | return self.commentAnimationHallyOpened; 119 | } 120 | 121 | - (void)resetAllProperty { 122 | self.commentAnimationHallyOpened = [self halleyIsOpen]; 123 | self.randomReplyCount = self.currentTimes = 0; 124 | self.randomIncreaseCountArray = nil; 125 | } 126 | 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/UIView+Frame.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Frame.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (Frame) 12 | @property (nonatomic, getter = top, setter = setTop:) CGFloat y; 13 | @property (nonatomic, getter = left, setter = setLeft:) CGFloat x; 14 | @property (nonatomic) CGFloat right; 15 | @property (nonatomic) CGFloat bottom; 16 | 17 | -(void)setOrigin:(CGPoint)loc; 18 | -(void)setX:(CGFloat)x; 19 | -(void)setY:(CGFloat)y; 20 | -(void)setSize:(CGSize)sz; 21 | -(void)setWidth:(CGFloat)w; 22 | -(void)setHeight:(CGFloat)h; 23 | -(void)setCenterX:(CGFloat)x; 24 | -(void)setCenterY:(CGFloat)y; 25 | -(CGPoint)origin; 26 | -(CGFloat)x; 27 | -(CGFloat)y; 28 | -(CGFloat)left; 29 | -(CGFloat)top; 30 | -(CGFloat)bottom; 31 | -(CGFloat)right; 32 | -(CGSize)size; 33 | -(CGFloat)height; 34 | -(CGFloat)width; 35 | -(CGFloat)centerX; 36 | -(CGFloat)centerY; 37 | -(CGFloat)maxX; 38 | -(CGFloat)maxY; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/UIView+Frame.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationLabelView.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import "UIView+Frame.h" 10 | 11 | @implementation UIView (Frame) 12 | 13 | #pragma UIView+Frame 14 | 15 | #pragma mark - Right 16 | - (void)setRight:(CGFloat)right { 17 | CGRect frame = self.frame; 18 | frame.origin.x = right - frame.size.width; 19 | self.frame = frame; 20 | } 21 | - (CGFloat)right { 22 | return self.frame.origin.x + self.frame.size.width; 23 | } 24 | 25 | #pragma mark - Bottom 26 | - (void)setBottom:(CGFloat)bottom { 27 | CGRect frame = self.frame; 28 | frame.origin.y = bottom - frame.size.height; 29 | self.frame = frame; 30 | } 31 | 32 | -(CGFloat)bottom{ 33 | return self.frame.origin.y + self.frame.size.height; 34 | } 35 | 36 | #pragma mark - Origin 37 | - (void)setOrigin:(CGPoint)origin{ 38 | CGRect rc = self.frame; 39 | rc.origin = origin; 40 | self.frame = rc; 41 | } 42 | 43 | -(CGPoint)origin{ 44 | return self.frame.origin; 45 | } 46 | 47 | #pragma mark - Top 48 | - (CGFloat)top { 49 | return self.frame.origin.y; 50 | } 51 | 52 | - (void)setTop:(CGFloat)y { 53 | CGRect frame = self.frame; 54 | frame.origin.y = y; 55 | self.frame = frame; 56 | } 57 | 58 | #pragma mark - Left 59 | - (CGFloat)left { 60 | return self.frame.origin.x; 61 | } 62 | 63 | - (void)setLeft:(CGFloat)x { 64 | CGRect frame = self.frame; 65 | frame.origin.x = x; 66 | self.frame = frame; 67 | } 68 | 69 | -(void)setX:(CGFloat)x{ 70 | CGRect rc = self.frame; 71 | rc.origin.x = x; 72 | self.frame = rc; 73 | } 74 | 75 | -(void)setY:(CGFloat)y{ 76 | CGRect rc = self.frame; 77 | rc.origin.y = y; 78 | self.frame = rc; 79 | } 80 | 81 | -(void)setSize:(CGSize)sz{ 82 | CGRect rc = self.frame; 83 | rc.size = sz; 84 | self.frame = rc; 85 | } 86 | 87 | -(void)setWidth:(CGFloat)w{ 88 | CGRect rc = self.frame; 89 | rc.size.width = w; 90 | self.frame = rc; 91 | } 92 | 93 | -(void)setHeight:(CGFloat)h{ 94 | CGRect rc = self.frame; 95 | rc.size.height = h; 96 | self.frame = rc; 97 | } 98 | 99 | -(void)setCenterY:(CGFloat)y{ 100 | CGPoint pt = self.center; 101 | pt.y = y; 102 | self.center = pt; 103 | } 104 | 105 | -(void)setCenterX:(CGFloat)x{ 106 | CGPoint pt = self.center; 107 | pt.x = x; 108 | self.center = pt; 109 | } 110 | 111 | -(CGFloat)x{ 112 | return self.frame.origin.x; 113 | } 114 | 115 | -(CGFloat)y{ 116 | return self.frame.origin.y; 117 | } 118 | 119 | -(CGSize)size{ 120 | return self.frame.size; 121 | } 122 | 123 | -(CGFloat)height 124 | { 125 | return self.frame.size.height; 126 | } 127 | 128 | -(CGFloat)width 129 | { 130 | return self.frame.size.width; 131 | } 132 | 133 | -(CGFloat)centerX{ 134 | return self.center.x; 135 | } 136 | 137 | -(CGFloat)centerY{ 138 | return self.center.y; 139 | } 140 | 141 | -(CGFloat)maxX{ 142 | return CGRectGetMaxX(self.frame); 143 | } 144 | 145 | -(CGFloat)maxY{ 146 | return CGRectGetMaxY(self.frame); 147 | } 148 | @end 149 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "AnimationLabelView.h" 11 | #import "UIView+Frame.h" 12 | #import "LabelAnimationHelper.h" 13 | 14 | @interface ViewController () 15 | 16 | @end 17 | 18 | @implementation ViewController { 19 | AnimationLabelView *_animationView; 20 | UIButton *_startButton; 21 | LabelAnimationHelper *_animationHelper; 22 | NSTimer *_animationIntervalTimer; 23 | NSInteger _count; 24 | } 25 | 26 | - (void)viewDidLoad { 27 | _animationHelper = [LabelAnimationHelper sharedInstance]; 28 | _count = 66666; 29 | [super viewDidLoad]; 30 | [self buildBaseViews]; 31 | [self buildAnimationView]; 32 | } 33 | 34 | - (void)buildBaseViews { 35 | _startButton = [UIButton buttonWithType:UIButtonTypeCustom]; 36 | _startButton.backgroundColor = [UIColor greenColor]; 37 | _startButton.frame = CGRectMake(self.view.width/2 - 20, 100, 40, 30); 38 | [_startButton setTitle:@"开始" forState:UIControlStateNormal]; 39 | [_startButton addTarget:self action:@selector(beginAnimation) forControlEvents:UIControlEventTouchUpInside]; 40 | [self.view addSubview:_startButton]; 41 | } 42 | 43 | - (void)buildAnimationView { 44 | __weak ViewController *weakSelf = self; 45 | 46 | AnimationLabelView *animationView = [[AnimationLabelView alloc] init]; 47 | UIFont *labelFont = [animationView labelFont]; 48 | CGFloat width = [[NSString stringWithFormat:@"%li",(long)_count] boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:labelFont} context:nil].size.width; 49 | animationView.frame = CGRectMake(140, self.view.frame.size.height/2, width, 46); 50 | 51 | GotoCommentBlock block = ^() { 52 | [weakSelf sayHello]; 53 | }; 54 | animationView.gotoCommentBlock = block; 55 | 56 | _animationView = animationView; 57 | [self.view addSubview:_animationView]; 58 | [_animationView changeCommentViewWithPlay:NO with:[NSString stringWithFormat:@"%li",(long)_count]]; 59 | } 60 | 61 | - (void)sayHello { 62 | 63 | } 64 | 65 | - (void)beginAnimation { 66 | [_animationHelper calculateRandomCountWithOri:3333 withPercentage:1]; 67 | [self startTimerToPlayCommentViewWithType:LabelAnimationHelperChangeValueTypeGroupAnimation]; 68 | } 69 | 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; 73 | } 74 | 75 | - (void)startTimerToPlayCommentViewWithType:(LabelAnimationHelperChangeValueType)type { 76 | _animationHelper.currentTimes = 0; 77 | NSInteger arrayCount = 0; 78 | if (type == LabelAnimationHelperChangeValueTypeGroupAnimation) { 79 | arrayCount = COMMENT_RANDOM_COUNT; 80 | }else if (type == LabelAnimationHelperChangeValueTypeOnceAnimation) { 81 | arrayCount = 1; 82 | }else if (type == LabelAnimationHelperChangeValueTypeNoneAnimation) { 83 | arrayCount = 0; 84 | }else { 85 | return; 86 | } 87 | 88 | [_animationHelper configRandomArrayWithArrayCount:arrayCount]; 89 | if (_animationHelper.randomIncreaseCountArray || arrayCount == 0) { 90 | 91 | _animationIntervalTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_TIME_INTERVAL target:self selector:@selector(playCommentViewAnimation) userInfo:nil repeats:YES]; 92 | [[NSRunLoop currentRunLoop] addTimer:_animationIntervalTimer forMode:NSRunLoopCommonModes]; 93 | } 94 | } 95 | 96 | - (void)playCommentViewAnimation { 97 | if (_animationHelper.randomIncreaseCountArray) { 98 | if (_animationHelper.randomIncreaseCountArray.count >= (_animationHelper.currentTimes+1)) { 99 | NSInteger increaseCount = [_animationHelper.randomIncreaseCountArray[_animationHelper.currentTimes] integerValue]; 100 | if (increaseCount > 0) { 101 | _count += increaseCount; 102 | [_animationView changeCommentViewWithPlay:YES with:[NSString stringWithFormat:@"%li",(long)_count]]; 103 | } 104 | } 105 | } 106 | 107 | _animationHelper.currentTimes += 1; 108 | if (_animationHelper.currentTimes == ANIMATION_TIMER_TIMES) { 109 | //停止定时器 110 | _animationHelper.currentTimes = 0; 111 | [_animationIntervalTimer invalidate]; 112 | } 113 | } 114 | 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LabelAnimationDemo 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. 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 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemoTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemoTests/LabelAnimationDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LabelAnimationDemoTests.m 3 | // LabelAnimationDemoTests 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LabelAnimationDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LabelAnimationDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemoUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LabelAnimationDemo/LabelAnimationDemoUITests/LabelAnimationDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LabelAnimationDemoUITests.m 3 | // LabelAnimationDemoUITests 4 | // 5 | // Created by JerryLiu on 2017/5/29. 6 | // Copyright © 2017年 JerryLiu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LabelAnimationDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LabelAnimationDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LabelAnimation 2 | 类网易新闻客户端跟贴动画Demo 3 | 4 | 效果如下: 5 | ![LabelAnimation](https://github.com/jerryliurui/LabelAnimation/blob/master/demoAnimation.gif) 6 | -------------------------------------------------------------------------------- /demoAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerryliurui/LabelAnimation/406830c7a23b1e65bd562e2d2d48cda051521e0d/demoAnimation.gif --------------------------------------------------------------------------------