├── .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 | 
2 |
3 | # SS-iOS-Animations
4 |
5 | SwiftUI animation library to bring your app to life. ✨
6 |
7 | [](https://developer.apple.com/documentation/swiftui)
8 | [](https://developer.apple.com/swift/)
9 | [](https://developer.apple.com/documentation/swiftui/animations)
10 | [](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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------