├── .gitignore ├── AnimateTextExample ├── AnimateTextExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── AnimateText.xcscheme ├── Shared │ ├── AnimateTextExampleApp.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── ContentView.swift │ ├── CustomEffect.swift │ └── TapAnimateTextView.swift └── macOS │ └── macOS.entitlements ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── AnimateText │ ├── AnimateText.swift │ ├── Effects │ ├── Advanced │ │ ├── ATChainEffect.swift │ │ ├── ATChimeBellEffect.swift │ │ ├── ATCurtainEffect.swift │ │ ├── ATDropEffect.swift │ │ ├── ATHangEffect.swift │ │ ├── ATPaperEffect.swift │ │ ├── ATRandomTypoEffect.swift │ │ ├── ATSpringEffect.swift │ │ ├── ATTwistEffect.swift │ │ └── ATTypoEffect.swift │ └── Basic │ │ ├── ATBlurEffect.swift │ │ ├── ATBottomTopEffect.swift │ │ ├── ATOffsetEffect.swift │ │ ├── ATOpacityEffect.swift │ │ ├── ATRotateEffect.swift │ │ ├── ATScaleEffect.swift │ │ ├── ATSlideEffect.swift │ │ └── ATTopBottomEffect.swift │ ├── Extensions │ └── View+Extensions.swift │ ├── Modifiers │ └── ATRandomTypoAnimation.swift │ ├── Preview │ └── ATAnimateTextPreview.swift │ └── Protocol │ ├── ATTextAnimatable.swift │ ├── ATTextAnimateEffect.swift │ └── Data │ └── ATElementData.swift └── Tests └── AnimateTextTests └── AnimateTextTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /AnimateTextExample/AnimateTextExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4B4CFFBD27AE804100F03090 /* AnimateText in Frameworks */ = {isa = PBXBuildFile; productRef = 4B4CFFBC27AE804100F03090 /* AnimateText */; }; 11 | 4B4CFFBF27AE804600F03090 /* AnimateText in Frameworks */ = {isa = PBXBuildFile; productRef = 4B4CFFBE27AE804600F03090 /* AnimateText */; }; 12 | 4B50A0CF27AE7B63004BDAEF /* AnimateTextExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B50A0BF27AE7B62004BDAEF /* AnimateTextExampleApp.swift */; }; 13 | 4B50A0D027AE7B63004BDAEF /* AnimateTextExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B50A0BF27AE7B62004BDAEF /* AnimateTextExampleApp.swift */; }; 14 | 4B50A0D127AE7B63004BDAEF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B50A0C027AE7B62004BDAEF /* ContentView.swift */; }; 15 | 4B50A0D227AE7B63004BDAEF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B50A0C027AE7B62004BDAEF /* ContentView.swift */; }; 16 | 4B50A0D327AE7B63004BDAEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B50A0C127AE7B63004BDAEF /* Assets.xcassets */; }; 17 | 4B50A0D427AE7B63004BDAEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B50A0C127AE7B63004BDAEF /* Assets.xcassets */; }; 18 | 4BA170DE27B107DB00CCE42B /* CustomEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA170DD27B107DB00CCE42B /* CustomEffect.swift */; }; 19 | 4BA170DF27B107DB00CCE42B /* CustomEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA170DD27B107DB00CCE42B /* CustomEffect.swift */; }; 20 | 4BBCCCB127B001C400296595 /* TapAnimateTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBCCCB027B001C400296595 /* TapAnimateTextView.swift */; }; 21 | 4BBCCCB227B001C400296595 /* TapAnimateTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBCCCB027B001C400296595 /* TapAnimateTextView.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 4B50A0BF27AE7B62004BDAEF /* AnimateTextExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateTextExampleApp.swift; sourceTree = ""; }; 26 | 4B50A0C027AE7B62004BDAEF /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 27 | 4B50A0C127AE7B63004BDAEF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | 4B50A0C627AE7B63004BDAEF /* AnimateTextExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnimateTextExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 4B50A0CC27AE7B63004BDAEF /* AnimateTextExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnimateTextExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 4B50A0CE27AE7B63004BDAEF /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; 31 | 4B50A0DE27AE7BBD004BDAEF /* AnimateText */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = AnimateText; path = ..; sourceTree = ""; }; 32 | 4BA170DD27B107DB00CCE42B /* CustomEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEffect.swift; sourceTree = ""; }; 33 | 4BBCCCB027B001C400296595 /* TapAnimateTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapAnimateTextView.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 4B50A0C327AE7B63004BDAEF /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | 4B4CFFBD27AE804100F03090 /* AnimateText in Frameworks */, 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | 4B50A0C927AE7B63004BDAEF /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 4B4CFFBF27AE804600F03090 /* AnimateText in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 4B4CFFBB27AE804100F03090 /* Frameworks */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | ); 60 | name = Frameworks; 61 | sourceTree = ""; 62 | }; 63 | 4B50A0B927AE7B62004BDAEF = { 64 | isa = PBXGroup; 65 | children = ( 66 | 4B50A0DD27AE7BBD004BDAEF /* Packages */, 67 | 4B50A0BE27AE7B62004BDAEF /* Shared */, 68 | 4B50A0CD27AE7B63004BDAEF /* macOS */, 69 | 4B50A0C727AE7B63004BDAEF /* Products */, 70 | 4B4CFFBB27AE804100F03090 /* Frameworks */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | 4B50A0BE27AE7B62004BDAEF /* Shared */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 4B50A0BF27AE7B62004BDAEF /* AnimateTextExampleApp.swift */, 78 | 4B50A0C027AE7B62004BDAEF /* ContentView.swift */, 79 | 4BA170DD27B107DB00CCE42B /* CustomEffect.swift */, 80 | 4BBCCCB027B001C400296595 /* TapAnimateTextView.swift */, 81 | 4B50A0C127AE7B63004BDAEF /* Assets.xcassets */, 82 | ); 83 | path = Shared; 84 | sourceTree = ""; 85 | }; 86 | 4B50A0C727AE7B63004BDAEF /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 4B50A0C627AE7B63004BDAEF /* AnimateTextExample.app */, 90 | 4B50A0CC27AE7B63004BDAEF /* AnimateTextExample.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 4B50A0CD27AE7B63004BDAEF /* macOS */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 4B50A0CE27AE7B63004BDAEF /* macOS.entitlements */, 99 | ); 100 | path = macOS; 101 | sourceTree = ""; 102 | }; 103 | 4B50A0DD27AE7BBD004BDAEF /* Packages */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 4B50A0DE27AE7BBD004BDAEF /* AnimateText */, 107 | ); 108 | name = Packages; 109 | sourceTree = ""; 110 | }; 111 | /* End PBXGroup section */ 112 | 113 | /* Begin PBXNativeTarget section */ 114 | 4B50A0C527AE7B63004BDAEF /* AnimateTextExample (iOS) */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = 4B50A0D727AE7B63004BDAEF /* Build configuration list for PBXNativeTarget "AnimateTextExample (iOS)" */; 117 | buildPhases = ( 118 | 4B50A0C227AE7B63004BDAEF /* Sources */, 119 | 4B50A0C327AE7B63004BDAEF /* Frameworks */, 120 | 4B50A0C427AE7B63004BDAEF /* Resources */, 121 | ); 122 | buildRules = ( 123 | ); 124 | dependencies = ( 125 | ); 126 | name = "AnimateTextExample (iOS)"; 127 | packageProductDependencies = ( 128 | 4B4CFFBC27AE804100F03090 /* AnimateText */, 129 | ); 130 | productName = "AnimateTextExample (iOS)"; 131 | productReference = 4B50A0C627AE7B63004BDAEF /* AnimateTextExample.app */; 132 | productType = "com.apple.product-type.application"; 133 | }; 134 | 4B50A0CB27AE7B63004BDAEF /* AnimateTextExample (macOS) */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = 4B50A0DA27AE7B63004BDAEF /* Build configuration list for PBXNativeTarget "AnimateTextExample (macOS)" */; 137 | buildPhases = ( 138 | 4B50A0C827AE7B63004BDAEF /* Sources */, 139 | 4B50A0C927AE7B63004BDAEF /* Frameworks */, 140 | 4B50A0CA27AE7B63004BDAEF /* Resources */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | ); 146 | name = "AnimateTextExample (macOS)"; 147 | packageProductDependencies = ( 148 | 4B4CFFBE27AE804600F03090 /* AnimateText */, 149 | ); 150 | productName = "AnimateTextExample (macOS)"; 151 | productReference = 4B50A0CC27AE7B63004BDAEF /* AnimateTextExample.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 4B50A0BA27AE7B62004BDAEF /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | BuildIndependentTargetsInParallel = 1; 161 | LastSwiftUpdateCheck = 1320; 162 | LastUpgradeCheck = 1320; 163 | TargetAttributes = { 164 | 4B50A0C527AE7B63004BDAEF = { 165 | CreatedOnToolsVersion = 13.2.1; 166 | }; 167 | 4B50A0CB27AE7B63004BDAEF = { 168 | CreatedOnToolsVersion = 13.2.1; 169 | }; 170 | }; 171 | }; 172 | buildConfigurationList = 4B50A0BD27AE7B62004BDAEF /* Build configuration list for PBXProject "AnimateTextExample" */; 173 | compatibilityVersion = "Xcode 13.0"; 174 | developmentRegion = en; 175 | hasScannedForEncodings = 0; 176 | knownRegions = ( 177 | en, 178 | Base, 179 | ); 180 | mainGroup = 4B50A0B927AE7B62004BDAEF; 181 | productRefGroup = 4B50A0C727AE7B63004BDAEF /* Products */; 182 | projectDirPath = ""; 183 | projectRoot = ""; 184 | targets = ( 185 | 4B50A0C527AE7B63004BDAEF /* AnimateTextExample (iOS) */, 186 | 4B50A0CB27AE7B63004BDAEF /* AnimateTextExample (macOS) */, 187 | ); 188 | }; 189 | /* End PBXProject section */ 190 | 191 | /* Begin PBXResourcesBuildPhase section */ 192 | 4B50A0C427AE7B63004BDAEF /* Resources */ = { 193 | isa = PBXResourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | 4B50A0D327AE7B63004BDAEF /* Assets.xcassets in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | 4B50A0CA27AE7B63004BDAEF /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 4B50A0D427AE7B63004BDAEF /* Assets.xcassets in Resources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 4B50A0C227AE7B63004BDAEF /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 4B50A0D127AE7B63004BDAEF /* ContentView.swift in Sources */, 216 | 4BA170DE27B107DB00CCE42B /* CustomEffect.swift in Sources */, 217 | 4BBCCCB127B001C400296595 /* TapAnimateTextView.swift in Sources */, 218 | 4B50A0CF27AE7B63004BDAEF /* AnimateTextExampleApp.swift in Sources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | 4B50A0C827AE7B63004BDAEF /* Sources */ = { 223 | isa = PBXSourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 4B50A0D227AE7B63004BDAEF /* ContentView.swift in Sources */, 227 | 4BA170DF27B107DB00CCE42B /* CustomEffect.swift in Sources */, 228 | 4BBCCCB227B001C400296595 /* TapAnimateTextView.swift in Sources */, 229 | 4B50A0D027AE7B63004BDAEF /* AnimateTextExampleApp.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin XCBuildConfiguration section */ 236 | 4B50A0D527AE7B63004BDAEF /* Debug */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | CLANG_ANALYZER_NONNULL = YES; 241 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_ENABLE_OBJC_WEAK = YES; 247 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 248 | CLANG_WARN_BOOL_CONVERSION = YES; 249 | CLANG_WARN_COMMA = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 252 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 253 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INFINITE_RECURSION = YES; 257 | CLANG_WARN_INT_CONVERSION = YES; 258 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 260 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 262 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 263 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 264 | CLANG_WARN_STRICT_PROTOTYPES = YES; 265 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 266 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 267 | CLANG_WARN_UNREACHABLE_CODE = YES; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | COPY_PHASE_STRIP = NO; 270 | DEBUG_INFORMATION_FORMAT = dwarf; 271 | ENABLE_STRICT_OBJC_MSGSEND = YES; 272 | ENABLE_TESTABILITY = YES; 273 | GCC_C_LANGUAGE_STANDARD = gnu11; 274 | GCC_DYNAMIC_NO_PIC = NO; 275 | GCC_NO_COMMON_BLOCKS = YES; 276 | GCC_OPTIMIZATION_LEVEL = 0; 277 | GCC_PREPROCESSOR_DEFINITIONS = ( 278 | "DEBUG=1", 279 | "$(inherited)", 280 | ); 281 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 282 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 283 | GCC_WARN_UNDECLARED_SELECTOR = YES; 284 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 285 | GCC_WARN_UNUSED_FUNCTION = YES; 286 | GCC_WARN_UNUSED_VARIABLE = YES; 287 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 288 | MTL_FAST_MATH = YES; 289 | ONLY_ACTIVE_ARCH = YES; 290 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 291 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 292 | }; 293 | name = Debug; 294 | }; 295 | 4B50A0D627AE7B63004BDAEF /* Release */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ALWAYS_SEARCH_USER_PATHS = NO; 299 | CLANG_ANALYZER_NONNULL = YES; 300 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 301 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 302 | CLANG_CXX_LIBRARY = "libc++"; 303 | CLANG_ENABLE_MODULES = YES; 304 | CLANG_ENABLE_OBJC_ARC = YES; 305 | CLANG_ENABLE_OBJC_WEAK = YES; 306 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 307 | CLANG_WARN_BOOL_CONVERSION = YES; 308 | CLANG_WARN_COMMA = YES; 309 | CLANG_WARN_CONSTANT_CONVERSION = YES; 310 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 313 | CLANG_WARN_EMPTY_BODY = YES; 314 | CLANG_WARN_ENUM_CONVERSION = YES; 315 | CLANG_WARN_INFINITE_RECURSION = YES; 316 | CLANG_WARN_INT_CONVERSION = YES; 317 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 318 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 319 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 323 | CLANG_WARN_STRICT_PROTOTYPES = YES; 324 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 325 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 326 | CLANG_WARN_UNREACHABLE_CODE = YES; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | COPY_PHASE_STRIP = NO; 329 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 330 | ENABLE_NS_ASSERTIONS = NO; 331 | ENABLE_STRICT_OBJC_MSGSEND = YES; 332 | GCC_C_LANGUAGE_STANDARD = gnu11; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | MTL_ENABLE_DEBUG_INFO = NO; 341 | MTL_FAST_MATH = YES; 342 | SWIFT_COMPILATION_MODE = wholemodule; 343 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 344 | }; 345 | name = Release; 346 | }; 347 | 4B50A0D827AE7B63004BDAEF /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 351 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 352 | CODE_SIGN_STYLE = Automatic; 353 | CURRENT_PROJECT_VERSION = 1; 354 | DEVELOPMENT_TEAM = SM6445X39C; 355 | ENABLE_PREVIEWS = YES; 356 | GENERATE_INFOPLIST_FILE = YES; 357 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 358 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 359 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 360 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 361 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 362 | IPHONEOS_DEPLOYMENT_TARGET = 15.2; 363 | LD_RUNPATH_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "@executable_path/Frameworks", 366 | ); 367 | MARKETING_VERSION = 1.0; 368 | PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AnimateTextExample; 369 | PRODUCT_NAME = AnimateTextExample; 370 | SDKROOT = iphoneos; 371 | SWIFT_EMIT_LOC_STRINGS = YES; 372 | SWIFT_VERSION = 5.0; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Debug; 376 | }; 377 | 4B50A0D927AE7B63004BDAEF /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 381 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 382 | CODE_SIGN_STYLE = Automatic; 383 | CURRENT_PROJECT_VERSION = 1; 384 | DEVELOPMENT_TEAM = SM6445X39C; 385 | ENABLE_PREVIEWS = YES; 386 | GENERATE_INFOPLIST_FILE = YES; 387 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 388 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 389 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 390 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 391 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 392 | IPHONEOS_DEPLOYMENT_TARGET = 15.2; 393 | LD_RUNPATH_SEARCH_PATHS = ( 394 | "$(inherited)", 395 | "@executable_path/Frameworks", 396 | ); 397 | MARKETING_VERSION = 1.0; 398 | PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AnimateTextExample; 399 | PRODUCT_NAME = AnimateTextExample; 400 | SDKROOT = iphoneos; 401 | SWIFT_EMIT_LOC_STRINGS = YES; 402 | SWIFT_VERSION = 5.0; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 4B50A0DB27AE7B63004BDAEF /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 412 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 413 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; 414 | CODE_SIGN_STYLE = Automatic; 415 | COMBINE_HIDPI_IMAGES = YES; 416 | CURRENT_PROJECT_VERSION = 1; 417 | DEVELOPMENT_TEAM = SM6445X39C; 418 | ENABLE_HARDENED_RUNTIME = YES; 419 | ENABLE_PREVIEWS = YES; 420 | GENERATE_INFOPLIST_FILE = YES; 421 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; 422 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 423 | LD_RUNPATH_SEARCH_PATHS = ( 424 | "$(inherited)", 425 | "@executable_path/../Frameworks", 426 | ); 427 | MACOSX_DEPLOYMENT_TARGET = 12.1; 428 | MARKETING_VERSION = 1.0; 429 | PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AnimateTextExample; 430 | PRODUCT_NAME = AnimateTextExample; 431 | SDKROOT = macosx; 432 | SWIFT_EMIT_LOC_STRINGS = YES; 433 | SWIFT_VERSION = 5.0; 434 | }; 435 | name = Debug; 436 | }; 437 | 4B50A0DC27AE7B63004BDAEF /* Release */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 441 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 442 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; 443 | CODE_SIGN_STYLE = Automatic; 444 | COMBINE_HIDPI_IMAGES = YES; 445 | CURRENT_PROJECT_VERSION = 1; 446 | DEVELOPMENT_TEAM = SM6445X39C; 447 | ENABLE_HARDENED_RUNTIME = YES; 448 | ENABLE_PREVIEWS = YES; 449 | GENERATE_INFOPLIST_FILE = YES; 450 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; 451 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 452 | LD_RUNPATH_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "@executable_path/../Frameworks", 455 | ); 456 | MACOSX_DEPLOYMENT_TARGET = 12.1; 457 | MARKETING_VERSION = 1.0; 458 | PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AnimateTextExample; 459 | PRODUCT_NAME = AnimateTextExample; 460 | SDKROOT = macosx; 461 | SWIFT_EMIT_LOC_STRINGS = YES; 462 | SWIFT_VERSION = 5.0; 463 | }; 464 | name = Release; 465 | }; 466 | /* End XCBuildConfiguration section */ 467 | 468 | /* Begin XCConfigurationList section */ 469 | 4B50A0BD27AE7B62004BDAEF /* Build configuration list for PBXProject "AnimateTextExample" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | 4B50A0D527AE7B63004BDAEF /* Debug */, 473 | 4B50A0D627AE7B63004BDAEF /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | 4B50A0D727AE7B63004BDAEF /* Build configuration list for PBXNativeTarget "AnimateTextExample (iOS)" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | 4B50A0D827AE7B63004BDAEF /* Debug */, 482 | 4B50A0D927AE7B63004BDAEF /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | 4B50A0DA27AE7B63004BDAEF /* Build configuration list for PBXNativeTarget "AnimateTextExample (macOS)" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 4B50A0DB27AE7B63004BDAEF /* Debug */, 491 | 4B50A0DC27AE7B63004BDAEF /* Release */, 492 | ); 493 | defaultConfigurationIsVisible = 0; 494 | defaultConfigurationName = Release; 495 | }; 496 | /* End XCConfigurationList section */ 497 | 498 | /* Begin XCSwiftPackageProductDependency section */ 499 | 4B4CFFBC27AE804100F03090 /* AnimateText */ = { 500 | isa = XCSwiftPackageProductDependency; 501 | productName = AnimateText; 502 | }; 503 | 4B4CFFBE27AE804600F03090 /* AnimateText */ = { 504 | isa = XCSwiftPackageProductDependency; 505 | productName = AnimateText; 506 | }; 507 | /* End XCSwiftPackageProductDependency section */ 508 | }; 509 | rootObject = 4B50A0BA27AE7B62004BDAEF /* Project object */; 510 | } 511 | -------------------------------------------------------------------------------- /AnimateTextExample/AnimateTextExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AnimateTextExample/AnimateTextExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AnimateTextExample/AnimateTextExample.xcodeproj/xcshareddata/xcschemes/AnimateText.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/AnimateTextExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimateTextExampleApp.swift 3 | // AnimateTextExample 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | @main 12 | struct AnimateTextExampleApp: App { 13 | var body: some Scene { 14 | WindowGroup { 15 | ContentView() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | }, 93 | { 94 | "idiom" : "mac", 95 | "scale" : "1x", 96 | "size" : "16x16" 97 | }, 98 | { 99 | "idiom" : "mac", 100 | "scale" : "2x", 101 | "size" : "16x16" 102 | }, 103 | { 104 | "idiom" : "mac", 105 | "scale" : "1x", 106 | "size" : "32x32" 107 | }, 108 | { 109 | "idiom" : "mac", 110 | "scale" : "2x", 111 | "size" : "32x32" 112 | }, 113 | { 114 | "idiom" : "mac", 115 | "scale" : "1x", 116 | "size" : "128x128" 117 | }, 118 | { 119 | "idiom" : "mac", 120 | "scale" : "2x", 121 | "size" : "128x128" 122 | }, 123 | { 124 | "idiom" : "mac", 125 | "scale" : "1x", 126 | "size" : "256x256" 127 | }, 128 | { 129 | "idiom" : "mac", 130 | "scale" : "2x", 131 | "size" : "256x256" 132 | }, 133 | { 134 | "idiom" : "mac", 135 | "scale" : "1x", 136 | "size" : "512x512" 137 | }, 138 | { 139 | "idiom" : "mac", 140 | "scale" : "2x", 141 | "size" : "512x512" 142 | } 143 | ], 144 | "info" : { 145 | "author" : "xcode", 146 | "version" : 1 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // AnimateTextExample 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import AnimateText 11 | 12 | struct ContentView: View { 13 | 14 | @State private var unitType: ATUnitType = .letters 15 | 16 | let words = "Labyrinth,Ineffable,Incendiary,Ephemeral,Cynosure,Propinquity,Infatuation,Incandescent,Eudaemonia,Raconteur,Petrichor,Sumptuous,Aesthete,Nadir,Miraculous,Lassitude,Gossamer,Bungalow,Aurora,Inure,Mellifluous,Euphoria,Cherish,Demure,Elixir,Eternity,Felicity,Languor,Love,Solitude,Epiphany,Quintessential,Plethora,Nemesis,Lithe,Tranquility,Elegance,Renaissance,Eloquence,Sequoia,Peace,Lullaby,Paradox,Pristine,Effervescent,Opulence,Ethereal,Sanguine,Panacea,Bodacious,Axiom,Silhouette,Surreptitious,Ingenue,Dulcet,Tryst,Ebullience".components(separatedBy: ",") 17 | let sentence = "Don’t dwell on the past.,Believe in yourself.,Follow your heart.,Seize the day.,You only live once.,Past is just past.,Love yourself.,Don’t beat yourself up.,Life is a journey.,No Pain,No gain,No sweat,The die is cast.,When they go low,A friend is a second myself.,Appearances are deceptive.,Be brave.,Every cloud has a silver lining.,Don’t judge a book by its cover.,Hang in there.,This is how life is.,Live positive.,Seeing is believing.,He can do, She can do,Why not me,If not now,then when?,Respect individual.,Habit is a second nature.,Time is gold.,You deserve to be loved.,Love what you do.,Time waits for no one.,Don’t waste your youth.,Pain past is pleasure.,United we stand.,Envy and wrath shorten life.,Life is all about timing.".components(separatedBy: ",") 18 | 19 | let customUserInfo: [String : Any] = ["color": Color.accentColor] 20 | let typeUserInfo: [String : Any] = ["base": "-"] 21 | 22 | var content: some View { 23 | Group { 24 | Group { 25 | TapAnimateTextView(type: unitType, elements: getElements(), userInfo: customUserInfo) 26 | } 27 | Group { 28 | TapAnimateTextView(type: unitType, elements: getElements()) 29 | TapAnimateTextView(type: unitType, elements: getElements(), userInfo: typeUserInfo) 30 | TapAnimateTextView(type: unitType, elements: getElements()) 31 | TapAnimateTextView(type: unitType, elements: getElements()) 32 | TapAnimateTextView(type: unitType, elements: getElements()) 33 | TapAnimateTextView(type: unitType, elements: getElements()) 34 | TapAnimateTextView(type: unitType, elements: getElements()) 35 | TapAnimateTextView(type: unitType, elements: getElements()) 36 | TapAnimateTextView(type: unitType, elements: getElements()) 37 | TapAnimateTextView(type: unitType, elements: getElements()) 38 | } 39 | Group { 40 | TapAnimateTextView(type: unitType, elements: getElements()) 41 | TapAnimateTextView(type: unitType, elements: getElements()) 42 | TapAnimateTextView(type: unitType, elements: getElements()) 43 | TapAnimateTextView(type: unitType, elements: getElements()) 44 | TapAnimateTextView(type: unitType, elements: getElements()) 45 | TapAnimateTextView(type: unitType, elements: getElements()) 46 | TapAnimateTextView(type: unitType, elements: getElements()) 47 | TapAnimateTextView(type: unitType, elements: getElements()) 48 | } 49 | } 50 | } 51 | 52 | var body: some View { 53 | #if os(iOS) 54 | NavigationView { 55 | VStack { 56 | Picker(selection: $unitType, content: { 57 | Text("Letters").tag(ATUnitType.letters) 58 | Text("Words").tag(ATUnitType.words) 59 | }, label: { 60 | EmptyView() 61 | }) 62 | .pickerStyle(.segmented) 63 | .padding() 64 | TabView { 65 | content 66 | } 67 | .tabViewStyle(.page(indexDisplayMode: .always)) 68 | .indexViewStyle(.page(backgroundDisplayMode: .always)) 69 | } 70 | .navigationTitle(Text("AnimateText")) 71 | .navigationBarTitleDisplayMode(.inline) 72 | } 73 | .navigationViewStyle(.stack) 74 | #else 75 | VStack { 76 | Picker(selection: $unitType, content: { 77 | Text("Letters").tag(ATUnitType.letters) 78 | Text("Words").tag(ATUnitType.words) 79 | }, label: { 80 | EmptyView() 81 | }) 82 | .pickerStyle(.segmented) 83 | .padding() 84 | List { 85 | content 86 | .frame(height: 260) 87 | } 88 | } 89 | .navigationTitle(Text("AnimateText")) 90 | .frame(minWidth: 900, minHeight: 800) 91 | #endif 92 | } 93 | 94 | private func getElements() -> [String] { 95 | return self.unitType == .letters ? self.words : self.sentence 96 | } 97 | } 98 | 99 | struct ContentView_Previews: PreviewProvider { 100 | static var previews: some View { 101 | ContentView() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/CustomEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomEffect.swift 3 | // AnimateTextExample 4 | // 5 | // Created by jasu on 2022/02/07. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import AnimateText 11 | 12 | struct CustomEffect: ATTextAnimateEffect { 13 | 14 | var data: ATElementData 15 | var userInfo: Any? 16 | 17 | var color: Color = .red 18 | 19 | public init(_ data: ATElementData, _ userInfo: Any?) { 20 | self.data = data 21 | self.userInfo = userInfo 22 | if let info = userInfo as? [String: Any] { 23 | color = info["color"] as! Color 24 | } 25 | } 26 | 27 | func body(content: Content) -> some View { 28 | ZStack { 29 | content 30 | .opacity(data.value) 31 | content 32 | .foregroundColor(color) 33 | .opacity(data.invValue) 34 | .overlay( 35 | Rectangle().fill(Color.clear) 36 | .border(Color.accentColor.opacity(0.5), width: 1) 37 | ) 38 | } 39 | .animation(.spring(response: 1.2, dampingFraction: 0.6, blendDuration: 0.9).delay(Double(data.index) * 0.10), value: data.value) 40 | .scaleEffect(data.scale, anchor: .bottom) 41 | .rotationEffect(Angle(degrees: -360 * data.invValue)) 42 | .animation(.spring(response: 0.3, dampingFraction: 0.6, blendDuration: 0.9).delay(Double(data.index) * 0.10), value: data.value) 43 | } 44 | } 45 | 46 | struct CustomEffect_Previews: PreviewProvider { 47 | static var previews: some View { 48 | ATAnimateTextPreview() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /AnimateTextExample/Shared/TapAnimateTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TapAnimateTextView.swift 3 | // AnimateTextExample 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import AnimateText 11 | 12 | struct TapAnimateTextView: View { 13 | 14 | let type: ATUnitType 15 | let elements: [String] 16 | var userInfo: Any? = nil 17 | 18 | @State private var text: String = "" 19 | 20 | var body: some View { 21 | GeometryReader { proxy in 22 | VStack(alignment: .leading) { 23 | Spacer() 24 | ZStack(alignment: .leading) { 25 | AnimateText($text, type: type, userInfo: userInfo) 26 | .font(.custom("Helvetica SemiBold", size: 30)) 27 | .padding(.vertical) 28 | if text.isEmpty { 29 | VStack(alignment: .leading) { 30 | Text(String(describing: E.self)) 31 | .foregroundColor(Color.accentColor) 32 | .font(.custom("Helvetica SemiBold", size: 30)) 33 | .transition(.opacity) 34 | Text("Touch the screen.") 35 | .font(.callout) 36 | .opacity(0.3) 37 | .disabled(true) 38 | } 39 | } 40 | } 41 | .padding() 42 | .padding(.bottom, 50) 43 | Spacer() 44 | } 45 | } 46 | .clipped() 47 | .contentShape(Rectangle()) 48 | .onTapGesture { 49 | changeText() 50 | } 51 | .onAppear { 52 | text = "" 53 | } 54 | } 55 | 56 | private func changeText() { 57 | let newText = self.elements[Int.random(in: (0..(type: .letters, elements: ["AnimateText"]) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /AnimateTextExample/macOS/macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 jasu 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. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "AnimateText", 8 | platforms: [ 9 | .iOS(.v14), 10 | .macOS(.v11) 11 | ], 12 | products: [ 13 | // Products define the executables and libraries a package produces, and make them visible to other packages. 14 | .library( 15 | name: "AnimateText", 16 | targets: ["AnimateText"]), 17 | ], 18 | dependencies: [ 19 | // Dependencies declare other packages that this package depends on. 20 | // .package(url: /* package url */, from: "1.0.0"), 21 | ], 22 | targets: [ 23 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 24 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 25 | .target( 26 | name: "AnimateText", 27 | dependencies: []), 28 | .testTarget( 29 | name: "AnimateTextTests", 30 | dependencies: ["AnimateText"]), 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **AnimateText for SwiftUI** 2 | This library for animating text. Developed with SwiftUI. This library supports iOS/macOS. 3 | 4 | [![Platforms](https://img.shields.io/badge/Platforms-iOS%20%7C%20macOS-blue?style=flat-square)](https://developer.apple.com/macOS) 5 | [![iOS](https://img.shields.io/badge/iOS-14.0-blue.svg)](https://developer.apple.com/iOS) 6 | [![macOS](https://img.shields.io/badge/macOS-11.0-blue.svg)](https://developer.apple.com/macOS) 7 | [![instagram](https://img.shields.io/badge/instagram-@dev.fabula-orange.svg?style=flat-square)](https://www.instagram.com/dev.fabula) 8 | [![SPM](https://img.shields.io/badge/SPM-compatible-red?style=flat-square)](https://developer.apple.com/documentation/swift_packages/package/) 9 | [![MIT](https://img.shields.io/badge/licenses-MIT-red.svg)](https://opensource.org/licenses/MIT) 10 | 11 | ## Screenshot 12 | https://user-images.githubusercontent.com/1617304/153016287-73f15db7-b642-4c4d-afc7-b3ecb2a17640.mp4 13 | 14 | ## Example 15 | [https://fabulaapp.page.link/224](https://fabulaapp.page.link/224) 16 | 17 | ## Usages 18 | 1. AnimateText 19 | ```swift 20 | /// A view that animates binding text. Passing the effect type as a generic. 21 | /// struct AnimateText where E : ATTextAnimateEffect 22 | /// Binding the text to be expressed. 23 | @State var text: String = "AnimateText" 24 | 25 | /// The type used to split text. 26 | @State var type: ATUnitType = .letters 27 | 28 | /// Custom user info for the effect. 29 | @State var userInfo: Any? = nil 30 | 31 | AnimateText($text, type: type, userInfo: userInfo) 32 | 33 | ``` 34 | 35 | 2. Each effect only needs to conform to the ATTextAnimateEffect protocol. 36 | ```swift 37 | /// Custom animation effect. 38 | public struct CustomEffect: ATTextAnimateEffect { 39 | 40 | public var data: ATElementData 41 | public var userInfo: Any? 42 | 43 | public init(_ data: ATElementData, _ userInfo: Any?) { 44 | self.data = data 45 | self.userInfo = userInfo 46 | } 47 | 48 | public func body(content: Content) -> some View { 49 | content 50 | .opacity(data.value) 51 | .animation(.easeInOut.delay(Double(data.index) * 0.06), value: data.value) 52 | } 53 | } 54 | ``` 55 | 56 | 3. ATTextAnimateEffect protocol 57 | ```swift 58 | /// A protocol to implement text animation effects. 59 | public protocol ATTextAnimateEffect: ViewModifier { 60 | 61 | /// Informational data required for each element animation. 62 | var data: ATElementData { get } 63 | /// Custom user info for the effect. 64 | /// The effect maintains a strong reference to this object until it (the effect) is invalidated. This parameter may be nil. 65 | var userInfo: Any? { get } 66 | 67 | init(_ data: ATElementData, _ userInfo: Any?) 68 | } 69 | 70 | ``` 71 | ## Swift Package Manager 72 | The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. Once you have your Swift package set up, adding AnimateText as a dependency is as easy as adding it to the dependencies value of your Package.swift. 73 | 74 | ```swift 75 | dependencies: [ 76 | .package(url: "https://github.com/jasudev/AnimateText.git", .branch("main")) 77 | ] 78 | ``` 79 | 80 | ## Contact 81 | instagram : [@dev.fabula](https://www.instagram.com/dev.fabula) 82 | email : [dev.fabula@gmail.com](mailto:dev.fabula@gmail.com) 83 | 84 | ## License 85 | AnimateText is available under the MIT license. See the [LICENSE](LICENSE) file for more info. 86 | -------------------------------------------------------------------------------- /Sources/AnimateText/AnimateText.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimateText.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// A view that animates binding text. Passing the effect type as a generic. 29 | public struct AnimateText: View { 30 | 31 | /// Binding the text to be expressed. 32 | @Binding private var text: String 33 | 34 | /// The type used to split text. 35 | var type: ATUnitType = .letters 36 | 37 | /// Custom user info for the effect. 38 | var userInfo: Any? = nil 39 | 40 | /// Split text into individual elements. 41 | @State private var elements: Array = [] 42 | 43 | /// A value used for animation processing. A value between 0 and 1. 44 | @State private var value: Double = 0 45 | 46 | /// Used to re-create the view. 47 | @State private var toggle: Bool = false 48 | 49 | /// The first text is exposed as the default text. 50 | @State private var isChanged: Bool = false 51 | 52 | /// The size of the Text view. 53 | @State private var size: CGSize = .zero 54 | 55 | /// initialize `AnimateText` 56 | /// 57 | /// - Parameters: 58 | /// - text: Bind the text you want to express. 59 | /// - type: The type used to split text. `ATUnitType` 60 | /// - userInfo: Custom user info for the effect. 61 | /// 62 | public init(_ text: Binding, type: ATUnitType = .letters, userInfo: Any? = nil) { 63 | _text = text 64 | self.type = type 65 | self.userInfo = userInfo 66 | } 67 | 68 | public var body: some View { 69 | ZStack(alignment: .leading) { 70 | if !isChanged { 71 | Text(text) 72 | .lineLimit(1) 73 | .takeSize($size) 74 | }else { 75 | HStack(spacing: 0) { 76 | ForEach(Array(elements.enumerated()), id: \.offset) { index, element in 77 | let data = ATElementData(element: element, 78 | type: self.type, 79 | index: index, 80 | count: elements.count, 81 | value: value, 82 | size: size) 83 | if toggle { 84 | Text(element).modifier(E(data, userInfo)) 85 | }else { 86 | Text(element).modifier(E(data, userInfo)) 87 | } 88 | } 89 | } 90 | .fixedSize(horizontal: true, vertical: false) 91 | } 92 | } 93 | .onChange(of: text) { _ in 94 | withAnimation { 95 | value = 0 96 | getText(text) 97 | toggle.toggle() 98 | } 99 | self.isChanged = true 100 | DispatchQueue.main.async { 101 | value = 1 102 | } 103 | } 104 | } 105 | 106 | private func getText(_ text: String) { 107 | switch type { 108 | case .letters: 109 | self.elements = text.map { String($0) } 110 | case .words: 111 | var elements = [String]() 112 | text.components(separatedBy: " ").forEach{ 113 | elements.append($0) 114 | elements.append(" ") 115 | } 116 | elements.removeLast() 117 | self.elements = elements 118 | } 119 | } 120 | } 121 | 122 | struct AnimateText_Previews: PreviewProvider { 123 | static var previews: some View { 124 | ATAnimateTextPreview() 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATChainEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATChainEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Chain animation effect. 29 | public struct ATChainEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .rotationEffect(Angle.degrees(-270) * data.invValue, anchor: .topLeading) 43 | .animation(.spring().delay(Double(data.index) * 0.08), value: data.value) 44 | } 45 | } 46 | 47 | struct ATChainEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATChimeBellEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATChimeBellEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// ChimeBell animation effect. 29 | public struct ATChimeBellEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .animation(.easeIn.delay(Double(data.index) * 0.1), value: data.value) 43 | .rotation3DEffect(Angle(degrees: 180 * data.invValue), axis: (x: 1, y: 0, z: 0), anchor: .top, anchorZ: 0, perspective: 0.7) 44 | .animation(.spring(response: 0.6, dampingFraction: 0.6, blendDuration: 0.7).delay(Double(data.index) * 0.08), value: data.value) 45 | } 46 | } 47 | 48 | struct ATChimeBellEffect_Previews: PreviewProvider { 49 | static var previews: some View { 50 | ATAnimateTextPreview() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATCurtainEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATCurtainEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Curtain animation effect. 29 | public struct ATCurtainEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .rotationEffect(Angle.degrees(80) * data.invValue, anchor: .topLeading) 43 | .animation(.spring().delay(Double(data.index) * 0.08), value: data.value) 44 | } 45 | } 46 | 47 | struct ATCurtainEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATDropEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATDropEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Drop animation effect. 29 | public struct ATDropEffect: ATTextAnimateEffect { 30 | 31 | let randomSize: Double 32 | public var data: ATElementData 33 | public var userInfo: Any? 34 | 35 | public init(_ data: ATElementData, _ userInfo: Any?) { 36 | self.data = data 37 | self.userInfo = userInfo 38 | randomSize = data.size.width 39 | } 40 | 41 | public func body(content: Content) -> some View { 42 | content 43 | .scaleEffect(6 * data.invValue + 1) 44 | .rotation3DEffect(Angle(degrees: Double.random(in: -270...270) * data.invValue), axis: (x: 0, y: 0, z: 1)) 45 | .animation(.easeInOut.delay(Double(data.index) * 0.05), value: data.value) 46 | .opacity(data.value) 47 | .animation(.easeIn.delay(Double(data.index) * 0.05), value: data.value) 48 | .blur(radius: 26 - 26 * data.value) 49 | .animation(.spring().delay(Double(data.index) * 0.05), value: data.value) 50 | } 51 | } 52 | 53 | struct ATDropEffect_Previews: PreviewProvider { 54 | static var previews: some View { 55 | ATAnimateTextPreview() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATHangEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATHangEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Hang animation effect. 29 | public struct ATHangEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .animation(.easeIn.delay(Double(data.index) * 0.1), value: data.value) 43 | .rotation3DEffect(Angle(degrees: 180 * data.invValue), axis: (x: 0, y: 1, z: 1), anchor: .top) 44 | .animation(.spring(response: 0.5, dampingFraction: 0.4, blendDuration: 0.9).delay(Double(data.index) * 0.08), value: data.value) 45 | } 46 | } 47 | 48 | struct ATHangEffect_Previews: PreviewProvider { 49 | static var previews: some View { 50 | ATAnimateTextPreview() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATPaperEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATPaperEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Paper animation effect. 29 | public struct ATPaperEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .offset(x: data.size.height * data.invValue) 43 | .animation(.spring().delay(Double(data.index) * 0.06), value: data.value) 44 | .rotation3DEffect(Angle(degrees: -180 * data.invValue), axis: (x: 0, y: 1, z: 0), anchor: .leading, anchorZ: 0.5, perspective: 0.5) 45 | .animation(.easeInOut.delay(Double(data.index) * 0.05), value: data.value) 46 | } 47 | } 48 | 49 | struct ATPaperEffect_Previews: PreviewProvider { 50 | static var previews: some View { 51 | ATAnimateTextPreview() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATRandomTypoEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATRandomTypoEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Text random animation effect. 29 | public struct ATRandomTypoEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .modifier(ATRandomTypoAnimation(data)) 42 | .offset(x: 6 * data.invValue, y: 0) 43 | .animation(.spring(response: 0.3, dampingFraction: 0.7, blendDuration: 0.5).delay(Double(data.index) * 0.06), value: data.value) 44 | } 45 | } 46 | 47 | struct ATRandomTypoEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATSpringEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATSpringEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Spring animation effect. 29 | public struct ATSpringEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .animation(.easeIn.delay(Double(data.index) * 0.15), value: data.value) 43 | .rotation3DEffect(Angle(degrees: 180 * data.invValue), axis: (x: 0, y: 0, z: 1), anchor: .bottom) 44 | .animation(.spring(response: 0.5, dampingFraction: 0.4, blendDuration: 0.9).delay(Double(data.index) * 0.12), value: data.value) 45 | } 46 | } 47 | 48 | struct ATSpringEffect_Previews: PreviewProvider { 49 | static var previews: some View { 50 | ATAnimateTextPreview() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATTwistEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATTwistEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Twist animation effect. 29 | public struct ATTwistEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .scaleEffect(3 * data.invValue + 1) 42 | .blur(radius: 12 * data.invValue) 43 | .opacity(data.value) 44 | .offset(x: 30 * data.invValue, y: -50 * data.invValue) 45 | .animation(.easeInOut(duration: 1.0).delay(Double(data.index) * 0.09), value: data.value) 46 | .rotationEffect(Angle.degrees(360 * data.invValue)) 47 | .animation(.easeInOut(duration: 1.0).delay(Double(data.index) * 0.11), value: data.value) 48 | } 49 | } 50 | 51 | struct ATTwistEffect_Previews: PreviewProvider { 52 | static var previews: some View { 53 | ATAnimateTextPreview() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Advanced/ATTypoEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATTypoEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/08. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// A fixed text animation effect. 29 | public struct ATTypoEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public var baseText: String = "-" 35 | 36 | public init(_ data: ATElementData, _ userInfo: Any?) { 37 | self.data = data 38 | self.userInfo = userInfo 39 | if let info = userInfo as? [String: Any] { 40 | baseText = info["base"] as! String 41 | } 42 | } 43 | 44 | public func body(content: Content) -> some View { 45 | content 46 | .modifier(ATRandomTypoAnimation(data, base: baseText)) 47 | .offset(x: 6 * data.invValue, y: 0) 48 | .animation(.spring(response: 0.3, dampingFraction: 0.7, blendDuration: 0.5).delay(Double(data.index) * 0.06), value: data.value) 49 | } 50 | } 51 | 52 | struct ATTypoEffect_Previews: PreviewProvider { 53 | static var previews: some View { 54 | ATAnimateTextPreview() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATBlurEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATBlurEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Blur animation effect. 29 | public struct ATBlurEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .blur(radius: 20 - 20 * data.value) 43 | .animation(.easeInOut(duration: 0.6).delay(Double(data.index) * 0.06), value: data.value) 44 | } 45 | } 46 | 47 | struct ATBlurEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATBottomTopEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATBottomTopEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Bottom to top animation effect. 29 | public struct ATBottomTopEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .offset(x: 0, y: data.invValue * (data.size.height * 3.6) ) 43 | .animation(.spring().delay(Double(data.index) * 0.09), value: data.value) 44 | } 45 | } 46 | 47 | struct ATBottomTopEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATOffsetEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATOffsetEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Offset animation effect. 29 | public struct ATOffsetEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .offset(x: 0, y: Double.random(in: -100...100) * data.invValue) 43 | .animation(.easeInOut(duration: 0.4).delay(Double(data.index) * 0.06), value: data.value) 44 | } 45 | } 46 | 47 | struct ATOffsetEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATOpacityEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATOpacityEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Opacity animation effect. 29 | public struct ATOpacityEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .animation(.easeInOut.delay(Double(data.index) * 0.06), value: data.value) 43 | } 44 | } 45 | 46 | struct ATOpacityEffect_Previews: PreviewProvider { 47 | static var previews: some View { 48 | ATAnimateTextPreview() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATRotateEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATRotateEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Rotation animation effect. 29 | public struct ATRotateEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .rotationEffect(Angle(degrees: 460 * data.invValue)) 43 | .animation(.easeInOut.delay(Double(data.index) * 0.06), value: data.value) 44 | } 45 | } 46 | 47 | struct ATRotateEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATScaleEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATScaleEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Scale animation effect. 29 | public struct ATScaleEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .scaleEffect(data.scale) 43 | .animation(.easeInOut(duration: 0.4).delay(Double(data.index) * 0.06), value: data.value) 44 | } 45 | } 46 | 47 | struct ATScaleEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATSlideEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATSlideEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Slide animation effect. 29 | public struct ATSlideEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .offset(x: data.size.width * data.invValue, y: 0) 43 | .animation(.spring().delay(Double(data.index) * 0.05), value: data.value) 44 | } 45 | } 46 | 47 | struct ATSlideEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/AnimateText/Effects/Basic/ATTopBottomEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATTopBottomEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Top to bottom animation effect. 29 | public struct ATTopBottomEffect: ATTextAnimateEffect { 30 | 31 | public var data: ATElementData 32 | public var userInfo: Any? 33 | 34 | public init(_ data: ATElementData, _ userInfo: Any?) { 35 | self.data = data 36 | self.userInfo = userInfo 37 | } 38 | 39 | public func body(content: Content) -> some View { 40 | content 41 | .opacity(data.value) 42 | .offset(x: 0, y: data.invValue * -(data.size.height * 3.6) ) 43 | .animation(.spring().delay(Double(data.index) * 0.09), value: data.value) 44 | } 45 | } 46 | 47 | struct ATTopBottomEffect_Previews: PreviewProvider { 48 | static var previews: some View { 49 | ATAnimateTextPreview() 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Sources/AnimateText/Extensions/View+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Extensions.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/08. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | extension View { 29 | func takeSize(_ size: Binding) -> some View { 30 | self.modifier(SizeModifier(size)) 31 | } 32 | } 33 | 34 | struct SizeModifier: ViewModifier { 35 | 36 | @Binding var size: CGSize 37 | 38 | init(_ size: Binding) { 39 | _size = size 40 | } 41 | 42 | func body(content: Content) -> some View { 43 | content 44 | .background( 45 | GeometryReader { proxy in 46 | Color.clear.preference(key: SizePreferenceKey.self, value: proxy.size) 47 | } 48 | ) 49 | .onPreferenceChange(SizePreferenceKey.self) { preference in 50 | self.size = preference 51 | } 52 | } 53 | } 54 | 55 | struct SizePreferenceKey: PreferenceKey { 56 | typealias V = CGSize 57 | static var defaultValue: V = .zero 58 | static func reduce(value: inout V, nextValue: () -> V) { 59 | value = nextValue() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/AnimateText/Modifiers/ATRandomTypoAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATRandomTypoAnimation.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// A modifier that randomly expresses text through animation. 29 | public struct ATRandomTypoAnimation: ATTextAnimatable { 30 | 31 | private var length: Int 32 | private var base: String 33 | 34 | public var data: ATElementData 35 | public var animatableData: Double { 36 | get { data.value } 37 | set { 38 | data.value = newValue 39 | } 40 | } 41 | 42 | /// initialize `ATRandomTypoAnimation` 43 | /// 44 | /// - Parameters: 45 | /// - data: Informational data required for each element animation. 46 | /// - length: Maximum number of characters to be displayed at random. 47 | /// - base: Characters to be displayed at random. 48 | /// 49 | public init(_ data: ATElementData, length: Int = 2, base: String? = nil) { 50 | self.data = data 51 | self.length = data.element.count + length 52 | self.base = base ?? "!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + "--------------------------------------------------------------------------------------------------------" 53 | } 54 | 55 | public func body(content: Content) -> some View { 56 | narrowerText(data.value) 57 | .fixedSize() 58 | } 59 | 60 | /// A method of randomly processing text with animation. 61 | /// 62 | /// - Parameters: 63 | /// - value: A value used in animation, between 0 and 1. 64 | /// 65 | private func narrowerText(_ value: Double) -> some View { 66 | let firstText = value >= 0.5 ? Text(data.element) : Text("") 67 | let random = randomText(Int((1.0 - value) * Double(length))) 68 | return firstText + Text(random) 69 | } 70 | 71 | /// A method that randomly returns text. 72 | /// 73 | /// - Parameters: 74 | /// - length: The number of letters to be returned randomly. The default value is 2 75 | /// 76 | private func randomText(_ length: Int = 2) -> String { 77 | var newValue: String = "" 78 | for _ in 0..: View { 30 | 31 | @State private var text: String = "Animate" 32 | 33 | let words = "Labyrinth,Ineffable,Incendiary,Ephemeral,Cynosure,Propinquity,Infatuation,Incandescent,Eudaemonia,Raconteur,Petrichor,Sumptuous,Aesthete,Nadir,Miraculous,Lassitude,Gossamer,Bungalow,Aurora,Inure,Mellifluous,Euphoria,Cherish,Demure,Elixir,Eternity,Felicity,Languor,Love,Solitude,Epiphany,Quintessential,Plethora,Nemesis,Lithe,Tranquility,Elegance,Renaissance,Eloquence,Sequoia,Peace,Lullaby,Paradox,Pristine,Effervescent,Opulence,Ethereal,Sanguine,Panacea,Bodacious,Axiom,Silhouette,Surreptitious,Ingenue,Dulcet,Tryst,Ebullience".components(separatedBy: ",") 34 | let sentence = "Don’t dwell on the past.,Believe in yourself.,Follow your heart.,Seize the day.,You only live once.,Past is just past.,Love yourself.,Don’t beat yourself up.,Life is a journey.,No Pain,No gain,No sweat,The die is cast.,When they go low,A friend is a second myself.,Appearances are deceptive.,Be brave.,Every cloud has a silver lining.,Don’t judge a book by its cover.,Hang in there.,This is how life is.,Live positive.,Seeing is believing.,He can do, She can do,Why not me,If not now,then when?,Respect individual.,Habit is a second nature.,Time is gold.,You deserve to be loved.,Love what you do.,Time waits for no one.,Don’t waste your youth.,Pain past is pleasure.,United we stand.,Envy and wrath shorten life.,Life is all about timing.".components(separatedBy: ",") 35 | 36 | @State private var type: ATUnitType = .letters 37 | 38 | public init() {} 39 | public var body: some View { 40 | GeometryReader { proxy in 41 | VStack(alignment: .leading) { 42 | Spacer() 43 | AnimateText($text, type: type) 44 | .font(.largeTitle) 45 | .padding() 46 | .background( 47 | RoundedRectangle(cornerRadius: 10) 48 | .fill(Color.blue.opacity(0.1)) 49 | ) 50 | .padding(.leading) 51 | Divider().padding() 52 | HStack { 53 | Spacer() 54 | Button { 55 | type = .letters 56 | text = self.words[Int.random(in: (0..() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Sources/AnimateText/Protocol/ATTextAnimatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATTextAnimatable.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/07. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// A protocol to implement text animation. 29 | public protocol ATTextAnimatable: AnimatableModifier { 30 | 31 | var data: ATElementData { get } 32 | var animatableData: Double { get } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Sources/AnimateText/Protocol/ATTextAnimateEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATTextAnimateEffect.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/05. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// The type used to split text 29 | public enum ATUnitType { 30 | /// Split into letters. 31 | case letters 32 | /// Split into words. 33 | case words 34 | } 35 | 36 | /// A protocol to implement text animation effects. 37 | public protocol ATTextAnimateEffect: ViewModifier { 38 | 39 | /// Informational data required for each element animation. 40 | var data: ATElementData { get } 41 | /// Custom user info for the effect. 42 | /// The effect maintains a strong reference to this object until it (the effect) is invalidated. This parameter may be nil. 43 | var userInfo: Any? { get } 44 | 45 | init(_ data: ATElementData, _ userInfo: Any?) 46 | } 47 | -------------------------------------------------------------------------------- /Sources/AnimateText/Protocol/Data/ATElementData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ATElementData.swift 3 | // AnimateText 4 | // 5 | // Created by jasu on 2022/02/06. 6 | // Copyright (c) 2022 jasu All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is furnished 13 | // to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 23 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | import SwiftUI 27 | 28 | /// Informational data required for each element animation. 29 | public struct ATElementData { 30 | 31 | /// Individual element `String`. 32 | public let element: String 33 | 34 | /// The type used to split text. 35 | public let type: ATUnitType 36 | 37 | /// The position of Element. 38 | public let index: Int 39 | 40 | /// The length of text. 41 | public let count: Int 42 | 43 | /// The value used for animation. (0 ~ 1) 44 | public var value: Double 45 | 46 | /// The size of the Text view. 47 | public var size: CGSize 48 | 49 | /// Correction to a value between 0.001 and 1.0. 50 | public var scale: Double { 51 | return correctValue(value) 52 | } 53 | 54 | /// Correction to a value between 1.0 and 0.001. 55 | public var invScale: Double { 56 | return correctValue(1.0 - value) 57 | } 58 | 59 | /// The inversion of the value used in the animation. (1 ~ 0) 60 | public var invValue: Double { 61 | return 1.0 - value 62 | } 63 | 64 | /// The value of the position of the current character in the entire text. 65 | /// A value between 0 and 1. 66 | public var locValue: Double { 67 | return correctValue(Double(index) / Double(count - 1)) 68 | } 69 | 70 | /// Correction to a value between 0.001 and 1.0. 71 | private func correctValue(_ value: Double) -> Double { 72 | let newValue = max(value, 0.001) 73 | return min(newValue, 1.0) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/AnimateTextTests/AnimateTextTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import AnimateText 3 | 4 | final class AnimateTextTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | // XCTAssertEqual(AnimateText().text, "Hello, World!") 10 | } 11 | } 12 | --------------------------------------------------------------------------------