├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Package.swift ├── README.md ├── SSSwiftUIAnimations.podspec ├── SSSwiftUIAnimations.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── SSSwiftUIAnimations ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Banner └── Banner.png ├── ContentView.swift ├── Examples ├── ExampleLRArrowView.swift ├── ExampleListModel.swift ├── ExampleListRow.swift ├── ExampleProgressView.swift ├── ExampleReactionAnimationView.swift ├── ExampleWaterProgressView.swift └── ExamplesList │ ├── CustomToolBar.swift │ ├── ExampleListModel.swift │ └── ExampleListRow.swift ├── GIFs ├── LRArrowView.gif ├── ProgressView.gif ├── ReactionAnimationView.gif └── WaterProgressView.gif ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── SSSwiftUIAnimationsApp.swift └── Sources ├── ArrowLeftRightAnimation ├── CircularView.swift ├── FilledStrokeCircle.swift ├── LRArrowAnimStyle.swift ├── LeftArrow.swift └── SSLRArrowView.swift ├── ProgressAnimation ├── ArrowView.swift ├── CheckView.swift ├── DownArrow.swift ├── ModelClass.swift ├── ProgressCircle.swift ├── ProgressView.swift └── WaveView.swift ├── ReactionAnimation ├── ReactionAnimationView.swift ├── ReactionAnimationViewModel.swift └── ReactionAnimationViewStyle.swift ├── Utils └── ViewExtension.swift └── WaterProgressAnimation ├── BubbleView.swift ├── CheckmarkView.swift ├── WaterCircleOutlineView.swift ├── WaterCircleView.swift ├── WaterProgressTextView.swift ├── WaterProgressView.swift └── WaterProgressViewStyle.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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Way to contribute 2 | 3 | 1. Fork the repo and create your branch from `master`. 4 | 2. Clone the project to your own machine. (Please have a look at __[README](README.md#installation)__ to understand how to run this project on your machine) 5 | 3. Commit changes to your own branch 6 | 4. Make sure your code lints. 7 | 5. Push your work back up to your fork. 8 | 6. Issue that pull request! 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 mobile-simformsolutions 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 | // 2 | // Package.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 11/11/24. 6 | // 7 | 8 | // swift-tools-version: 6.0 9 | import PackageDescription 10 | 11 | let package = Package( 12 | name: "SSSwiftUIAnimations", 13 | platforms: [ 14 | .iOS(.v15) // Specify minimum platform versions 15 | ], 16 | products: [ 17 | // Products define the executables and libraries a package produces and makes available to clients. 18 | .library( 19 | name: "SSSwiftUIAnimations", 20 | targets: ["SSSwiftUIAnimations"]), 21 | ], 22 | dependencies: [ 23 | // Dependencies declare other packages that this package depends on. 24 | ], 25 | targets: [ 26 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 27 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 28 | .target( 29 | name: "SSSwiftUIAnimations", 30 | dependencies: [], 31 | path: "SSSwiftUIAnimations" 32 | ) 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Banner](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/SSSwiftUIAnimations/Banner/Banner.png?raw=true) 2 | 3 | # SS-iOS-Animations 4 | 5 | SwiftUI animation library to bring your app to life. ✨ 6 | 7 | [![swiftUI](https://img.shields.io/badge/-swiftUI-blue)](https://developer.apple.com/documentation/swiftui) 8 | [![swift](https://img.shields.io/badge/-swift-blue)](https://developer.apple.com/swift/) 9 | [![swiftUI Animations](https://img.shields.io/badge/-animations-blue)](https://developer.apple.com/documentation/swiftui/animations) 10 | [![Twitter](https://img.shields.io/badge/Twitter-@simform-blue.svg?style=flat)](https://twitter.com/simform) 11 | 12 | 🚀 **SS-iOS-Animations**: Turn ordinary user interfaces into extraordinary experiences! With this library, you can make your app more fun 13 | and engaging by adding smooth and eye-catching animations to your app. Whether you're new to SwiftUI or a pro, our easy-to-use animations make it 14 | simple to add that extra wow factor to your app. Spice up your app with cool transitions, fun effects, and interactive touches, all thanks to 15 | **SS-iOS-Animations**. Try it out and take your SwiftUI apps to the next level! ✨ 16 | 17 | [Check out the Examples](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/tree/master/SSSwiftUIAnimations/Examples) 18 | 19 | ## Animations 20 | 21 | ### ProgressView ⏳ 22 | ![ProgressView](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/SSSwiftUIAnimations/GIFs/ProgressView.gif?raw=true) 23 | 24 | [**Code Link**](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/tree/master/SSSwiftUIAnimations/Sources/ProgressAnimation) | Animation Name: ProgressAnimation 25 | 26 | ### Left Right ArrowView ↔ 27 | ![LRArrowView](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/SSSwiftUIAnimations/GIFs/LRArrowView.gif?raw=true) 28 | 29 | [**Code Link**](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/tree/master/SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation) | Animation Name: ArrowLeftRightAnimation 30 | 31 | ### Water Effect ProgressView 🌊 32 | ![WaterEffectProgressView](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/SSSwiftUIAnimations/GIFs/WaterProgressView.gif?raw=true) 33 | 34 | [**Code Link**](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/tree/master/SSSwiftUIAnimations/Sources/WaterProgressAnimation) | Animation Name: WaterProgressAnimation 35 | 36 | ### Reaction Animation View 🌊 37 | ![ReactionAnimationView](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/SSSwiftUIAnimations/GIFs/ReactionAnimationView.gif?raw=true) 38 | 39 | [**Code Link**](https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/tree/master/SSSwiftUIAnimations/Sources/ReactionAnimation) | Animation Name: ReactionAnimation 40 | 41 | ## Installation 42 | **CocoaPods** 43 | 44 | - You can use CocoaPods to install SSSwiftUIAnimations by adding it to your Podfile. It will add all the Animation to your project: 45 | 46 | use_frameworks! 47 | pod 'SSSwiftUIAnimations' 48 | 49 | - For installing only particular Animation from the Pod. Use /AnimationName at the end. You can find the AnimationName at the bottom of Animations listed above.
**Example: pod 'SSSwiftUIAnimations/WaterProgressAnimation'** 50 | 51 | pod 'SSSwiftUIAnimations/[AnimationName]' 52 | 53 | - Then, wherever you want to use it, Just: 54 | 55 | import SwiftUI 56 | import SSSwiftUIAnimations 57 | 58 | **Swift Package Manager** 59 | 60 | - When using Xcode 11 or later, you can install `SSSwiftUIAnimations` through [Swift Package Manager](https://swift.org/package-manager/) by going to your Project settings > `Swift Packages` and add the repository by providing the GitHub URL. Alternatively, you can go to `File` > `Swift Packages` > `Add Package Dependencies...` 61 | 62 | **Manually** 63 | - Download and drop **SSSwiftUIAnimations** folder in your project. 64 | - Congratulations! 65 | 66 | ## Found these animations useful? :heart: 67 | 68 | Support it by joining [stargazers] :star: for this repository. 69 | 70 | ## How to Contribute :handshake: 71 | 72 | Whether you're helping us fix bugs, improve the docs, or a feature request, we'd love to have you! :muscle: \ 73 | Check out our __[Contributing Guide]__ for ideas on contributing. 74 | 75 | ## Bugs and Feedback 76 | 77 | For bugs, feature feature requests, and discussion use [GitHub Issues]. 78 | 79 | ## Checkout our Other Mobile Libraries 80 | 81 | Check out our other libraries [Awesome-Mobile-Libraries]. 82 | 83 | ## License 84 | 85 | Distributed under the MIT license. See [LICENSE] for details. 86 | 87 | 88 | 89 | 90 | [stargazers]: https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/stargazers 91 | 92 | [Contributing Guide]: https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/CONTRIBUTING.md 93 | 94 | [Awesome-Mobile-Libraries]: https://github.com/SimformSolutionsPvtLtd/Awesome-Mobile-Libraries 95 | 96 | [Github Issues]: https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/issues 97 | 98 | [license]: https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations/blob/master/LICENSE 99 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SSSwiftUIAnimations' 3 | s.version = '1.0.0' 4 | s.summary = 'With this library, you can make your app more fun and engaging by adding smooth and eye-catching animations to your app.' 5 | s.description = <<-DESC 6 | SS-iOS-Animations: Turn ordinary user interfaces into extraordinary experiences! With this library, you can make your app more fun and engaging by adding smooth and eye-catching animations to your app. Whether you're new to SwiftUI or a pro, our easy-to-use animations make it simple to add that extra wow factor to your app. Spice up your app with cool transitions, fun effects, and interactive touches, all thanks to SS-iOS-Animations. Try it out and take your SwiftUI apps to the next level! 7 | DESC 8 | s.homepage = 'https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations.git' 9 | s.license = { :type => 'MIT', :file => 'LICENSE' } 10 | s.author = { 'Rahul Yadav' => 'rahul.y@simformsolutions.com' } 11 | s.source = { :git => 'https://github.com/SimformSolutionsPvtLtd/SS-iOS-Animations.git', :tag => s.version.to_s } 12 | 13 | s.ios.deployment_target = '15.0' 14 | 15 | s.source_files = 'SSSwiftUIAnimations/Sources/**/*' 16 | 17 | s.subspec 'WaterProgressAnimation' do |waterprogressanimation| 18 | waterprogressanimation.source_files = 'SSSwiftUIAnimations/Sources/WaterProgressAnimation/**/*', 'SSSwiftUIAnimations/Sources/Utils/**/*' 19 | end 20 | 21 | s.subspec 'ProgressAnimation' do |progressanimation| 22 | progressanimation.source_files = 'SSSwiftUIAnimations/Sources/ProgressAnimation/**/*', 23 | 'SSSwiftUIAnimations/Sources/Utils/**/*' 24 | end 25 | 26 | s.subspec 'ArrowLeftRightAnimation' do |arrowleftrightanimation| 27 | arrowleftrightanimation.source_files = 'SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/**/*', 'SSSwiftUIAnimations/Sources/Utils/**/*' 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2BC2D8F328CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F228CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift */; }; 11 | 2BC2D8F528CF3A6F00CAB302 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F428CF3A6F00CAB302 /* ContentView.swift */; }; 12 | 2BC2D8F728CF3A7000CAB302 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F628CF3A7000CAB302 /* Assets.xcassets */; }; 13 | 2BC2D8FA28CF3A7000CAB302 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F928CF3A7000CAB302 /* Preview Assets.xcassets */; }; 14 | 4636F36E291E1BD600C8DB5B /* LeftArrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4636F36D291E1BD600C8DB5B /* LeftArrow.swift */; }; 15 | 469963A5290FCE3600DC01AD /* SSLRArrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469963A4290FCE3600DC01AD /* SSLRArrowView.swift */; }; 16 | 8FE1727C2CAA8BF100F8AB45 /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE1727A2CAA8BF100F8AB45 /* ViewExtension.swift */; }; 17 | 902200F62CDCC87E001DCC3A /* ReactionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 902200F52CDCC87E001DCC3A /* ReactionAnimationView.swift */; }; 18 | 902200F82CDCC951001DCC3A /* ReactionAnimationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 902200F72CDCC951001DCC3A /* ReactionAnimationViewModel.swift */; }; 19 | 902200FA2CDCCF23001DCC3A /* ExampleReactionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 902200F92CDCCF1C001DCC3A /* ExampleReactionAnimationView.swift */; }; 20 | 902201062CDE116E001DCC3A /* ReactionAnimationViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 902201052CDE1166001DCC3A /* ReactionAnimationViewStyle.swift */; }; 21 | 902201082CDE2293001DCC3A /* ReactionAnimationView.gif in Resources */ = {isa = PBXBuildFile; fileRef = 902201072CDE2293001DCC3A /* ReactionAnimationView.gif */; }; 22 | B10677FE2BE8D0D400957B4E /* DownArrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10677FD2BE8D0D400957B4E /* DownArrow.swift */; }; 23 | B1098E7D2BD94ED900BC19DD /* WaveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1098E7C2BD94ED900BC19DD /* WaveView.swift */; }; 24 | B11B983A2BCE9C3F00D76016 /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11B98392BCE9C3F00D76016 /* CheckView.swift */; }; 25 | B1343F722C060680009ACE90 /* ProgressView.gif in Resources */ = {isa = PBXBuildFile; fileRef = B1343F712C060680009ACE90 /* ProgressView.gif */; }; 26 | B1343F742C060686009ACE90 /* LRArrowView.gif in Resources */ = {isa = PBXBuildFile; fileRef = B1343F732C060686009ACE90 /* LRArrowView.gif */; }; 27 | B14AB36C2BC41B05004B09C4 /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14AB36B2BC41B05004B09C4 /* ProgressView.swift */; }; 28 | B153FD0C2BFB3C1800AEFE83 /* LRArrowAnimStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B153FD0B2BFB3C1800AEFE83 /* LRArrowAnimStyle.swift */; }; 29 | B153FD0E2BFB566000AEFE83 /* ExampleListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B153FD0D2BFB566000AEFE83 /* ExampleListRow.swift */; }; 30 | B153FD102BFB649300AEFE83 /* ExampleListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B153FD0F2BFB649300AEFE83 /* ExampleListModel.swift */; }; 31 | B153FD132BFB71F500AEFE83 /* FilledStrokeCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B153FD122BFB71F500AEFE83 /* FilledStrokeCircle.swift */; }; 32 | B153FD152BFB7A7900AEFE83 /* ExampleLRArrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B153FD142BFB7A7900AEFE83 /* ExampleLRArrowView.swift */; }; 33 | B15FD7992C04785700752CEA /* CustomToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15FD7982C04785700752CEA /* CustomToolBar.swift */; }; 34 | B177713F2BF39A60001723EC /* ModelClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B177713E2BF39A60001723EC /* ModelClass.swift */; }; 35 | B18792612AA5A0D2006F2CC9 /* CircularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18792602AA5A0D2006F2CC9 /* CircularView.swift */; }; 36 | B19E0B662BF7498700E65974 /* ExampleProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19E0B652BF7498700E65974 /* ExampleProgressView.swift */; }; 37 | B1DFCA512BF4FA3D00F01505 /* ProgressCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DFCA502BF4FA3D00F01505 /* ProgressCircle.swift */; }; 38 | B1DFCA532BF4FC7900F01505 /* ArrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DFCA522BF4FC7900F01505 /* ArrowView.swift */; }; 39 | B1EA09DE2C11A6B70024BC28 /* Banner.png in Resources */ = {isa = PBXBuildFile; fileRef = B1EA09DD2C11A6B70024BC28 /* Banner.png */; }; 40 | B717EC9D2C45488100555F90 /* CheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B717EC9C2C45488100555F90 /* CheckmarkView.swift */; }; 41 | B741A1442C4A6D610083399B /* WaterProgressView.gif in Resources */ = {isa = PBXBuildFile; fileRef = B741A1432C4A6D610083399B /* WaterProgressView.gif */; }; 42 | B741D9A62C46448200ABFCB4 /* WaterProgressTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B741D9A52C46448200ABFCB4 /* WaterProgressTextView.swift */; }; 43 | B780A9182C3D063500342512 /* WaterProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B780A9172C3D063500342512 /* WaterProgressView.swift */; }; 44 | B780A91A2C3D0BCB00342512 /* WaterProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B780A9192C3D0BCB00342512 /* WaterProgressViewStyle.swift */; }; 45 | B780A9242C3D7A7C00342512 /* WaterCircleOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B780A9232C3D7A7C00342512 /* WaterCircleOutlineView.swift */; }; 46 | B780A9262C3D806300342512 /* ExampleWaterProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B780A9252C3D806300342512 /* ExampleWaterProgressView.swift */; }; 47 | B780A9282C3D851700342512 /* WaterCircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B780A9272C3D851700342512 /* WaterCircleView.swift */; }; 48 | B7ECD58D2C452D8100B6A703 /* BubbleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7ECD58C2C452D8100B6A703 /* BubbleView.swift */; }; 49 | /* End PBXBuildFile section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 2BC2D8EF28CF3A6F00CAB302 /* SSSwiftUIAnimations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SSSwiftUIAnimations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 2BC2D8F228CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSSwiftUIAnimationsApp.swift; sourceTree = ""; }; 54 | 2BC2D8F428CF3A6F00CAB302 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 55 | 2BC2D8F628CF3A7000CAB302 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 2BC2D8F928CF3A7000CAB302 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 57 | 4636F36D291E1BD600C8DB5B /* LeftArrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftArrow.swift; sourceTree = ""; }; 58 | 469963A4290FCE3600DC01AD /* SSLRArrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSLRArrowView.swift; sourceTree = ""; }; 59 | 8F9765232CE20D5000034CF7 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 60 | 8FE1727A2CAA8BF100F8AB45 /* ViewExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = ""; }; 61 | 902200F52CDCC87E001DCC3A /* ReactionAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionAnimationView.swift; sourceTree = ""; }; 62 | 902200F72CDCC951001DCC3A /* ReactionAnimationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionAnimationViewModel.swift; sourceTree = ""; }; 63 | 902200F92CDCCF1C001DCC3A /* ExampleReactionAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleReactionAnimationView.swift; sourceTree = ""; }; 64 | 902201052CDE1166001DCC3A /* ReactionAnimationViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionAnimationViewStyle.swift; sourceTree = ""; }; 65 | 902201072CDE2293001DCC3A /* ReactionAnimationView.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = ReactionAnimationView.gif; sourceTree = ""; }; 66 | B10677FD2BE8D0D400957B4E /* DownArrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownArrow.swift; sourceTree = ""; }; 67 | B1098E7C2BD94ED900BC19DD /* WaveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveView.swift; sourceTree = ""; }; 68 | B11B98392BCE9C3F00D76016 /* CheckView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckView.swift; sourceTree = ""; }; 69 | B1343F712C060680009ACE90 /* ProgressView.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = ProgressView.gif; sourceTree = ""; }; 70 | B1343F732C060686009ACE90 /* LRArrowView.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = LRArrowView.gif; sourceTree = ""; }; 71 | B14AB36B2BC41B05004B09C4 /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; }; 72 | B153FD0B2BFB3C1800AEFE83 /* LRArrowAnimStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRArrowAnimStyle.swift; sourceTree = ""; }; 73 | B153FD0D2BFB566000AEFE83 /* ExampleListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleListRow.swift; sourceTree = ""; }; 74 | B153FD0F2BFB649300AEFE83 /* ExampleListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleListModel.swift; sourceTree = ""; }; 75 | B153FD122BFB71F500AEFE83 /* FilledStrokeCircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilledStrokeCircle.swift; sourceTree = ""; }; 76 | B153FD142BFB7A7900AEFE83 /* ExampleLRArrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleLRArrowView.swift; sourceTree = ""; }; 77 | B15FD7982C04785700752CEA /* CustomToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomToolBar.swift; sourceTree = ""; }; 78 | B177713E2BF39A60001723EC /* ModelClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelClass.swift; sourceTree = ""; }; 79 | B18792602AA5A0D2006F2CC9 /* CircularView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularView.swift; sourceTree = ""; }; 80 | B19E0B652BF7498700E65974 /* ExampleProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleProgressView.swift; sourceTree = ""; }; 81 | B1DFCA502BF4FA3D00F01505 /* ProgressCircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressCircle.swift; sourceTree = ""; }; 82 | B1DFCA522BF4FC7900F01505 /* ArrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowView.swift; sourceTree = ""; }; 83 | B1EA09DD2C11A6B70024BC28 /* Banner.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Banner.png; sourceTree = ""; }; 84 | B717EC9C2C45488100555F90 /* CheckmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkView.swift; sourceTree = ""; }; 85 | B741A1432C4A6D610083399B /* WaterProgressView.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = WaterProgressView.gif; sourceTree = ""; }; 86 | B741D9A52C46448200ABFCB4 /* WaterProgressTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterProgressTextView.swift; sourceTree = ""; }; 87 | B780A9172C3D063500342512 /* WaterProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterProgressView.swift; sourceTree = ""; }; 88 | B780A9192C3D0BCB00342512 /* WaterProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterProgressViewStyle.swift; sourceTree = ""; }; 89 | B780A9232C3D7A7C00342512 /* WaterCircleOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterCircleOutlineView.swift; sourceTree = ""; }; 90 | B780A9252C3D806300342512 /* ExampleWaterProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWaterProgressView.swift; sourceTree = ""; }; 91 | B780A9272C3D851700342512 /* WaterCircleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterCircleView.swift; sourceTree = ""; }; 92 | B7ECD58C2C452D8100B6A703 /* BubbleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BubbleView.swift; sourceTree = ""; }; 93 | /* End PBXFileReference section */ 94 | 95 | /* Begin PBXFrameworksBuildPhase section */ 96 | 2BC2D8EC28CF3A6F00CAB302 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXFrameworksBuildPhase section */ 104 | 105 | /* Begin PBXGroup section */ 106 | 2BC2D8E628CF3A6F00CAB302 = { 107 | isa = PBXGroup; 108 | children = ( 109 | 8F9765232CE20D5000034CF7 /* Package.swift */, 110 | 2BC2D8F128CF3A6F00CAB302 /* SSSwiftUIAnimations */, 111 | 2BC2D8F028CF3A6F00CAB302 /* Products */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | 2BC2D8F028CF3A6F00CAB302 /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 2BC2D8EF28CF3A6F00CAB302 /* SSSwiftUIAnimations.app */, 119 | ); 120 | name = Products; 121 | sourceTree = ""; 122 | }; 123 | 2BC2D8F128CF3A6F00CAB302 /* SSSwiftUIAnimations */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | B1DE99D72C060E1A006995FB /* Sources */, 127 | 2BC2D8F228CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift */, 128 | B153FD112BFB64BE00AEFE83 /* Examples */, 129 | B1EA09DC2C11A68A0024BC28 /* Banner */, 130 | B1343F702C06064B009ACE90 /* GIFs */, 131 | 2BC2D8F428CF3A6F00CAB302 /* ContentView.swift */, 132 | 2BC2D8F628CF3A7000CAB302 /* Assets.xcassets */, 133 | 2BC2D8F828CF3A7000CAB302 /* Preview Content */, 134 | ); 135 | path = SSSwiftUIAnimations; 136 | sourceTree = ""; 137 | }; 138 | 2BC2D8F828CF3A7000CAB302 /* Preview Content */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 2BC2D8F928CF3A7000CAB302 /* Preview Assets.xcassets */, 142 | ); 143 | path = "Preview Content"; 144 | sourceTree = ""; 145 | }; 146 | 469963A3290FCE1900DC01AD /* ArrowLeftRightAnimation */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 469963A4290FCE3600DC01AD /* SSLRArrowView.swift */, 150 | 4636F36D291E1BD600C8DB5B /* LeftArrow.swift */, 151 | B18792602AA5A0D2006F2CC9 /* CircularView.swift */, 152 | B153FD0B2BFB3C1800AEFE83 /* LRArrowAnimStyle.swift */, 153 | B153FD122BFB71F500AEFE83 /* FilledStrokeCircle.swift */, 154 | ); 155 | path = ArrowLeftRightAnimation; 156 | sourceTree = ""; 157 | }; 158 | 8FE1727B2CAA8BF100F8AB45 /* Utils */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 8FE1727A2CAA8BF100F8AB45 /* ViewExtension.swift */, 162 | ); 163 | path = Utils; 164 | sourceTree = ""; 165 | }; 166 | 902200F42CDCC860001DCC3A /* ReactionAnimation */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 902201052CDE1166001DCC3A /* ReactionAnimationViewStyle.swift */, 170 | 902200F52CDCC87E001DCC3A /* ReactionAnimationView.swift */, 171 | 902200F72CDCC951001DCC3A /* ReactionAnimationViewModel.swift */, 172 | ); 173 | path = ReactionAnimation; 174 | sourceTree = ""; 175 | }; 176 | B1343F702C06064B009ACE90 /* GIFs */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 902201072CDE2293001DCC3A /* ReactionAnimationView.gif */, 180 | B1343F712C060680009ACE90 /* ProgressView.gif */, 181 | B741A1432C4A6D610083399B /* WaterProgressView.gif */, 182 | B1343F732C060686009ACE90 /* LRArrowView.gif */, 183 | ); 184 | path = GIFs; 185 | sourceTree = ""; 186 | }; 187 | B14AB36A2BC40286004B09C4 /* ProgressAnimation */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | B14AB36B2BC41B05004B09C4 /* ProgressView.swift */, 191 | B1DFCA502BF4FA3D00F01505 /* ProgressCircle.swift */, 192 | B1DFCA522BF4FC7900F01505 /* ArrowView.swift */, 193 | B10677FD2BE8D0D400957B4E /* DownArrow.swift */, 194 | B1098E7C2BD94ED900BC19DD /* WaveView.swift */, 195 | B11B98392BCE9C3F00D76016 /* CheckView.swift */, 196 | B177713E2BF39A60001723EC /* ModelClass.swift */, 197 | ); 198 | path = ProgressAnimation; 199 | sourceTree = ""; 200 | }; 201 | B153FD112BFB64BE00AEFE83 /* Examples */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 902200F92CDCCF1C001DCC3A /* ExampleReactionAnimationView.swift */, 205 | B19E0B652BF7498700E65974 /* ExampleProgressView.swift */, 206 | B153FD142BFB7A7900AEFE83 /* ExampleLRArrowView.swift */, 207 | B780A9252C3D806300342512 /* ExampleWaterProgressView.swift */, 208 | B1F9ED332BFCD85000189871 /* ExamplesList */, 209 | ); 210 | path = Examples; 211 | sourceTree = ""; 212 | }; 213 | B1DE99D72C060E1A006995FB /* Sources */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | 902200F42CDCC860001DCC3A /* ReactionAnimation */, 217 | 8FE1727B2CAA8BF100F8AB45 /* Utils */, 218 | B780A9162C3D05CD00342512 /* WaterProgressAnimation */, 219 | B14AB36A2BC40286004B09C4 /* ProgressAnimation */, 220 | 469963A3290FCE1900DC01AD /* ArrowLeftRightAnimation */, 221 | ); 222 | path = Sources; 223 | sourceTree = ""; 224 | }; 225 | B1EA09DC2C11A68A0024BC28 /* Banner */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | B1EA09DD2C11A6B70024BC28 /* Banner.png */, 229 | ); 230 | path = Banner; 231 | sourceTree = ""; 232 | }; 233 | B1F9ED332BFCD85000189871 /* ExamplesList */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | B153FD0F2BFB649300AEFE83 /* ExampleListModel.swift */, 237 | B153FD0D2BFB566000AEFE83 /* ExampleListRow.swift */, 238 | B15FD7982C04785700752CEA /* CustomToolBar.swift */, 239 | ); 240 | path = ExamplesList; 241 | sourceTree = ""; 242 | }; 243 | B780A9162C3D05CD00342512 /* WaterProgressAnimation */ = { 244 | isa = PBXGroup; 245 | children = ( 246 | B780A9172C3D063500342512 /* WaterProgressView.swift */, 247 | B780A9192C3D0BCB00342512 /* WaterProgressViewStyle.swift */, 248 | B780A9232C3D7A7C00342512 /* WaterCircleOutlineView.swift */, 249 | B780A9272C3D851700342512 /* WaterCircleView.swift */, 250 | B7ECD58C2C452D8100B6A703 /* BubbleView.swift */, 251 | B717EC9C2C45488100555F90 /* CheckmarkView.swift */, 252 | B741D9A52C46448200ABFCB4 /* WaterProgressTextView.swift */, 253 | ); 254 | path = WaterProgressAnimation; 255 | sourceTree = ""; 256 | }; 257 | /* End PBXGroup section */ 258 | 259 | /* Begin PBXNativeTarget section */ 260 | 2BC2D8EE28CF3A6F00CAB302 /* SSSwiftUIAnimations */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = 2BC2D8FD28CF3A7000CAB302 /* Build configuration list for PBXNativeTarget "SSSwiftUIAnimations" */; 263 | buildPhases = ( 264 | 2BC2D8EB28CF3A6F00CAB302 /* Sources */, 265 | 2BC2D8EC28CF3A6F00CAB302 /* Frameworks */, 266 | 2BC2D8ED28CF3A6F00CAB302 /* Resources */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | ); 272 | name = SSSwiftUIAnimations; 273 | productName = SSSwiftUIAnimations; 274 | productReference = 2BC2D8EF28CF3A6F00CAB302 /* SSSwiftUIAnimations.app */; 275 | productType = "com.apple.product-type.application"; 276 | }; 277 | /* End PBXNativeTarget section */ 278 | 279 | /* Begin PBXProject section */ 280 | 2BC2D8E728CF3A6F00CAB302 /* Project object */ = { 281 | isa = PBXProject; 282 | attributes = { 283 | BuildIndependentTargetsInParallel = 1; 284 | LastSwiftUpdateCheck = 1340; 285 | LastUpgradeCheck = 1340; 286 | TargetAttributes = { 287 | 2BC2D8EE28CF3A6F00CAB302 = { 288 | CreatedOnToolsVersion = 13.4.1; 289 | }; 290 | }; 291 | }; 292 | buildConfigurationList = 2BC2D8EA28CF3A6F00CAB302 /* Build configuration list for PBXProject "SSSwiftUIAnimations" */; 293 | compatibilityVersion = "Xcode 13.0"; 294 | developmentRegion = en; 295 | hasScannedForEncodings = 0; 296 | knownRegions = ( 297 | en, 298 | Base, 299 | ); 300 | mainGroup = 2BC2D8E628CF3A6F00CAB302; 301 | productRefGroup = 2BC2D8F028CF3A6F00CAB302 /* Products */; 302 | projectDirPath = ""; 303 | projectRoot = ""; 304 | targets = ( 305 | 2BC2D8EE28CF3A6F00CAB302 /* SSSwiftUIAnimations */, 306 | ); 307 | }; 308 | /* End PBXProject section */ 309 | 310 | /* Begin PBXResourcesBuildPhase section */ 311 | 2BC2D8ED28CF3A6F00CAB302 /* Resources */ = { 312 | isa = PBXResourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | 2BC2D8FA28CF3A7000CAB302 /* Preview Assets.xcassets in Resources */, 316 | B741A1442C4A6D610083399B /* WaterProgressView.gif in Resources */, 317 | B1343F722C060680009ACE90 /* ProgressView.gif in Resources */, 318 | B1EA09DE2C11A6B70024BC28 /* Banner.png in Resources */, 319 | 2BC2D8F728CF3A7000CAB302 /* Assets.xcassets in Resources */, 320 | B1343F742C060686009ACE90 /* LRArrowView.gif in Resources */, 321 | 902201082CDE2293001DCC3A /* ReactionAnimationView.gif in Resources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | /* End PBXResourcesBuildPhase section */ 326 | 327 | /* Begin PBXSourcesBuildPhase section */ 328 | 2BC2D8EB28CF3A6F00CAB302 /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | B153FD0E2BFB566000AEFE83 /* ExampleListRow.swift in Sources */, 333 | 2BC2D8F528CF3A6F00CAB302 /* ContentView.swift in Sources */, 334 | B153FD102BFB649300AEFE83 /* ExampleListModel.swift in Sources */, 335 | B177713F2BF39A60001723EC /* ModelClass.swift in Sources */, 336 | B10677FE2BE8D0D400957B4E /* DownArrow.swift in Sources */, 337 | B1098E7D2BD94ED900BC19DD /* WaveView.swift in Sources */, 338 | B19E0B662BF7498700E65974 /* ExampleProgressView.swift in Sources */, 339 | B18792612AA5A0D2006F2CC9 /* CircularView.swift in Sources */, 340 | B153FD152BFB7A7900AEFE83 /* ExampleLRArrowView.swift in Sources */, 341 | 2BC2D8F528CF3A6F00CAB302 /* ContentView.swift in Sources */, 342 | 902200F62CDCC87E001DCC3A /* ReactionAnimationView.swift in Sources */, 343 | 4636F36E291E1BD600C8DB5B /* LeftArrow.swift in Sources */, 344 | B15FD7992C04785700752CEA /* CustomToolBar.swift in Sources */, 345 | 469963A5290FCE3600DC01AD /* SSLRArrowView.swift in Sources */, 346 | B153FD0C2BFB3C1800AEFE83 /* LRArrowAnimStyle.swift in Sources */, 347 | B717EC9D2C45488100555F90 /* CheckmarkView.swift in Sources */, 348 | B780A9282C3D851700342512 /* WaterCircleView.swift in Sources */, 349 | B153FD132BFB71F500AEFE83 /* FilledStrokeCircle.swift in Sources */, 350 | 2BC2D8F328CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift in Sources */, 351 | B741D9A62C46448200ABFCB4 /* WaterProgressTextView.swift in Sources */, 352 | 902201062CDE116E001DCC3A /* ReactionAnimationViewStyle.swift in Sources */, 353 | B780A9242C3D7A7C00342512 /* WaterCircleOutlineView.swift in Sources */, 354 | B1DFCA532BF4FC7900F01505 /* ArrowView.swift in Sources */, 355 | B780A9182C3D063500342512 /* WaterProgressView.swift in Sources */, 356 | B11B983A2BCE9C3F00D76016 /* CheckView.swift in Sources */, 357 | 902200F82CDCC951001DCC3A /* ReactionAnimationViewModel.swift in Sources */, 358 | B7ECD58D2C452D8100B6A703 /* BubbleView.swift in Sources */, 359 | 8FE1727C2CAA8BF100F8AB45 /* ViewExtension.swift in Sources */, 360 | B780A9262C3D806300342512 /* ExampleWaterProgressView.swift in Sources */, 361 | B780A91A2C3D0BCB00342512 /* WaterProgressViewStyle.swift in Sources */, 362 | 902200FA2CDCCF23001DCC3A /* ExampleReactionAnimationView.swift in Sources */, 363 | B1DFCA512BF4FA3D00F01505 /* ProgressCircle.swift in Sources */, 364 | B14AB36C2BC41B05004B09C4 /* ProgressView.swift in Sources */, 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXSourcesBuildPhase section */ 369 | 370 | /* Begin XCBuildConfiguration section */ 371 | 2BC2D8FB28CF3A7000CAB302 /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_ANALYZER_NONNULL = YES; 376 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 377 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 378 | CLANG_ENABLE_MODULES = YES; 379 | CLANG_ENABLE_OBJC_ARC = YES; 380 | CLANG_ENABLE_OBJC_WEAK = YES; 381 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 382 | CLANG_WARN_BOOL_CONVERSION = YES; 383 | CLANG_WARN_COMMA = YES; 384 | CLANG_WARN_CONSTANT_CONVERSION = YES; 385 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 387 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INFINITE_RECURSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 401 | CLANG_WARN_UNREACHABLE_CODE = YES; 402 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 403 | COPY_PHASE_STRIP = NO; 404 | DEBUG_INFORMATION_FORMAT = dwarf; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | ENABLE_TESTABILITY = YES; 407 | GCC_C_LANGUAGE_STANDARD = gnu11; 408 | GCC_DYNAMIC_NO_PIC = NO; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_OPTIMIZATION_LEVEL = 0; 411 | GCC_PREPROCESSOR_DEFINITIONS = ( 412 | "DEBUG=1", 413 | "$(inherited)", 414 | ); 415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 417 | GCC_WARN_UNDECLARED_SELECTOR = YES; 418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 419 | GCC_WARN_UNUSED_FUNCTION = YES; 420 | GCC_WARN_UNUSED_VARIABLE = YES; 421 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 422 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 423 | MTL_FAST_MATH = YES; 424 | ONLY_ACTIVE_ARCH = YES; 425 | SDKROOT = iphoneos; 426 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 427 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 428 | }; 429 | name = Debug; 430 | }; 431 | 2BC2D8FC28CF3A7000CAB302 /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ALWAYS_SEARCH_USER_PATHS = NO; 435 | CLANG_ANALYZER_NONNULL = YES; 436 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 437 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 438 | CLANG_ENABLE_MODULES = YES; 439 | CLANG_ENABLE_OBJC_ARC = YES; 440 | CLANG_ENABLE_OBJC_WEAK = YES; 441 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 442 | CLANG_WARN_BOOL_CONVERSION = YES; 443 | CLANG_WARN_COMMA = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 448 | CLANG_WARN_EMPTY_BODY = YES; 449 | CLANG_WARN_ENUM_CONVERSION = YES; 450 | CLANG_WARN_INFINITE_RECURSION = YES; 451 | CLANG_WARN_INT_CONVERSION = YES; 452 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 453 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 454 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 455 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 456 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 457 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 458 | CLANG_WARN_STRICT_PROTOTYPES = YES; 459 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 460 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 461 | CLANG_WARN_UNREACHABLE_CODE = YES; 462 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 463 | COPY_PHASE_STRIP = NO; 464 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 465 | ENABLE_NS_ASSERTIONS = NO; 466 | ENABLE_STRICT_OBJC_MSGSEND = YES; 467 | GCC_C_LANGUAGE_STANDARD = gnu11; 468 | GCC_NO_COMMON_BLOCKS = YES; 469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 | GCC_WARN_UNDECLARED_SELECTOR = YES; 472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 | GCC_WARN_UNUSED_FUNCTION = YES; 474 | GCC_WARN_UNUSED_VARIABLE = YES; 475 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 476 | MTL_ENABLE_DEBUG_INFO = NO; 477 | MTL_FAST_MATH = YES; 478 | SDKROOT = iphoneos; 479 | SWIFT_COMPILATION_MODE = wholemodule; 480 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 481 | VALIDATE_PRODUCT = YES; 482 | }; 483 | name = Release; 484 | }; 485 | 2BC2D8FE28CF3A7000CAB302 /* Debug */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 489 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 490 | CODE_SIGN_STYLE = Automatic; 491 | CURRENT_PROJECT_VERSION = 1; 492 | DEVELOPMENT_ASSET_PATHS = "\"SSSwiftUIAnimations/Preview Content\""; 493 | DEVELOPMENT_TEAM = K7XJG666ZW; 494 | ENABLE_PREVIEWS = YES; 495 | GENERATE_INFOPLIST_FILE = YES; 496 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 497 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 498 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 499 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 500 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 501 | LD_RUNPATH_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "@executable_path/Frameworks", 504 | ); 505 | MARKETING_VERSION = 1.0; 506 | PRODUCT_BUNDLE_IDENTIFIER = com.simform.SSSwiftUIAnimations; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | SWIFT_EMIT_LOC_STRINGS = YES; 509 | SWIFT_VERSION = 5.0; 510 | TARGETED_DEVICE_FAMILY = "1,2"; 511 | }; 512 | name = Debug; 513 | }; 514 | 2BC2D8FF28CF3A7000CAB302 /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 518 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 519 | CODE_SIGN_STYLE = Automatic; 520 | CURRENT_PROJECT_VERSION = 1; 521 | DEVELOPMENT_ASSET_PATHS = "\"SSSwiftUIAnimations/Preview Content\""; 522 | DEVELOPMENT_TEAM = K7XJG666ZW; 523 | ENABLE_PREVIEWS = YES; 524 | GENERATE_INFOPLIST_FILE = YES; 525 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 526 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 527 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 528 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 529 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 530 | LD_RUNPATH_SEARCH_PATHS = ( 531 | "$(inherited)", 532 | "@executable_path/Frameworks", 533 | ); 534 | MARKETING_VERSION = 1.0; 535 | PRODUCT_BUNDLE_IDENTIFIER = com.simform.SSSwiftUIAnimations; 536 | PRODUCT_NAME = "$(TARGET_NAME)"; 537 | SWIFT_EMIT_LOC_STRINGS = YES; 538 | SWIFT_VERSION = 5.0; 539 | TARGETED_DEVICE_FAMILY = "1,2"; 540 | }; 541 | name = Release; 542 | }; 543 | /* End XCBuildConfiguration section */ 544 | 545 | /* Begin XCConfigurationList section */ 546 | 2BC2D8EA28CF3A6F00CAB302 /* Build configuration list for PBXProject "SSSwiftUIAnimations" */ = { 547 | isa = XCConfigurationList; 548 | buildConfigurations = ( 549 | 2BC2D8FB28CF3A7000CAB302 /* Debug */, 550 | 2BC2D8FC28CF3A7000CAB302 /* Release */, 551 | ); 552 | defaultConfigurationIsVisible = 0; 553 | defaultConfigurationName = Release; 554 | }; 555 | 2BC2D8FD28CF3A7000CAB302 /* Build configuration list for PBXNativeTarget "SSSwiftUIAnimations" */ = { 556 | isa = XCConfigurationList; 557 | buildConfigurations = ( 558 | 2BC2D8FE28CF3A7000CAB302 /* Debug */, 559 | 2BC2D8FF28CF3A7000CAB302 /* Release */, 560 | ); 561 | defaultConfigurationIsVisible = 0; 562 | defaultConfigurationName = Release; 563 | }; 564 | /* End XCConfigurationList section */ 565 | }; 566 | rootObject = 2BC2D8E728CF3A6F00CAB302 /* Project object */; 567 | } 568 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/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 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/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" : "2x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "83.5x83.5" 82 | }, 83 | { 84 | "idiom" : "ios-marketing", 85 | "scale" : "1x", 86 | "size" : "1024x1024" 87 | } 88 | ], 89 | "info" : { 90 | "author" : "xcode", 91 | "version" : 1 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Banner/Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SS-iOS-Animations/055098a120e8355eba4e3591289680db9ac42125/SSSwiftUIAnimations/Banner/Banner.png -------------------------------------------------------------------------------- /SSSwiftUIAnimations/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Purva Ruparelia on 12/09/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | private var exampleList = ExampleListModel.exampleList 12 | 13 | var body: some View { 14 | NavigationView { 15 | ZStack { 16 | List{ 17 | ForEach(exampleList) {item in 18 | ExampleListRow(exampleListItem: item) 19 | } 20 | }.listStyle(.insetGrouped) 21 | .listRowSpacing(10) 22 | .listRowSeparator(.hidden) 23 | .customToolbar(title: "Examples", fontSize: 30, displayMode: .inline) 24 | } 25 | } 26 | } 27 | } 28 | 29 | struct ContentView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | ContentView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleLRArrowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleLRArrowView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleLRArrowView: View { 11 | var body: some View { 12 | SSLRArrowView(arrowSize: 10, arrowViewAnimStyle: LRArrowAnimStyle(circleSize: 100, strokeEmptyColor: .blue, strokeFillColor: .blue, arrowColor: .blue, circleStrokeSize: 5, arrowStrokeSize: 5), leftArrowViewTap: ({ 13 | print("left arrow tap") 14 | }), rightArrowViewTap: ({ 15 | print("right arrow tap") 16 | })).customToolbar(title: "ArrowLRView Example", fontSize: 17) 17 | } 18 | } 19 | 20 | #Preview { 21 | ExampleLRArrowView() 22 | } 23 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleListModel.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class ExampleListModel: Identifiable { 11 | 12 | // MARK: - Variables 13 | var id = UUID() 14 | var rowTitle: String 15 | var destinationView: AnyView 16 | 17 | // MARK: - init 18 | init(rowTitle: String, destinationView: AnyView) { 19 | self.rowTitle = rowTitle 20 | self.destinationView = destinationView 21 | } 22 | 23 | // data for example row list 24 | static let exampleList = [ExampleListModel(rowTitle: "ProgressView", destinationView: AnyView(ExampleProgressView())), ExampleListModel(rowTitle: "Left-RightArrowView", destinationView: AnyView(ExampleArrowAnimation()))] 25 | } 26 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleListRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleListRow.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleListRow: View { 11 | var exampleListItem: ExampleListModel 12 | 13 | var body: some View { 14 | NavigationLink(destination: exampleListItem.destinationView) { 15 | Text(exampleListItem.rowTitle) 16 | .font(Font.system(size: 16, weight: .bold)) 17 | .foregroundStyle(.indigo) 18 | }.listRowBackground( 19 | Capsule() 20 | .fill( 21 | LinearGradient( 22 | gradient: Gradient(colors: [Color.mint, Color.pink]), 23 | startPoint: .topLeading, 24 | endPoint: .bottomTrailing) 25 | ) 26 | .padding(.vertical, 2).padding(.horizontal, 0) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleProgressView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 17/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleProgressView: View { 11 | 12 | // MARK: - Variables 13 | @State private var progress: Float = 0.0 14 | @State var timer: Timer? 15 | 16 | var body: some View { 17 | VStack { 18 | SSProgressView(progress: $progress, style: SSProgressViewStyle(circleSize: 200, circleStrokeWidth: 5, arrowStrokeWidth: 5, progressTextColor: .pink, fillStrokeColor: .blue, arrowColor: .blue, allowCancelProgress: true), onProgressViewClick: { 19 | simulateDownloadProgress() 20 | }, onProgressCompletion: { 21 | timer?.invalidate() 22 | }, onCancelProgress: { 23 | timer?.invalidate() 24 | }) 25 | }.customToolbar(title: "ProgressView Example", fontSize: 17) 26 | } 27 | 28 | // MARK: - Private functions 29 | private func simulateDownloadProgress() { 30 | timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { timer in 31 | if progress >= 1.0 { 32 | timer.invalidate() 33 | } else { 34 | progress += 0.01 35 | } 36 | } 37 | } 38 | } 39 | 40 | #Preview { 41 | ExampleProgressView() 42 | } 43 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleReactionAnimationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleReactionAnimationView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Faaiz Daglawala on 07/11/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleReactionAnimationView: View { 11 | 12 | // MARK: - Variables 13 | 14 | var body: some View { 15 | VStack { 16 | SSReactionAnimationView( 17 | style: SSReactionAnimationViewStyle( 18 | outerCircleColor: .clear, 19 | innerCircleColor: .blue, 20 | defautHeartColor: .gray, 21 | selectedHeartColor: .red 22 | )) { isSelected in } 23 | .frame(width: 100, height: 100) 24 | } 25 | .customToolbar(title: "ReactionAnimationView Example", fontSize: 17) 26 | } 27 | 28 | } 29 | 30 | #Preview { 31 | ExampleReactionAnimationView() 32 | } 33 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExampleWaterProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleWaterProgressView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 09/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleWaterProgressView: View { 11 | 12 | // MARK: - Variables 13 | @State private var progress: Double = 0.0 14 | 15 | var body: some View { 16 | VStack { 17 | SSWaterProgressView(progress: $progress, 18 | showPercent: true, 19 | style: SSWaterProgressViewStyle( 20 | circleSize: 200, 21 | circleStrokeWidth: 10, 22 | progressFont: .system(size: 16, weight: .bold), 23 | progressTextColor: .black, 24 | emptyStrokeColor: .cyan.opacity(0.2), 25 | fillStrokeColor: .cyan, 26 | waterColor: .mint, 27 | showBubbles: true, 28 | bubbleColor: .white, 29 | checkMarkImg: "checkmark", 30 | checkMarkImgColor: .white 31 | ), 32 | onProgressCompletion: { print("Progress Completed") }) 33 | Spacer() 34 | .frame(height: 40) 35 | Slider(value: $progress) { 36 | Text("Slide to manage progress") 37 | } 38 | .frame(width: 150, alignment: .bottom) 39 | } 40 | .customToolbar(title: "Water ProgressView Example", fontSize: 17) 41 | } 42 | } 43 | 44 | #Preview { 45 | ExampleWaterProgressView() 46 | } 47 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExamplesList/CustomToolBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomToolBar.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 27/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | //custom view modifier for the toolbar 11 | struct CustomToolbarModifier: ViewModifier { 12 | var title: String 13 | var fontSize: CGFloat 14 | var color: Color 15 | var displayMode: NavigationBarItem.TitleDisplayMode = .inline 16 | 17 | func body(content: Content) -> some View { 18 | content 19 | .navigationBarTitleDisplayMode(displayMode) 20 | .toolbar { 21 | ToolbarItem(placement: .principal) { 22 | Text(title) 23 | .font(.system(size: fontSize, weight: .bold)) 24 | .foregroundColor(color) 25 | } 26 | } 27 | } 28 | } 29 | 30 | extension View { 31 | func customToolbar(title: String, fontSize: CGFloat, color: Color = .indigo, displayMode: NavigationBarItem.TitleDisplayMode = .inline) -> some View { 32 | self.modifier(CustomToolbarModifier(title: title, fontSize: fontSize, color: color)) 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExamplesList/ExampleListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleListModel.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class ExampleListModel: Identifiable { 11 | 12 | // MARK: - Variables 13 | var id = UUID() 14 | var rowTitle: String 15 | var destinationView: AnyView 16 | 17 | // MARK: - init 18 | init(rowTitle: String, destinationView: AnyView) { 19 | self.rowTitle = rowTitle 20 | self.destinationView = destinationView 21 | } 22 | 23 | // data for example row list 24 | static let exampleList = [ 25 | ExampleListModel(rowTitle: "ProgressView", destinationView: AnyView(ExampleProgressView())), 26 | ExampleListModel(rowTitle: "Arrow Left Right View", destinationView: AnyView(ExampleLRArrowView())), 27 | ExampleListModel(rowTitle: "Water Progress View", destinationView: AnyView(ExampleWaterProgressView())), 28 | ExampleListModel(rowTitle: "ReactionAnimaionView", destinationView: AnyView(ExampleReactionAnimationView())) 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Examples/ExamplesList/ExampleListRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleListRow.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExampleListRow: View { 11 | var exampleListItem: ExampleListModel 12 | 13 | var body: some View { 14 | NavigationLink(destination: exampleListItem.destinationView) { 15 | Text(exampleListItem.rowTitle) 16 | .font(Font.system(size: 16, weight: .bold)) 17 | .foregroundStyle(.indigo) 18 | }.listRowBackground( 19 | Capsule() 20 | .fill( 21 | LinearGradient( 22 | gradient: Gradient(colors: [Color.mint, Color.pink]), 23 | startPoint: .topLeading, 24 | endPoint: .bottomTrailing) 25 | ) 26 | .padding(.vertical, 2).padding(.horizontal, 0) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/GIFs/LRArrowView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SS-iOS-Animations/055098a120e8355eba4e3591289680db9ac42125/SSSwiftUIAnimations/GIFs/LRArrowView.gif -------------------------------------------------------------------------------- /SSSwiftUIAnimations/GIFs/ProgressView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SS-iOS-Animations/055098a120e8355eba4e3591289680db9ac42125/SSSwiftUIAnimations/GIFs/ProgressView.gif -------------------------------------------------------------------------------- /SSSwiftUIAnimations/GIFs/ReactionAnimationView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SS-iOS-Animations/055098a120e8355eba4e3591289680db9ac42125/SSSwiftUIAnimations/GIFs/ReactionAnimationView.gif -------------------------------------------------------------------------------- /SSSwiftUIAnimations/GIFs/WaterProgressView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SS-iOS-Animations/055098a120e8355eba4e3591289680db9ac42125/SSSwiftUIAnimations/GIFs/WaterProgressView.gif -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/SSSwiftUIAnimationsApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSSwiftUIAnimationsApp.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Purva Ruparelia on 12/09/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct SSSwiftUIAnimationsApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/CircularView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircularView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 04/09/23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CircularView: View { 11 | 12 | // MARK: - Variables 13 | 14 | // End progress of circle stroke 15 | @State private var endProgress: CGFloat = 0.0 16 | 17 | // Arrow click state manage 18 | @State private var isArrowClicked = false 19 | 20 | // Arrow offset manage 21 | @State private var arrowOffset: CGFloat = 2 22 | 23 | // Start progress of circle stroke 24 | @State private var startProgress: CGFloat = 0.0 25 | 26 | // State manage for animation completed 27 | @State private var isAnimationCompleted = false 28 | 29 | // Manage immediate click 30 | @State private var isDisabled = false 31 | 32 | // Arrow size 33 | @State var arrowSize: CGFloat 34 | 35 | // Custom animation style 36 | var arrowAnimStyle: LRArrowAnimStyle 37 | 38 | // Tap callback on view tap 39 | var tapCallBack: (() -> Void)? 40 | 41 | // Animation duration 42 | var animationDuration = 0.4 43 | 44 | // MARK: - Body view 45 | var body: some View { 46 | ZStack { 47 | 48 | //Empty Stroke circle view 49 | Circle() 50 | .stroke( 51 | arrowAnimStyle.strokeEmptyColor 52 | .opacity(0.3), 53 | lineWidth: arrowAnimStyle.circleStrokeSize 54 | ) 55 | .frame(width: arrowAnimStyle.circleSize, height: arrowAnimStyle.circleSize) 56 | 57 | // Animate circle stroke from center left to center right of top circle stroke 58 | animatedCircle() 59 | .rotationEffect(.degrees(.zero)) 60 | 61 | // Animate circle stroke from center left to center right of bottom circle stroke 62 | animatedCircle() 63 | .rotation3DEffect( 64 | .degrees(180), axis: (x: 1, y: .zero, z: .zero) 65 | ) 66 | 67 | // Left arrow with animation 68 | LeftArrow() 69 | .stroke(style: StrokeStyle(lineWidth: arrowAnimStyle.arrowStrokeSize, lineCap: .round, lineJoin: .round)) 70 | .frame( 71 | width: isArrowClicked ? .zero : arrowSize, 72 | height: isArrowClicked ? .zero : arrowSize 73 | ) 74 | .offset(x: arrowOffset) 75 | .animation(.linear(duration: 0.3), value: isArrowClicked) 76 | .foregroundColor(arrowAnimStyle.arrowColor) 77 | .onTapGesture { 78 | animateView() 79 | isDisabled = true 80 | 81 | // Callback for tap gesture 82 | tapCallBack?() 83 | 84 | // For disabling immediate click 85 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.1) { 86 | isDisabled = false 87 | } 88 | } 89 | }.onAppear { 90 | adjustArrowSize() 91 | 92 | // Arrow's x offset to adjust arrow in center 93 | arrowOffset = arrowSize / 4.5 94 | }.disabled(isDisabled) 95 | } 96 | 97 | // MARK: - Private functions 98 | private func animatedCircle() -> some View { 99 | return FilledStrokeCircle(isAnimationCompleted: $isAnimationCompleted, startProgress: $startProgress, endProgress: $endProgress, arrowStyle: arrowAnimStyle) 100 | } 101 | 102 | private func animateView() { 103 | isArrowClicked = true 104 | startProgress = 0.5 105 | isAnimationCompleted = false 106 | arrowOffset = -(arrowAnimStyle.circleSize / 2) 107 | 108 | // Animate circle stroke 109 | Timer.scheduledTimer(withTimeInterval: animationDuration * 0.950, repeats: false) { _ in 110 | endProgress = arrowAnimStyle.circleSize / 2 111 | arrowOffset = arrowAnimStyle.circleSize / 2 112 | isAnimationCompleted = true 113 | } 114 | 115 | // Adjusting arrow back animation 116 | Timer.scheduledTimer(withTimeInterval: animationDuration * 2, repeats: false) { _ in 117 | withAnimation(Animation.linear(duration: animationDuration * 0.25)) { 118 | startProgress = endProgress 119 | endProgress = .zero 120 | } 121 | } 122 | 123 | // Adjust arrow position when animation is completed 124 | Timer.scheduledTimer(withTimeInterval: animationDuration * 2.25, repeats: false) { _ in 125 | withAnimation(Animation.linear(duration: animationDuration * 1.7)) { 126 | isArrowClicked = false 127 | arrowOffset = arrowSize / 4 128 | isAnimationCompleted = false 129 | } 130 | } 131 | 132 | // Manage circle stoke animation when animation is about to complete 133 | Timer.scheduledTimer(withTimeInterval: animationDuration * 3, repeats: false) { _ in 134 | isArrowClicked = false 135 | isAnimationCompleted = true 136 | } 137 | } 138 | 139 | // Validate and adjust minimum arrow size 140 | private func adjustArrowSize() { 141 | if arrowSize < arrowAnimStyle.circleSize / 4 { 142 | arrowSize = arrowAnimStyle.circleSize / 4 143 | } else if arrowSize >= arrowAnimStyle.circleSize { 144 | arrowSize = arrowAnimStyle.circleSize/1.2 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/FilledStrokeCircle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilledStrokeCircle.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FilledStrokeCircle: View { 11 | 12 | // MARK: - Variables 13 | @Binding var isAnimationCompleted: Bool 14 | @Binding var startProgress: CGFloat 15 | @Binding var endProgress: CGFloat 16 | let arrowStyle: LRArrowAnimStyle 17 | 18 | // MARK: - Body view 19 | var body: some View { 20 | Circle() 21 | .trim( 22 | from: isAnimationCompleted ? startProgress : 0.5, 23 | to: isAnimationCompleted ? endProgress : 0.5 24 | ) 25 | .stroke( 26 | arrowStyle.strokeFillColor, 27 | style: StrokeStyle( 28 | lineWidth: arrowStyle.circleStrokeSize, 29 | lineCap: .butt, 30 | lineJoin: .round 31 | ) 32 | ) 33 | .frame(width: arrowStyle.circleSize, height: arrowStyle.circleSize) 34 | .animation(.linear(duration: 1.6) 35 | .speed(0.08), value: endProgress) 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/LRArrowAnimStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LRArrowAnimStyle.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 20/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct LRArrowAnimStyle { 11 | 12 | // MARK: - Variables 13 | 14 | // Arrow size 15 | var arrowSize: CGFloat 16 | 17 | // Circle size 18 | var circleSize: CGFloat 19 | 20 | // Empty stroke color for circle 21 | var strokeEmptyColor: Color 22 | 23 | // Fill stroke color for circle 24 | var strokeFillColor: Color 25 | 26 | // Arrow color 27 | var arrowColor: Color 28 | 29 | // Circle stroke size 30 | var circleStrokeSize: CGFloat 31 | 32 | // Arrow stroke size 33 | var arrowStrokeSize: CGFloat 34 | 35 | init( 36 | arrowSize: CGFloat = 20, 37 | circleSize: CGFloat = 80, 38 | strokeEmptyColor: Color = .brown, 39 | strokeFillColor: Color = .brown, 40 | arrowColor: Color = .brown, 41 | circleStrokeSize: CGFloat = 1, 42 | arrowStrokeSize: CGFloat = 1 43 | ) { 44 | self.arrowSize = max(arrowSize, 10) 45 | self.circleSize = max(circleSize, 50) 46 | self.strokeEmptyColor = strokeEmptyColor 47 | self.strokeFillColor = strokeFillColor 48 | self.arrowColor = arrowColor 49 | self.circleStrokeSize = max(min(circleStrokeSize, circleSize/1.2), 1) 50 | self.arrowStrokeSize = max(min(arrowStrokeSize, circleSize/5), 1) 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/LeftArrow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftArrow.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 11/11/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LeftArrow: Shape { 11 | func path(in rect: CGRect) -> Path { 12 | var path = Path() 13 | path.move(to: CGPoint(x: rect.midX, y: rect.minY)) 14 | path.addLine(to: CGPoint(x: rect.minX, y: rect.midY)) 15 | path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY)) 16 | return path 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ArrowLeftRightAnimation/SSLRArrowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSLRArrowView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 31/10/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct SSLRArrowView: View { 11 | 12 | // MARK: - Variables 13 | 14 | // arrow size 15 | var arrowSize: CGFloat = 20 16 | 17 | // Custom arrow animation style 18 | var arrowViewAnimStyle: LRArrowAnimStyle = LRArrowAnimStyle() 19 | 20 | // Callback for left arrow view tap 21 | var leftArrowViewTap: (() -> Void)? 22 | 23 | // Callback for right arrow view tap 24 | var rightArrowViewTap: (() -> Void)? 25 | 26 | // MARK: - Body view 27 | public var body: some View { 28 | HStack(spacing: 20 + arrowViewAnimStyle.circleStrokeSize) { 29 | 30 | // Left arrow view 31 | CircularView( 32 | arrowSize: arrowSize, 33 | arrowAnimStyle: arrowViewAnimStyle 34 | ) { 35 | leftArrowViewTap?() 36 | } 37 | 38 | // Right arrow view 39 | CircularView( 40 | arrowSize: arrowSize, 41 | arrowAnimStyle: arrowViewAnimStyle 42 | ) { 43 | rightArrowViewTap?() 44 | }.rotationEffect(Angle(degrees: 180)) 45 | .padding(.vertical, 30) 46 | } 47 | } 48 | 49 | // MARK: - Private functions 50 | private func CircleArrowView() -> some View { 51 | CircularView(arrowSize: arrowSize, arrowAnimStyle: arrowViewAnimStyle) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/ArrowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrowView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 15/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct Arrow: View { 11 | 12 | // MARK: - Variables 13 | var arrowStyle: ArrowViewParams 14 | var style: SSProgressViewStyle 15 | @Binding var progress: Float 16 | @Binding var bounceEffect: CGFloat 17 | @State var verticalLine = true 18 | 19 | var body: some View { 20 | VStack(spacing: 0) { 21 | 22 | // Vertical line of arrow 23 | RoundedRectangle(cornerRadius: style.circleStrokeWidth/2) 24 | .frame( 25 | width: arrowStyle.isAnimating ? style.circleStrokeWidth : style.arrowStrokeWidth + 1, 26 | height: arrowStyle.isAnimating ? (!arrowStyle.animationStarted ? style.circleStrokeWidth : style.circleStrokeWidth ) : style.circleSize * 0.40 27 | ).opacity((progress >= 0.001) ? 0 : 1) 28 | .foregroundColor(arrowStyle.isAnimating ? style.fillStrokeColor : style.arrowColor) 29 | .offset(y: arrowStyle.isAnimating ? (!arrowStyle.animationStarted ? -10 : -(style.circleSize/2)) : 0) 30 | 31 | if progress <= 0.0 { 32 | 33 | // if progress is less than 0.1 then showing down arrow animation 34 | DownArrow( 35 | initialAnim: arrowStyle.initialAnim, 36 | isDownward: arrowStyle.isDownward, 37 | progress: Float(progress), 38 | circleSize: style.circleSize, 39 | isAnimating: arrowStyle.isAnimating, bounceEffect: bounceEffect 40 | ) 41 | .stroke(style: StrokeStyle(lineWidth: style.arrowStrokeWidth, lineCap: .round)) 42 | .frame(width: 10, height: 50) 43 | .padding(.top, -50) 44 | .offset(y: arrowStyle.isAnimating ? 22 : .zero) 45 | .foregroundColor(style.arrowColor) 46 | .scaleEffect(CGSize(width: 1, height: arrowStyle.isAnimating ? 1 : style.circleSize * 0.005)) 47 | } else if progress > 0.0 && progress < 1.0 { 48 | 49 | // if progress is between >= 0.1 and < 1.0 starting wave animation 50 | WaveView( 51 | progress: $progress, 52 | arrowStrokeWidth: style.arrowStrokeWidth, 53 | width: style.circleSize 54 | ) 55 | .frame(width: 10, height: 30) 56 | .foregroundColor(style.arrowColor) 57 | } else if progress >= 1.0 { 58 | 59 | // if progress completed showing check mark 60 | CheckView( 61 | width: style.circleSize, 62 | arrowStrokeWidth: style.arrowStrokeWidth 63 | ) 64 | .padding(.top, -11) 65 | .offset(y: 22) 66 | .foregroundColor(style.arrowColor) 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/CheckView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 16/04/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CheckView: View { 11 | 12 | // MARK: - Variables 13 | 14 | // For drawing straight line before check mark 15 | @State private var x = 0.92 16 | @State private var y = 2.5 17 | 18 | // Width of line and check mark 19 | @State var width: CGFloat = 0 20 | 21 | // For scale and offset of check mark 22 | @State private var size: CGFloat = 0 23 | @State private var isAnimating = false 24 | @State private var straight: CGFloat = 0 25 | @State private var offset = 0 26 | 27 | var arrowStrokeWidth: CGFloat 28 | 29 | var body: some View { 30 | ZStack(alignment:.center) { 31 | VStack { 32 | Check(endPoint: CGPoint( 33 | x: isAnimating ? 0.3 : x, 34 | y: isAnimating ? 4.5 : y), 35 | height: size, 36 | straight: straight) 37 | .stroke(style: StrokeStyle(lineWidth: isAnimating ? arrowStrokeWidth / (size * 0.01) : arrowStrokeWidth, lineCap: .round)) 38 | .frame(width: width * 0.57, height: 105) 39 | .offset(CGSize(width: 0, height: isAnimating ? offset : 0)) 40 | }.scaleEffect( 41 | CGSize( 42 | width: isAnimating ? size * 0.01 : 1, 43 | height: isAnimating ? size * 0.01 : 1 44 | ), anchor: .center 45 | ) 46 | }.onAppear() { 47 | size = width 48 | offset = size <= 150 ? -10 : -1 49 | 50 | // For changing width once it turns from line to check mark 51 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 52 | withAnimation(.linear(duration: 0.5)) { 53 | isAnimating = true 54 | width = 65 55 | } 56 | } 57 | 58 | // For increasing the Y of check mark 59 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.1) { 60 | straight = 18.0 61 | } 62 | } 63 | } 64 | } 65 | 66 | struct Check: Shape { 67 | 68 | // MARK: - Variables 69 | var endPoint: CGPoint 70 | var height: CGFloat 71 | var straight: CGFloat 72 | 73 | // MARK: - Animatable data 74 | var animatableData: AnimatablePair { 75 | get { AnimatablePair(endPoint.x, endPoint.y) } 76 | set { 77 | endPoint.x = newValue.first 78 | endPoint.y = newValue.second 79 | } 80 | } 81 | 82 | func path(in rect: CGRect) -> Path { 83 | let rightLine = height < 150 ? 1.07 : 1.10 84 | let start = CGPoint( 85 | x: rect.midX * 0.23, 86 | y: rect.midY - 21 * endPoint.x - 0.2 87 | ) 88 | let end = CGPoint( 89 | x: rect.maxX/2, 90 | y: rect.maxY * endPoint.y / 8 91 | ) 92 | let start2 = CGPoint( 93 | x: rect.maxX/rightLine, 94 | y: rect.midY - 21 * endPoint.x - straight 95 | ) 96 | var path = Path() 97 | path.move(to: start) 98 | path.addLine(to: end) 99 | path.move(to: start2) 100 | path.addLine(to: end) 101 | return path 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/DownArrow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DownArrow.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 06/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DownArrow: Shape { 11 | 12 | // MARK: - Variables 13 | var initialAnim: CGFloat 14 | var isDownward: Bool 15 | var progress: Float 16 | var circleSize: CGFloat 17 | var isAnimating: Bool 18 | var bounceEffect: CGFloat 19 | 20 | var animatableData: AnimatablePair { 21 | get { AnimatablePair(initialAnim, bounceEffect)} 22 | 23 | set { 24 | self.initialAnim = newValue.first 25 | self.bounceEffect = newValue.second 26 | } 27 | } 28 | 29 | func path(in rect: CGRect) -> Path { 30 | var path = Path() 31 | let startX = rect.midX - circleSize * (isAnimating ? 0.12 : 0.13) 32 | let endX = rect.midX + circleSize * (isAnimating ? 0.12 : 0.12) 33 | let centerY = rect.midY 34 | var controlY: CGFloat 35 | 36 | if isDownward { 37 | controlY = centerY + (initialAnim * (rect.height / 2)) * (bounceEffect) 38 | } else { 39 | let bounceProgress = bounce(initialAnim) 40 | controlY = centerY - (bounceProgress * (rect.height / 4)) - (bounceEffect * 6) 41 | } 42 | 43 | let controlX = rect.midX 44 | path.move(to: CGPoint(x: startX, y: centerY)) 45 | 46 | if !isDownward { 47 | // Curve for bounce effect 48 | path.move(to: CGPoint(x: startX - circleSize * 0.1, y: centerY)) 49 | path.addQuadCurve( 50 | to: CGPoint(x: rect.maxX + circleSize * 0.21, y: centerY), 51 | control: CGPoint(x: controlX, y: controlY) 52 | ) 53 | } else if initialAnim <= 1.0 { 54 | // Down arrow 55 | path.addLine(to: CGPoint(x: rect.midX, y: controlY)) 56 | path.addLine(to: CGPoint(x: endX, y: centerY)) 57 | } else if progress < 0.1 { 58 | // Straight line 59 | path.move(to: CGPoint(x: startX - circleSize * 0.1, y: centerY)) 60 | path.addLine(to: CGPoint(x: rect.maxX + circleSize * 0.21, y: centerY)) 61 | } 62 | return path 63 | } 64 | 65 | // MARK: - Private functions 66 | 67 | // Function to apply bounce effect 68 | private func bounce(_ progress: CGFloat) -> CGFloat { 69 | let numberOfBounces = 1 70 | let bounces = CGFloat(numberOfBounces) 71 | return abs((circleSize * 0.01 - progress) * sin(progress * .pi * bounces)) 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/ModelClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelClass.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 14/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct SSProgressViewStyle { 11 | var circleSize: CGFloat 12 | var circleStrokeWidth: CGFloat 13 | var arrowStrokeWidth: CGFloat 14 | var fontSize: CGFloat 15 | var fontWeight: Font.Weight 16 | var progressTextColor: Color 17 | var emptyStrokeColor: Color 18 | var fillStrokeColor: Color 19 | var arrowColor: Color 20 | var allowCancelProgress: Bool 21 | 22 | init( 23 | circleSize: CGFloat = 200.0, 24 | circleStrokeWidth: CGFloat = 5.0, 25 | arrowStrokeWidth: CGFloat = 5.0, 26 | fontSize: CGFloat = 15.0, 27 | fontWeight: Font.Weight = .regular, 28 | progressTextColor: Color = Color.blue, 29 | emptyStrokeColor: Color = Color.blue.opacity(0.2), 30 | fillStrokeColor: Color = Color.blue, 31 | arrowColor: Color = Color.blue, 32 | allowCancelProgress: Bool = false 33 | ) { 34 | 35 | // Circle size 36 | self.circleSize = max(circleSize, 100) 37 | 38 | // Circle stroke width 39 | self.circleStrokeWidth = max(circleStrokeWidth, 1) 40 | 41 | // Arrow stroke width 42 | self.arrowStrokeWidth = max(arrowStrokeWidth, 1) 43 | 44 | // Progress text font size 45 | self.fontSize = fontSize 46 | 47 | // Progress text fontWeight 48 | self.fontWeight = fontWeight 49 | 50 | // Color of progress text percentage 51 | self.progressTextColor = progressTextColor 52 | 53 | // Circle's empty stroke's color 54 | self.emptyStrokeColor = emptyStrokeColor 55 | 56 | // Circle's fill stroke's color 57 | self.fillStrokeColor = fillStrokeColor 58 | 59 | // Arrow color 60 | self.arrowColor = arrowColor 61 | 62 | // want to allow cancellation of progress or not 63 | self.allowCancelProgress = allowCancelProgress 64 | } 65 | } 66 | 67 | struct ArrowViewParams { 68 | var isAnimating: Bool 69 | var initialAnim: CGFloat 70 | var isDownward: Bool 71 | var animationStarted: Bool 72 | var showVerticalLine: Bool 73 | var showPercent: Bool 74 | 75 | init( 76 | isAnimating: Bool = false, 77 | progress: Double = 0.0, 78 | doneAnimating: Bool = false, 79 | initialAnim: CGFloat = 1.0, 80 | isDownward: Bool = true, 81 | animationStarted: Bool = true, 82 | showVerticalLine: Bool = true, 83 | showPercent: Bool = false 84 | ) { 85 | self.isAnimating = isAnimating 86 | self.initialAnim = initialAnim 87 | self.isDownward = isDownward 88 | self.animationStarted = animationStarted 89 | self.showVerticalLine = showVerticalLine 90 | self.showPercent = showPercent 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/ProgressCircle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgressCircle.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 15/05/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProgressCircle: View { 11 | 12 | // MARK: - Variables 13 | @Binding var progress: Float 14 | var style: SSProgressViewStyle 15 | 16 | var body: some View { 17 | ZStack { 18 | Circle() 19 | .stroke(style.emptyStrokeColor, 20 | style: StrokeStyle(lineWidth: style.circleStrokeWidth, lineCap: .round)) 21 | .frame( 22 | width: style.circleSize, 23 | height: style.circleSize 24 | ) 25 | Circle() 26 | .trim(from: 0.0, to: CGFloat(progress)) 27 | .stroke(style.fillStrokeColor, 28 | style: StrokeStyle(lineWidth: style.circleStrokeWidth, lineCap: .round)) 29 | .frame( 30 | width: style.circleSize, 31 | height: style.circleSize 32 | ) 33 | .rotationEffect(.degrees(-90)) 34 | .transition(.slide) 35 | } 36 | } 37 | } 38 | 39 | struct ProgressText : View { 40 | 41 | // MARK: - Variables 42 | var showPercent: Bool 43 | @Binding var progress: Float 44 | var style: SSProgressViewStyle 45 | 46 | var body: some View { 47 | ZStack { 48 | if showPercent { 49 | Text("\(Int(progress * 100))%") 50 | .padding(.top, style.circleSize/2) 51 | .font(Font.system(size: style.fontSize, weight: style.fontWeight)) 52 | .foregroundColor(style.progressTextColor) 53 | } 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/ProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Shapes.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 08/04/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct SSProgressView: View { 11 | 12 | // MARK: - Variables 13 | 14 | // Dynamic progress while using progress view 15 | @Binding var progress: Float 16 | 17 | // For progressView customization 18 | @State var style: SSProgressViewStyle = SSProgressViewStyle() 19 | 20 | // Animation is started or not 21 | @State private var isAnimating = false 22 | 23 | // Starting initial animation before progress starts 24 | @State private var initialAnim: CGFloat = 1.0 25 | 26 | // Start/stop dot animation 27 | @State private var startDotAnim = false 28 | 29 | // Stating shape is down arrow or not 30 | @State private var isDownArrow = true 31 | 32 | // Hide/show progress percentage 33 | @State private var showPercent = false 34 | 35 | // Handling click on progress view 36 | @State private var canTap = true 37 | 38 | // For handling cancel and restart progress 39 | @State private var isStarted = true 40 | @State private var isReset = false 41 | 42 | @State private var initialTimer: Timer? 43 | @State private var bounceTimer: Timer? 44 | @State private var endTimer: Timer? 45 | 46 | // Call back for starting progress when user taps on progress view 47 | var onProgressViewClick: (() -> Void)? 48 | 49 | // Call back for finished animation 50 | var onProgressCompletion: (() -> Void)? 51 | 52 | // Call back for restarting animation in between 53 | var onCancelProgress: (() -> Void)? 54 | 55 | @State var bounceEffect: CGFloat = 1.0 56 | 57 | public var body: some View { 58 | ZStack { 59 | // Progress Circle with animation 60 | ProgressCircle(progress: $progress, style: style) 61 | .if(progress > 0.0) { 62 | $0.animation(.linear, value: progress) 63 | } 64 | 65 | // Progress percentage text view 66 | ProgressText(showPercent: showPercent, progress: $progress, style: style) 67 | 68 | // Arrow view parameters 69 | let arrowStyle = ArrowViewParams(isAnimating: isAnimating, progress: Double(progress), initialAnim: initialAnim, isDownward: isDownArrow, animationStarted: startDotAnim) 70 | 71 | // Arrow view 72 | Arrow(arrowStyle: arrowStyle, style: style, progress: $progress, bounceEffect: $bounceEffect) 73 | }.allowsHitTesting(canTap) 74 | .onTapGesture { 75 | if canTap && !(progress > 1.0) { 76 | manageAnimation() 77 | } 78 | } 79 | } 80 | 81 | // MARK: - Private functions 82 | 83 | // Starting initial animation 84 | private func startAnimation() { 85 | canTap = false 86 | resetProgress() 87 | startDownArrrowAnim() 88 | 89 | // Starting arrow's vertical line animation 90 | withAnimation(.linear(duration: 0.4)) { 91 | if !isAnimating { 92 | isAnimating.toggle() 93 | progressAnimation() 94 | } 95 | } 96 | 97 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 98 | withAnimation(.linear(duration: 0.7)) { 99 | bounceEffect = 2 100 | } 101 | } 102 | 103 | // Allowing view to reset properly before starting the progress again 104 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 105 | canTap = style.allowCancelProgress ? true : false 106 | } 107 | 108 | // Starting progress after initial animation is done 109 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 110 | onProgressViewClick?() 111 | } 112 | } 113 | 114 | private func manageAnimation() { 115 | if style.allowCancelProgress && !isReset { 116 | isStarted.toggle() 117 | if isStarted { 118 | // Cancel animation on clicking again 119 | resetProgress() 120 | onCancelProgress?() 121 | } else { 122 | startAnimation() 123 | } 124 | } else { 125 | isReset = false 126 | startAnimation() 127 | } 128 | } 129 | 130 | private func progressAnimation() { 131 | // Showing percentage text once initial animation is done 132 | initialTimer = Timer.scheduledTimer(withTimeInterval: 0.6, repeats: false) { timer in 133 | withAnimation(Animation.linear(duration: 0.2)) { 134 | showPercent = true 135 | startDotAnim = true 136 | } 137 | } 138 | 139 | DispatchQueue.main.asyncAfter(deadline: .now()) { 140 | endTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in 141 | 142 | // Reset progress after progress is completed 143 | if progress >= 1.0 { 144 | timer.invalidate() 145 | 146 | // Hide percentage text 147 | withAnimation(Animation.easeInOut) { 148 | showPercent = false 149 | } 150 | 151 | // Reset progress with animation 152 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 153 | withAnimation { 154 | resetProgress() 155 | isReset = true 156 | canTap = true 157 | } 158 | } 159 | onProgressCompletion?() 160 | } 161 | } 162 | } 163 | } 164 | 165 | private func startDownArrrowAnim() { 166 | var animationIndex = 0 167 | let animationsCount = 3 168 | 169 | // Animate arrow to straight line 170 | withAnimation(.linear(duration: 0.3)) { 171 | self.initialAnim = 0.0 172 | } 173 | 174 | // Bounce animation 175 | bounceTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { timer in 176 | withAnimation(.spring(duration: 0.5)) { 177 | if animationIndex % 2 == 0 { 178 | self.isDownArrow.toggle() 179 | } else { 180 | self.initialAnim = 1.3 181 | } 182 | } 183 | 184 | animationIndex += 1 185 | if animationIndex >= animationsCount { 186 | timer.invalidate() 187 | } 188 | } 189 | } 190 | 191 | // Reset progress 192 | private func resetProgress() { 193 | endTimer?.invalidate() 194 | initialTimer?.invalidate() 195 | bounceTimer?.invalidate() 196 | progress = 0.0 197 | isAnimating = false 198 | initialAnim = 1.0 199 | isDownArrow = true 200 | bounceEffect = 1.0 201 | startDotAnim = false 202 | showPercent = false 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ProgressAnimation/WaveView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaveView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Mansi Prajapati on 24/04/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WaveView: View { 11 | 12 | // MARK: - Variables 13 | @State private var amplitude: CGFloat = 0.0 14 | @State private var phase: CGFloat = 0.0 15 | @State private var height: CGFloat = 18 16 | @Binding var progress: Float 17 | var arrowStrokeWidth: CGFloat 18 | var width: CGFloat 19 | 20 | var body: some View { 21 | VStack(spacing: 0) { 22 | let waveProgress = 1.0 / CGFloat(5) 23 | let normedAmplitude = (1.5 * waveProgress - 0.8) * self.amplitude 24 | 25 | Wave( 26 | phase: phase, 27 | normedAmplitude: normedAmplitude, 28 | width: width 29 | ) 30 | .stroke(style: StrokeStyle(lineWidth: arrowStrokeWidth, lineCap: .round)) 31 | .frame(width: width * 0.44, height: height) 32 | .onAppear { 33 | Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { timer in 34 | if progress >= 0.001 && progress < 0.92 { 35 | // Wave animation 36 | withAnimation( Animation.linear(duration: 2).repeatForever(autoreverses: false)) { 37 | self.amplitude = 0.8 38 | self.phase -= 6.0 39 | } 40 | } else if progress >= 0.92 && progress < 0.99 { 41 | // Slowly transforming wave to straight line 42 | withAnimation(Animation.linear(duration: 2)) { 43 | self.amplitude = 0 44 | self.phase -= 0.3 45 | } 46 | } else if progress >= 0.99 { 47 | timer.invalidate() 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | struct Wave: Shape { 56 | 57 | // MARK: - Variables 58 | var phase: CGFloat 59 | var normedAmplitude: CGFloat 60 | var width: CGFloat 61 | 62 | // MARK: - Aniamatable data 63 | var animatableData: AnimatablePair { 64 | get { AnimatablePair(normedAmplitude, phase)} 65 | 66 | set { 67 | self.normedAmplitude = newValue.first 68 | self.phase = newValue.second 69 | } 70 | } 71 | 72 | func path(in rect: CGRect) -> Path { 73 | var path = Path() 74 | let maxAmplitude = rect.height / 2 75 | let mid = rect.width / 2 76 | let density = 0.7 77 | let lineWidth = width < 150 ? 1.5 : 1.0 78 | 79 | for x in Swift.stride(from: 0, 80 | to: rect.width + density + lineWidth, 81 | by: density 82 | ) { 83 | let scaling = -pow(1 / mid * (x - mid), 2) + width * 0.02 84 | let y = scaling * maxAmplitude * normedAmplitude * sin(CGFloat(2 * Double.pi) * (0.8) * (x / rect.width) + self.phase) + rect.height / 3 85 | 86 | if x == 0 { 87 | path.move(to: CGPoint(x: x, y: y)) 88 | } else { 89 | path.addLine(to: CGPoint(x: x + 2, y: y)) 90 | } 91 | } 92 | return path 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/ReactionAnimation/ReactionAnimationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ReactionAnimationSwiftUI 4 | // 5 | // Created by Faaiz Daglawala on 15/12/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// This is the Main View of ReactionAnimation. Add this to your View Hierarchy to start 11 | /// using animation in your screen. It will displays a heart animation with expanding circles and smaller animated circles surrounding it. 12 | /// Use case Example: 13 | /// ```swift 14 | /// SSReactionAnimationView( 15 | /// style: SSReactionAnimationViewStyle( 16 | /// outerCircleColor: .clear, 17 | /// innerCircleColor: .blue, 18 | /// defautHeartColor: .gray, 19 | /// selectedHeartColor: .red 20 | /// ) 21 | /// ) 22 | /// .frame(width: 100, height: 100) 23 | /// ``` 24 | public struct SSReactionAnimationView: View { 25 | 26 | // MARK: - Properties 27 | 28 | /// The ViewModel that manages the animation states and properties. 29 | @ObservedObject var viewModel: SSReactionAnimationViewModel 30 | 31 | /// A closure that is triggered when the animation is completed. 32 | /// The closure takes a single `Bool` parameter that indicates whether 33 | /// the animation concluded with the item in a selected state. 34 | var onAnimationCompleted: ((_ isSelected: Bool) -> ())? 35 | 36 | // MARK: - Initializer 37 | 38 | /// Initializes a new `SSReactionAnimationView` with a specified style and an optional 39 | /// completion handler for the animation. 40 | /// 41 | /// - Parameters: 42 | /// - style: A `SSReactionAnimationViewStyle` object that defines the colors and other styling options for the animation. 43 | /// - onAnimationCompleted: An optional closure that is called when the animation completes, 44 | /// with a `Bool` indicating if the item is in a selected state. 45 | public init(style: SSReactionAnimationViewStyle, onAnimationCompleted: ((_ isSelected: Bool) -> ())? = nil) { 46 | self.viewModel = SSReactionAnimationViewModel(style: style) 47 | self.onAnimationCompleted = onAnimationCompleted 48 | } 49 | 50 | public var body: some View { 51 | GeometryReader { geometryProxy in 52 | ZStack { 53 | // Background Circle 54 | Circle() 55 | .fill(viewModel.style.outerCircleColor) 56 | 57 | // Heart Icon - shows either a default or selected version based on animation state. 58 | if viewModel.isSelected { 59 | // Display a selected-color heart after all animations are complete. 60 | Image(systemName: "heart.fill") 61 | .resizable() 62 | .scaledToFit() 63 | .frame(width: geometryProxy.size.width / 2, height: geometryProxy.size.height / 2) 64 | .foregroundColor(viewModel.style.selectedHeartColor) 65 | .opacity(1) 66 | .onTapGesture { 67 | // Reset small circles and animations when the selected heart is tapped. 68 | viewModel.resetBubbleProperties() 69 | viewModel.resetAnimation() 70 | } 71 | } else { 72 | // Default-colored heart that scales down when the animation starts. 73 | Image(systemName: "heart.fill") 74 | .resizable() 75 | .scaledToFit() 76 | .frame(width: geometryProxy.size.width / 2, height: geometryProxy.size.height / 2) 77 | .foregroundColor(viewModel.style.defautHeartColor) 78 | .opacity(viewModel.heartScaleAnimation ? 0 : 1) 79 | .scaleEffect(viewModel.heartScaleAnimation ? 0 : 1) 80 | .animation(.easeInOut(duration: 0.3), value: viewModel.heartScaleAnimation) 81 | .onTapGesture { 82 | // Start the animation when the default heart is tapped. 83 | viewModel.startAnimationSequence() 84 | } 85 | } 86 | 87 | // Main expanding circles animation when the `circleAnimation` state is active. 88 | if viewModel.circleAnimation { 89 | MainAnimationCircles(viewModel: viewModel) 90 | } 91 | } 92 | .onChange(of: viewModel.isSelected) { _ in 93 | onAnimationCompleted?(viewModel.isSelected) 94 | } 95 | .onAppear { 96 | viewModel.setBubbleCount() 97 | // Set the geometry size in the ViewModel for positioning calculations. 98 | viewModel.setAnimationViewSize(size: geometryProxy.size) 99 | viewModel.calculateSizeProperties() 100 | } 101 | .frame(width: geometryProxy.size.width, height: geometryProxy.size.height) 102 | } 103 | } 104 | 105 | /// A view that handles displaying the main animated circles around the central circle. 106 | private struct MainAnimationCircles: View { 107 | 108 | // MARK: - Properties 109 | 110 | /// Binding to the parent view's ViewModel to access animation properties. 111 | @ObservedObject var viewModel: SSReactionAnimationViewModel 112 | 113 | // MARK: - Body 114 | 115 | var body: some View { 116 | ZStack { 117 | // Seven Large Circles around the central circle, each positioned based on angle calculation. 118 | ForEach(0.. CGFloat { 97 | let angleStep = 360.0 / Double(bubbleCount) // Divide full circle into 7 segments 98 | let angleInDegrees = angleStep * CGFloat(index) + offset 99 | return angleInDegrees * .pi / 180 100 | } 101 | 102 | /// Randomizes colors for both large and small circles. 103 | private func randomizeBubbleColors() { 104 | largeBubbleColors = (0..( 14 | _ condition: Bool, 15 | transform: (Self) -> Transform 16 | ) -> some View { 17 | if condition { 18 | transform(self) 19 | } else { 20 | self 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/BubbleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 15/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Create a view with Bubbles used in animation 11 | struct BubbleView: View { 12 | 13 | // MARK: Variables 14 | 15 | /// Used for styling the view 16 | @State var style: SSWaterProgressViewStyle = SSWaterProgressViewStyle() 17 | 18 | /// Initial Bubble Scale 19 | @State private var scale : CGFloat = 1 20 | 21 | var body: some View { 22 | if style.showBubbles { 23 | ZStack { 24 | // Number of bubbles 25 | ForEach (1...10, id:\.self) { _ in 26 | Circle() 27 | .foregroundColor(style.bubbleColor.opacity(Double.random(in: 0.15...0.25))) 28 | .scaleEffect(self.scale * .random(in: 0.5...1)) 29 | .frame(width: .random(in: 1...25), 30 | height: CGFloat.random (in: 20...25), 31 | alignment: .center) 32 | .position( 33 | CGPoint( 34 | x: .random(in: style.circleSize/4...style.circleSize/1.3), 35 | y: .random (in: style.circleSize/5...style.circleSize/1.2) 36 | ) 37 | ) 38 | } 39 | } 40 | .task { 41 | withAnimation( 42 | .spring (dampingFraction: 0.5) 43 | .repeatForever() 44 | .speed(.random(in: 0.1...0.15)) 45 | .delay(.random(in: 0.01...0.1)) 46 | ) { 47 | self.scale = 1.2 // default circle scale 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | #Preview { 55 | BubbleView() 56 | } 57 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/CheckmarkView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckmarkView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 15/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CheckmarkView: View { 11 | 12 | // MARK: Variables 13 | 14 | /// Decide weather to make checkmark visible or not 15 | @State var displayCheckmark: Bool = false 16 | 17 | /// Initial Zoom of Checkmark 18 | @State private var zoom = 0.1 19 | 20 | /// Style for SSWaterProgress View 21 | var style: SSWaterProgressViewStyle 22 | 23 | var body: some View { 24 | checkMarkImg 25 | } 26 | 27 | private var checkMarkImg: some View { 28 | Image(systemName: style.checkMarkImg) 29 | .resizable() 30 | .frame(width: style.circleSize/3, height: style.circleSize/3) 31 | .opacity(displayCheckmark ? 1 : 0) 32 | .foregroundStyle(style.checkMarkImgColor) 33 | .scaleEffect(zoom, anchor: .center) 34 | .foregroundColor(.white) 35 | .animation(.spring(dampingFraction: 0.4), value: displayCheckmark) 36 | .onAppear() { 37 | displayCheckmark.toggle() 38 | zoom = 1.0 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/WaterCircleOutlineView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaterCircleOutlineView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 09/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WaterCircleOutlineView: View { 11 | 12 | // MARK: - Variables 13 | 14 | /// Outline Progress value 15 | @Binding var progress: Double 16 | 17 | /// Used for styling the view 18 | var style: SSWaterProgressViewStyle 19 | 20 | var body: some View { 21 | ZStack { 22 | Circle() 23 | .stroke(style.emptyStrokeColor, style: StrokeStyle(lineWidth: style.circleStrokeWidth, lineCap: .round)) 24 | .frame(width: style.circleSize, 25 | height: style.circleSize) 26 | Circle() 27 | .trim(from: 0.0, to: CGFloat(progress)) 28 | .stroke(style.fillStrokeColor, style: StrokeStyle(lineWidth: style.circleStrokeWidth, lineCap: .round)) 29 | .frame(width: style.circleSize, height: style.circleSize) 30 | .rotationEffect(.degrees(-90)) 31 | .transition(.slide) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/WaterCircleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaterCircleView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 09/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WaterCircleView: Shape { 11 | 12 | // MARK: - Variables 13 | 14 | /// Progress Value used to determine the height of water 15 | var progress: Double 16 | 17 | /// Initial Animation Start 18 | var offset: Angle 19 | 20 | /// Enabling Animation 21 | var animatableData: Double { 22 | get { offset.degrees } 23 | set { offset = Angle(degrees: newValue) } 24 | } 25 | 26 | /** 27 | Creates a path that represents a wave or a solid background, depending on the provided progress value. 28 | 29 | - Parameters: 30 | - rect: A CGRect object defining the rectangle within which the path will be created. 31 | 32 | - Returns: 33 | A Path object representing the wave or solid background. 34 | 35 | This function generates a path suitable for drawing a wave or a solid rectangle within the given rectangle. It calculates the wave height and position based on the `progress` value (assumed to be a variable outside the function). 36 | 37 | The path creation process involves the following steps: 38 | 39 | 1. **Wave Parameters:** It defines the lowest and highest possible wave heights (`lowestWave` and `highestWave`). 40 | 2. **Wave Height Calculation:** Calculates the normalized wave height (`newPercent`) based on `progress`, and then determines the wave's amplitude (`waveHeight`) and vertical position (`yOffSet`) within the rectangle. The Animation is of Wave style until progress is not 100% else it will be solid background. 41 | 3. **Path Creation:** 42 | - **Starting Point:** Sets the starting point on the top of the wave based on `yOffSet` and the initial sine value. 43 | - **Wave Segments:** Iterates through angles to create line segments that represent the wave's shape. 44 | - **Connecting Lines:** Connects the last wave point to the bottom-right corner and then to the bottom-left corner, completing the shape. 45 | - **Closing the Path:** Closes the path by connecting the bottom-left corner back to the starting point. 46 | 47 | - Note: This function relies on external variables `progress` and `offset`. 48 | */ 49 | func path(in rect: CGRect) -> Path { 50 | var wavePath = Path() 51 | let lowestWave = 0.02 52 | let highestWave = 1.00 53 | 54 | let newPercent = lowestWave + (highestWave - lowestWave) * (progress/100) 55 | 56 | let waveHeight = progress == 100 ? 0 : 0.03 * rect.height 57 | let yOffSet = CGFloat(1 - newPercent) * 58 | (rect.height - 4 * waveHeight) + 2 * waveHeight 59 | let startAngle = offset 60 | let endAngle = offset + Angle(degrees: 360 + 10) 61 | 62 | wavePath.move(to: CGPoint(x: 0, y: yOffSet + waveHeight * 63 | CGFloat(sin(offset.radians)))) 64 | 65 | for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) { 66 | let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width 67 | wavePath.addLine(to: CGPoint(x: x, y: yOffSet + waveHeight * CGFloat(sin(Angle(degrees: angle).radians)))) 68 | } 69 | 70 | wavePath.addLine(to: CGPoint(x: rect.width, y: rect.height)) 71 | wavePath.addLine(to: CGPoint(x: 0, y: rect.height)) 72 | wavePath.closeSubpath() 73 | return wavePath 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/WaterProgressTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaterProgressTextView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 16/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WaterProgressTextView: View { 11 | 12 | // MARK: - Variable 13 | 14 | /// Decide weather to show percentage text or not 15 | var showPercentage: Bool 16 | 17 | /// Progress percent value 18 | @Binding var progress: Double 19 | 20 | /// For Styling text 21 | var style: SSWaterProgressViewStyle 22 | 23 | var body: some View { 24 | if showPercentage { 25 | Text("\(Int(progress * 100))%") 26 | .font(style.progressFont) 27 | .foregroundStyle(style.progressTextColor) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/WaterProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSWaterProgressView.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 09/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// This is the Main View of WaterAnimation. Add this to your View Hierarchy to start 11 | /// using animation in your screen. It will include circular progress indicator with water filled in it, 12 | /// along with progress text. 13 | /// Use case Example: 14 | /// ```swift 15 | /// SSWaterProgressView(progress: $progress, 16 | /// showPercent: true , 17 | /// style: SSWaterProgressViewStyle( 18 | /// circleSize: 200, 19 | /// circleStrokeWidth: 10, 20 | /// progressFont: .system(size: 16, weight: .bold), 21 | /// progressTextColor: .black, 22 | /// emptyStrokeColor: .cyan.opacity(0.2) , 23 | /// fillStrokeColor: .cyan, 24 | /// waterColor: .mint, 25 | /// showBubbles: true, 26 | /// bubbleColor: .white, 27 | /// checkMarkImg: "checkmark.circle.fill", 28 | /// checkMarkImgColor: .brown 29 | /// ), 30 | /// onProgressCompletion: { }) 31 | /// ``` 32 | public struct SSWaterProgressView: View { 33 | 34 | // MARK: - Variables 35 | 36 | /// Dynamic progress while using progress view 37 | @Binding var progress: Double 38 | 39 | /// Hide/show progress percentage 40 | var showPercent = false 41 | 42 | /// For progressView customization 43 | var style: SSWaterProgressViewStyle = SSWaterProgressViewStyle() 44 | 45 | /// Call back for finished animation 46 | var onProgressCompletion: (() -> Void)? 47 | 48 | /// Water Wave Animation 49 | @State private var startAnimation = Angle(degrees: 0) 50 | 51 | public var body: some View { 52 | if #available(iOS 17.0, *) { 53 | screenContent 54 | .onChange(of: progress) { oldVal, newValue in 55 | progressCompletionBlock(progressVal: newValue) 56 | } 57 | } else { 58 | screenContent 59 | .onChange(of: progress) { value in 60 | progressCompletionBlock(progressVal: value) 61 | } 62 | } 63 | } 64 | 65 | private var screenContent: some View { 66 | ZStack { 67 | progressOutlineView 68 | waterCircleView 69 | progressOverlay 70 | } 71 | } 72 | 73 | private func progressCompletionBlock(progressVal: Double) { 74 | if progressVal == 1.0 { 75 | if let onProgressCompletion { 76 | onProgressCompletion() 77 | } 78 | } 79 | } 80 | } 81 | 82 | extension SSWaterProgressView { 83 | 84 | /// Progress Circle With animation 85 | private var progressOutlineView: some View { 86 | WaterCircleOutlineView(progress: $progress, style: style) 87 | .if(progress > 0.0) { 88 | $0.animation(.linear, value: progress) 89 | } 90 | } 91 | 92 | /// Water Effect Circular View 93 | private var waterCircleView: some View { 94 | WaterCircleView( 95 | progress: progress * 100, 96 | offset: Angle(degrees: startAnimation.degrees) 97 | ) 98 | .fill(style.waterColor) 99 | .frame( 100 | width: style.circleSize, 101 | height: style.circleSize + style.circleStrokeWidth 102 | ) 103 | .mask { 104 | Circle() 105 | .frame(width: style.circleSize - style.circleStrokeWidth, 106 | height: style.circleSize - style.circleStrokeWidth) 107 | } 108 | .task { 109 | withAnimation(.linear(duration: 1) 110 | .repeatForever(autoreverses: false)) { 111 | startAnimation = Angle(degrees: 360) 112 | } 113 | } 114 | .overlay { 115 | BubbleView(style: style) 116 | } 117 | } 118 | 119 | /// Progress Overlays views such as completion checkmark and Progress TextView 120 | @ViewBuilder 121 | private var progressOverlay: some View { 122 | if progress == 1.0 { 123 | CheckmarkView(style: style) 124 | } else { 125 | // Progress percentage text view 126 | WaterProgressTextView( 127 | showPercentage: showPercent, 128 | progress: $progress, 129 | style: style 130 | ) 131 | } 132 | } 133 | } 134 | 135 | #Preview { 136 | SSWaterProgressView(progress: .constant(20.0)) 137 | } 138 | -------------------------------------------------------------------------------- /SSSwiftUIAnimations/Sources/WaterProgressAnimation/WaterProgressViewStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaterProgressViewStyle.swift 3 | // SSSwiftUIAnimations 4 | // 5 | // Created by Rahul Yadav on 09/07/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | // MARK: - SSWaterProgressViewStyle Configuration 11 | /// A style configuration for the SSWaterProgress Animation 12 | /// Use it to set the colour, size, stroke width, and visibility parameters of animation 13 | public struct SSWaterProgressViewStyle { 14 | 15 | // MARK: - Variables 16 | /// Size of the animation circle 17 | var circleSize: CGFloat 18 | 19 | /// Stroke Width of the progress circle 20 | var circleStrokeWidth: CGFloat 21 | 22 | /// Font of the progress percent text 23 | var progressFont: Font 24 | 25 | /// Color of the progress text 26 | var progressTextColor: Color 27 | 28 | /// Color of the empty part of the progress circle 29 | var emptyStrokeColor: Color 30 | 31 | /// Color of the filled part of progress circle 32 | var fillStrokeColor: Color 33 | 34 | /// Color of the water filled in animation 35 | var waterColor: Color 36 | 37 | /// Decide weather to show or hide bubbles 38 | var showBubbles: Bool 39 | 40 | /// Color of the bubble 41 | var bubbleColor: Color 42 | 43 | /// System Image Name to show when progress is completed 44 | var checkMarkImg: String 45 | 46 | /// Tint Color of checkmark image 47 | var checkMarkImgColor: Color 48 | 49 | /// WaterProgressView Styles and attributes 50 | /// - Parameters: 51 | /// - circleSize: Size of the animation circle 52 | /// - circleStrokeWidth: Stroke Width of the progress circle 53 | /// - progressFont: Font of the progress percent text 54 | /// - progressTextColor: Color of the progress text 55 | /// - emptyStrokeColor: Color of the empty part of the progress circle 56 | /// - fillStrokeColor: Color of the filled part of progress circle 57 | /// - waterColor: Color of the water filled in animation 58 | /// - showBubbles: Decide weather to show or hide bubbles 59 | /// - bubbleColor: Color of the bubble 60 | /// - checkMarkImg: System Image Name to show when progress is completed 61 | /// - checkMarkImgColor: Tint Color of checkmark image 62 | public init( 63 | circleSize: CGFloat = 200.0, 64 | circleStrokeWidth: CGFloat = 5.0, 65 | progressFont: Font = .system(size: 16, weight: .semibold), 66 | progressTextColor: Color = Color.white, 67 | emptyStrokeColor: Color = Color.gray.opacity(0.3), 68 | fillStrokeColor: Color = Color.orange, 69 | waterColor: Color = Color.yellow, 70 | showBubbles: Bool = true, 71 | bubbleColor: Color = Color.white, 72 | checkMarkImg: String = "checkmark", 73 | checkMarkImgColor: Color = .white 74 | ) { 75 | self.circleSize = max(circleSize, 100) 76 | self.circleStrokeWidth = max(circleStrokeWidth, 1) 77 | self.progressFont = progressFont 78 | self.progressTextColor = progressTextColor 79 | self.emptyStrokeColor = emptyStrokeColor 80 | self.fillStrokeColor = fillStrokeColor 81 | self.waterColor = waterColor 82 | self.showBubbles = showBubbles 83 | self.bubbleColor = bubbleColor 84 | self.checkMarkImg = checkMarkImg 85 | self.checkMarkImgColor = checkMarkImgColor 86 | } 87 | } 88 | --------------------------------------------------------------------------------