├── RippleScrollBarDemo
├── Assets.xcassets
│ ├── Contents.json
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── AppDelegate.swift
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── SceneDelegate.swift
├── ViewController.swift
└── RippleScrollBar.swift
├── RippleScrollBarDemo.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── someshkarthik.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
└── README.md
/RippleScrollBarDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RippleScrollBarDemo
2 | A demo project to explain the usage of CAReplicatorLayer to create RippleScrollBar component from CRED App
3 |
4 | # Preview
5 | https://user-images.githubusercontent.com/87409736/192079251-48138e46-aa16-4e56-b82b-8bcbca8bd4f6.mp4
6 |
7 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo.xcodeproj/xcuserdata/someshkarthik.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RippleScrollBarDemo.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 | UISceneStoryboardFile
19 | Main
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RippleScrollBarDemo
4 | //
5 | // Created by Somesh Karthik on 23/09/22.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // RippleScrollBarDemo
4 | //
5 | // Created by Somesh Karthik on 23/09/22.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | guard let _ = (scene as? UIWindowScene) else { return }
20 | }
21 |
22 | func sceneDidDisconnect(_ scene: UIScene) {
23 | // Called as the scene is being released by the system.
24 | // This occurs shortly after the scene enters the background, or when its session is discarded.
25 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
27 | }
28 |
29 | func sceneDidBecomeActive(_ scene: UIScene) {
30 | // Called when the scene has moved from an inactive state to an active state.
31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
32 | }
33 |
34 | func sceneWillResignActive(_ scene: UIScene) {
35 | // Called when the scene will move from an active state to an inactive state.
36 | // This may occur due to temporary interruptions (ex. an incoming phone call).
37 | }
38 |
39 | func sceneWillEnterForeground(_ scene: UIScene) {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 | func sceneDidEnterBackground(_ scene: UIScene) {
45 | // Called as the scene transitions from the foreground to the background.
46 | // Use this method to save data, release shared resources, and store enough scene-specific state information
47 | // to restore the scene back to its current state.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // RippleScrollBarDemo
4 | //
5 | // Created by Somesh Karthik on 23/09/22.
6 | //
7 |
8 | import UIKit
9 |
10 | final class ViewController: UIViewController {
11 | private let collectionView: UICollectionView = {
12 | let layout = UICollectionViewFlowLayout()
13 | layout.scrollDirection = .horizontal
14 | layout.itemSize = CGSize(width: 250, height: 300)
15 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
16 | collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: String(describing: UICollectionViewCell.self))
17 | collectionView.showsHorizontalScrollIndicator = false
18 | collectionView.contentInset = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 15)
19 | return collectionView
20 | }()
21 | private let scrollBar = RippleScrollBar()
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | // Do any additional setup after loading the view.
26 |
27 | view.backgroundColor = UIColor(red: 241/255, green: 237/255, blue: 228/255, alpha: 1.0)
28 |
29 | collectionView.delegate = self
30 | collectionView.backgroundColor = view.backgroundColor
31 | collectionView.dataSource = self
32 |
33 | view.addSubview(collectionView)
34 | collectionView.translatesAutoresizingMaskIntoConstraints = false
35 | NSLayoutConstraint.activate([
36 | collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
37 | collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
38 | collectionView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
39 | collectionView.heightAnchor.constraint(equalToConstant: 300)
40 | ])
41 |
42 | view.addSubview(scrollBar)
43 | scrollBar.translatesAutoresizingMaskIntoConstraints = false
44 | NSLayoutConstraint.activate([
45 | scrollBar.centerXAnchor.constraint(equalTo: collectionView.centerXAnchor),
46 | scrollBar.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 20),
47 | scrollBar.widthAnchor.constraint(equalToConstant: 200),
48 | scrollBar.heightAnchor.constraint(equalToConstant: 40)
49 | ])
50 |
51 | view.layoutIfNeeded()
52 | }
53 |
54 | override func viewWillAppear(_ animated: Bool) {
55 | super.viewWillAppear(animated)
56 | scrollViewDidScroll(collectionView)
57 | }
58 | }
59 |
60 | extension ViewController: UICollectionViewDataSource {
61 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
62 | 10
63 | }
64 |
65 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
66 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: UICollectionViewCell.self), for: indexPath)
67 | cell.backgroundColor = UIColor.white
68 | cell.layer.cornerRadius = 5
69 | cell.layer.borderColor = UIColor.gray.cgColor
70 | cell.layer.borderWidth = 1
71 | return cell
72 | }
73 | }
74 |
75 | extension ViewController: UICollectionViewDelegate {
76 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
77 | let contentInset = scrollView.contentInset
78 | let contentOffset = scrollView.contentOffset
79 | let contentSize = scrollView.contentSize
80 | let currentContentOffset = contentOffset.x + contentInset.left
81 |
82 | let totalContentOffset = contentSize.width + contentInset.left + contentInset.right - view.frame.width
83 |
84 | scrollBar.setPercentage(max(min(currentContentOffset/totalContentOffset, 1.0), 0))
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo/RippleScrollBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RippleScrollBar.swift
3 | // RippleScrollBarDemo
4 | //
5 | // Created by Somesh Karthik on 23/09/22.
6 | //
7 |
8 | import UIKit
9 |
10 | final class RippleScrollBar: UIView {
11 | private let leftInstance = RippleReplicatorInstance()
12 | private let rightInstance = RippleReplicatorInstance()
13 |
14 | override init(frame: CGRect) {
15 | super.init(frame: frame)
16 | setup()
17 | }
18 |
19 | required init?(coder: NSCoder) {
20 | super.init(coder: coder)
21 | setup()
22 | }
23 |
24 | func setup() {
25 | addSubview(leftInstance)
26 | addSubview(rightInstance)
27 |
28 | leftInstance.clipsToBounds = true
29 | rightInstance.clipsToBounds = true
30 | leftInstance.tintColor = UIColor(red: 114/255, green: 41/255, blue: 217/255, alpha: 1.0)
31 | leftInstance.direction = .left
32 | leftInstance.rippleLineWidth = 4
33 |
34 | rightInstance.direction = .right
35 | rightInstance.rippleLineWidth = leftInstance.rippleLineWidth
36 | rightInstance.tintColor = leftInstance.tintColor
37 |
38 | leftInstance.translatesAutoresizingMaskIntoConstraints = false
39 | NSLayoutConstraint.activate([
40 | leftInstance.leadingAnchor.constraint(equalTo: leadingAnchor),
41 | leftInstance.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
42 | leftInstance.bottomAnchor.constraint(equalTo: bottomAnchor),
43 | leftInstance.topAnchor.constraint(equalTo: topAnchor)
44 | ])
45 |
46 | rightInstance.translatesAutoresizingMaskIntoConstraints = false
47 | NSLayoutConstraint.activate([
48 | rightInstance.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
49 | rightInstance.trailingAnchor.constraint(equalTo: trailingAnchor),
50 | rightInstance.bottomAnchor.constraint(equalTo: bottomAnchor),
51 | rightInstance.topAnchor.constraint(equalTo: topAnchor)
52 | ])
53 | }
54 |
55 | func setPercentage(_ percentage: CGFloat) {
56 | rightInstance.setOffsetPercentage(percentage)
57 | leftInstance.setOffsetPercentage(percentage)
58 | }
59 | }
60 |
61 | private final class RippleReplicatorInstance: UIView {
62 | enum Direction {
63 | case left
64 | case right
65 | }
66 |
67 | override class var layerClass: AnyClass {
68 | CAReplicatorLayer.self
69 | }
70 |
71 | // MARK: Private Properties
72 | private var replicatorLayer: CAReplicatorLayer {
73 | layer as! CAReplicatorLayer
74 | }
75 |
76 | //MARK:- View Properties
77 | private let rippleShape = CAShapeLayer()
78 |
79 | private let rippleTranslation: CGFloat = 50
80 |
81 | //MARK:- Size constants and Margins
82 | private var arcRadius: CGFloat {
83 | bounds.height/2
84 | }
85 |
86 | private var rippleOffset: CGFloat {
87 | rippleSize.width - arcRadius
88 | }
89 |
90 | private let numberOfRipples: Int = 3
91 |
92 | // MARK: Public Properties
93 | override var tintColor: UIColor! {
94 | didSet {
95 | updateRipple()
96 | }
97 | }
98 |
99 | var direction: Direction = .left {
100 | didSet {
101 | updateRipple()
102 | }
103 | }
104 |
105 | var rippleSize: CGSize {
106 | let rippleWidth = bounds.width/CGFloat(numberOfRipples)
107 | return CGSize(width: rippleWidth + arcRadius*0.35, height: bounds.height)
108 | }
109 |
110 | /// Use this property to change the linewidth of child ripples
111 | /// Default value is `1`
112 | var rippleLineWidth: CGFloat = 1 {
113 | didSet {
114 | updateRipple()
115 | }
116 | }
117 |
118 | override init(frame: CGRect) {
119 | super.init(frame: frame)
120 | setup()
121 | }
122 |
123 | required init?(coder: NSCoder) {
124 | super.init(coder: coder)
125 | setup()
126 | }
127 |
128 | // MARK: Public Functions
129 | func setup() {
130 | rippleShape.fillColor = nil
131 | replicatorLayer.addSublayer(rippleShape)
132 | replicatorLayer.instanceCount = numberOfRipples
133 | }
134 |
135 | /// It will update the spacing between each ripples by taking
136 | /// ``direction``, `rippleOffset`` and ``rippleOffsetPercentage`` applied to `x`
137 | func setOffsetPercentage(_ percentage: CGFloat) {
138 | CATransaction.begin()
139 | CATransaction.setDisableActions(true)
140 | switch direction {
141 | case .left:
142 | replicatorLayer.instanceTransform = CATransform3DMakeTranslation(rippleOffset * percentage, 0, 0)
143 | case .right:
144 | replicatorLayer.instanceTransform = CATransform3DMakeTranslation(-1 * rippleOffset * (1-percentage), 0, 0)
145 | }
146 | CATransaction.commit()
147 | }
148 |
149 | override func draw(_ rect: CGRect) {
150 | super.draw(rect)
151 |
152 | let path: UIBezierPath
153 | switch direction {
154 | case .left:
155 | path = UIBezierPath(arcCenter: CGPoint(x: arcRadius, y: rippleSize.height/2), radius: rippleSize.height/2 - rippleLineWidth/2, startAngle: 3 * .pi/2, endAngle: .pi/2, clockwise: false)
156 |
157 | path.addLine(to: CGPoint(x: rippleSize.width, y: bounds.height-rippleLineWidth/2))
158 | path.move(to: CGPoint(x: arcRadius, y: rippleLineWidth/2))
159 | path.addLine(to: CGPoint(x: rippleSize.width, y: rippleLineWidth/2))
160 |
161 | case .right:
162 | let arcOriginX = rect.width - arcRadius
163 |
164 | path = UIBezierPath(arcCenter: CGPoint(x: arcOriginX, y: rippleSize.height/2), radius: rippleSize.height/2 - rippleLineWidth/2, startAngle: 3 * .pi/2, endAngle: .pi/2, clockwise: direction == .right)
165 |
166 | path.addLine(to: CGPoint(x: 0, y: bounds.height-rippleLineWidth/2))
167 | path.move(to: CGPoint(x: arcOriginX, y: rippleLineWidth/2))
168 | path.addLine(to: CGPoint(x: 0, y: rippleLineWidth/2))
169 |
170 | }
171 |
172 | rippleShape.path = path.cgPath
173 | }
174 |
175 | // MARK: Private Functions
176 |
177 | /// It will update the ripple related properties and frame
178 | private func updateRipple() {
179 | rippleShape.strokeColor = tintColor.cgColor
180 | rippleShape.lineWidth = rippleLineWidth
181 | let xOrigin = direction == .left ? 0 : bounds.width - rippleSize.width
182 | rippleShape.frame = CGRect(x: xOrigin, y: 0, width: rippleSize.width, height: rippleSize.height)
183 | }
184 | }
185 |
186 |
--------------------------------------------------------------------------------
/RippleScrollBarDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 87C4E78E28DE0B78005955DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C4E78D28DE0B78005955DA /* AppDelegate.swift */; };
11 | 87C4E79028DE0B78005955DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C4E78F28DE0B78005955DA /* SceneDelegate.swift */; };
12 | 87C4E79228DE0B78005955DA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C4E79128DE0B78005955DA /* ViewController.swift */; };
13 | 87C4E79528DE0B78005955DA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87C4E79328DE0B78005955DA /* Main.storyboard */; };
14 | 87C4E79728DE0B79005955DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 87C4E79628DE0B79005955DA /* Assets.xcassets */; };
15 | 87C4E79A28DE0B79005955DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87C4E79828DE0B79005955DA /* LaunchScreen.storyboard */; };
16 | 87C4E7A228DE21F2005955DA /* RippleScrollBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C4E7A128DE21F2005955DA /* RippleScrollBar.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 87C4E78A28DE0B78005955DA /* RippleScrollBarDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RippleScrollBarDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 87C4E78D28DE0B78005955DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
22 | 87C4E78F28DE0B78005955DA /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
23 | 87C4E79128DE0B78005955DA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
24 | 87C4E79428DE0B78005955DA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 87C4E79628DE0B79005955DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 87C4E79928DE0B79005955DA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
27 | 87C4E79B28DE0B79005955DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | 87C4E7A128DE21F2005955DA /* RippleScrollBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RippleScrollBar.swift; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 87C4E78728DE0B78005955DA /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 87C4E78128DE0B78005955DA = {
43 | isa = PBXGroup;
44 | children = (
45 | 87C4E78C28DE0B78005955DA /* RippleScrollBarDemo */,
46 | 87C4E78B28DE0B78005955DA /* Products */,
47 | );
48 | sourceTree = "";
49 | };
50 | 87C4E78B28DE0B78005955DA /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | 87C4E78A28DE0B78005955DA /* RippleScrollBarDemo.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | 87C4E78C28DE0B78005955DA /* RippleScrollBarDemo */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 87C4E78D28DE0B78005955DA /* AppDelegate.swift */,
62 | 87C4E78F28DE0B78005955DA /* SceneDelegate.swift */,
63 | 87C4E79128DE0B78005955DA /* ViewController.swift */,
64 | 87C4E79328DE0B78005955DA /* Main.storyboard */,
65 | 87C4E79628DE0B79005955DA /* Assets.xcassets */,
66 | 87C4E79828DE0B79005955DA /* LaunchScreen.storyboard */,
67 | 87C4E79B28DE0B79005955DA /* Info.plist */,
68 | 87C4E7A128DE21F2005955DA /* RippleScrollBar.swift */,
69 | );
70 | path = RippleScrollBarDemo;
71 | sourceTree = "";
72 | };
73 | /* End PBXGroup section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | 87C4E78928DE0B78005955DA /* RippleScrollBarDemo */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = 87C4E79E28DE0B79005955DA /* Build configuration list for PBXNativeTarget "RippleScrollBarDemo" */;
79 | buildPhases = (
80 | 87C4E78628DE0B78005955DA /* Sources */,
81 | 87C4E78728DE0B78005955DA /* Frameworks */,
82 | 87C4E78828DE0B78005955DA /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = RippleScrollBarDemo;
89 | productName = RippleScrollBarDemo;
90 | productReference = 87C4E78A28DE0B78005955DA /* RippleScrollBarDemo.app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | 87C4E78228DE0B78005955DA /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | BuildIndependentTargetsInParallel = 1;
100 | LastSwiftUpdateCheck = 1400;
101 | LastUpgradeCheck = 1400;
102 | TargetAttributes = {
103 | 87C4E78928DE0B78005955DA = {
104 | CreatedOnToolsVersion = 14.0;
105 | };
106 | };
107 | };
108 | buildConfigurationList = 87C4E78528DE0B78005955DA /* Build configuration list for PBXProject "RippleScrollBarDemo" */;
109 | compatibilityVersion = "Xcode 14.0";
110 | developmentRegion = en;
111 | hasScannedForEncodings = 0;
112 | knownRegions = (
113 | en,
114 | Base,
115 | );
116 | mainGroup = 87C4E78128DE0B78005955DA;
117 | productRefGroup = 87C4E78B28DE0B78005955DA /* Products */;
118 | projectDirPath = "";
119 | projectRoot = "";
120 | targets = (
121 | 87C4E78928DE0B78005955DA /* RippleScrollBarDemo */,
122 | );
123 | };
124 | /* End PBXProject section */
125 |
126 | /* Begin PBXResourcesBuildPhase section */
127 | 87C4E78828DE0B78005955DA /* Resources */ = {
128 | isa = PBXResourcesBuildPhase;
129 | buildActionMask = 2147483647;
130 | files = (
131 | 87C4E79A28DE0B79005955DA /* LaunchScreen.storyboard in Resources */,
132 | 87C4E79728DE0B79005955DA /* Assets.xcassets in Resources */,
133 | 87C4E79528DE0B78005955DA /* Main.storyboard in Resources */,
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | /* End PBXResourcesBuildPhase section */
138 |
139 | /* Begin PBXSourcesBuildPhase section */
140 | 87C4E78628DE0B78005955DA /* Sources */ = {
141 | isa = PBXSourcesBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | 87C4E7A228DE21F2005955DA /* RippleScrollBar.swift in Sources */,
145 | 87C4E79228DE0B78005955DA /* ViewController.swift in Sources */,
146 | 87C4E78E28DE0B78005955DA /* AppDelegate.swift in Sources */,
147 | 87C4E79028DE0B78005955DA /* SceneDelegate.swift in Sources */,
148 | );
149 | runOnlyForDeploymentPostprocessing = 0;
150 | };
151 | /* End PBXSourcesBuildPhase section */
152 |
153 | /* Begin PBXVariantGroup section */
154 | 87C4E79328DE0B78005955DA /* Main.storyboard */ = {
155 | isa = PBXVariantGroup;
156 | children = (
157 | 87C4E79428DE0B78005955DA /* Base */,
158 | );
159 | name = Main.storyboard;
160 | sourceTree = "";
161 | };
162 | 87C4E79828DE0B79005955DA /* LaunchScreen.storyboard */ = {
163 | isa = PBXVariantGroup;
164 | children = (
165 | 87C4E79928DE0B79005955DA /* Base */,
166 | );
167 | name = LaunchScreen.storyboard;
168 | sourceTree = "";
169 | };
170 | /* End PBXVariantGroup section */
171 |
172 | /* Begin XCBuildConfiguration section */
173 | 87C4E79C28DE0B79005955DA /* Debug */ = {
174 | isa = XCBuildConfiguration;
175 | buildSettings = {
176 | ALWAYS_SEARCH_USER_PATHS = NO;
177 | CLANG_ANALYZER_NONNULL = YES;
178 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
179 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
180 | CLANG_ENABLE_MODULES = YES;
181 | CLANG_ENABLE_OBJC_ARC = YES;
182 | CLANG_ENABLE_OBJC_WEAK = YES;
183 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
184 | CLANG_WARN_BOOL_CONVERSION = YES;
185 | CLANG_WARN_COMMA = YES;
186 | CLANG_WARN_CONSTANT_CONVERSION = YES;
187 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
189 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
190 | CLANG_WARN_EMPTY_BODY = YES;
191 | CLANG_WARN_ENUM_CONVERSION = YES;
192 | CLANG_WARN_INFINITE_RECURSION = YES;
193 | CLANG_WARN_INT_CONVERSION = YES;
194 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
195 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
198 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
199 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
200 | CLANG_WARN_STRICT_PROTOTYPES = YES;
201 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
203 | CLANG_WARN_UNREACHABLE_CODE = YES;
204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
205 | COPY_PHASE_STRIP = NO;
206 | DEBUG_INFORMATION_FORMAT = dwarf;
207 | ENABLE_STRICT_OBJC_MSGSEND = YES;
208 | ENABLE_TESTABILITY = YES;
209 | GCC_C_LANGUAGE_STANDARD = gnu11;
210 | GCC_DYNAMIC_NO_PIC = NO;
211 | GCC_NO_COMMON_BLOCKS = YES;
212 | GCC_OPTIMIZATION_LEVEL = 0;
213 | GCC_PREPROCESSOR_DEFINITIONS = (
214 | "DEBUG=1",
215 | "$(inherited)",
216 | );
217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
219 | GCC_WARN_UNDECLARED_SELECTOR = YES;
220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
221 | GCC_WARN_UNUSED_FUNCTION = YES;
222 | GCC_WARN_UNUSED_VARIABLE = YES;
223 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
224 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
225 | MTL_FAST_MATH = YES;
226 | ONLY_ACTIVE_ARCH = YES;
227 | SDKROOT = iphoneos;
228 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
229 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
230 | };
231 | name = Debug;
232 | };
233 | 87C4E79D28DE0B79005955DA /* Release */ = {
234 | isa = XCBuildConfiguration;
235 | buildSettings = {
236 | ALWAYS_SEARCH_USER_PATHS = NO;
237 | CLANG_ANALYZER_NONNULL = YES;
238 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
240 | CLANG_ENABLE_MODULES = YES;
241 | CLANG_ENABLE_OBJC_ARC = YES;
242 | CLANG_ENABLE_OBJC_WEAK = YES;
243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
244 | CLANG_WARN_BOOL_CONVERSION = YES;
245 | CLANG_WARN_COMMA = YES;
246 | CLANG_WARN_CONSTANT_CONVERSION = YES;
247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
249 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
250 | CLANG_WARN_EMPTY_BODY = YES;
251 | CLANG_WARN_ENUM_CONVERSION = YES;
252 | CLANG_WARN_INFINITE_RECURSION = YES;
253 | CLANG_WARN_INT_CONVERSION = YES;
254 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
255 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
256 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
258 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
260 | CLANG_WARN_STRICT_PROTOTYPES = YES;
261 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | COPY_PHASE_STRIP = NO;
266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
267 | ENABLE_NS_ASSERTIONS = NO;
268 | ENABLE_STRICT_OBJC_MSGSEND = YES;
269 | GCC_C_LANGUAGE_STANDARD = gnu11;
270 | GCC_NO_COMMON_BLOCKS = YES;
271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
273 | GCC_WARN_UNDECLARED_SELECTOR = YES;
274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
275 | GCC_WARN_UNUSED_FUNCTION = YES;
276 | GCC_WARN_UNUSED_VARIABLE = YES;
277 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
278 | MTL_ENABLE_DEBUG_INFO = NO;
279 | MTL_FAST_MATH = YES;
280 | SDKROOT = iphoneos;
281 | SWIFT_COMPILATION_MODE = wholemodule;
282 | SWIFT_OPTIMIZATION_LEVEL = "-O";
283 | VALIDATE_PRODUCT = YES;
284 | };
285 | name = Release;
286 | };
287 | 87C4E79F28DE0B79005955DA /* Debug */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
292 | CODE_SIGN_STYLE = Automatic;
293 | CURRENT_PROJECT_VERSION = 1;
294 | DEVELOPMENT_TEAM = UGULN3J6K6;
295 | GENERATE_INFOPLIST_FILE = YES;
296 | INFOPLIST_FILE = RippleScrollBarDemo/Info.plist;
297 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
298 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
299 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
300 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
301 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
302 | LD_RUNPATH_SEARCH_PATHS = (
303 | "$(inherited)",
304 | "@executable_path/Frameworks",
305 | );
306 | MARKETING_VERSION = 1.0;
307 | PRODUCT_BUNDLE_IDENTIFIER = com.sample.RippleScrollBarDemo;
308 | PRODUCT_NAME = "$(TARGET_NAME)";
309 | SWIFT_EMIT_LOC_STRINGS = YES;
310 | SWIFT_VERSION = 5.0;
311 | TARGETED_DEVICE_FAMILY = "1,2";
312 | };
313 | name = Debug;
314 | };
315 | 87C4E7A028DE0B79005955DA /* Release */ = {
316 | isa = XCBuildConfiguration;
317 | buildSettings = {
318 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
319 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
320 | CODE_SIGN_STYLE = Automatic;
321 | CURRENT_PROJECT_VERSION = 1;
322 | DEVELOPMENT_TEAM = UGULN3J6K6;
323 | GENERATE_INFOPLIST_FILE = YES;
324 | INFOPLIST_FILE = RippleScrollBarDemo/Info.plist;
325 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
326 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
327 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
328 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
329 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
330 | LD_RUNPATH_SEARCH_PATHS = (
331 | "$(inherited)",
332 | "@executable_path/Frameworks",
333 | );
334 | MARKETING_VERSION = 1.0;
335 | PRODUCT_BUNDLE_IDENTIFIER = com.sample.RippleScrollBarDemo;
336 | PRODUCT_NAME = "$(TARGET_NAME)";
337 | SWIFT_EMIT_LOC_STRINGS = YES;
338 | SWIFT_VERSION = 5.0;
339 | TARGETED_DEVICE_FAMILY = "1,2";
340 | };
341 | name = Release;
342 | };
343 | /* End XCBuildConfiguration section */
344 |
345 | /* Begin XCConfigurationList section */
346 | 87C4E78528DE0B78005955DA /* Build configuration list for PBXProject "RippleScrollBarDemo" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 87C4E79C28DE0B79005955DA /* Debug */,
350 | 87C4E79D28DE0B79005955DA /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | 87C4E79E28DE0B79005955DA /* Build configuration list for PBXNativeTarget "RippleScrollBarDemo" */ = {
356 | isa = XCConfigurationList;
357 | buildConfigurations = (
358 | 87C4E79F28DE0B79005955DA /* Debug */,
359 | 87C4E7A028DE0B79005955DA /* Release */,
360 | );
361 | defaultConfigurationIsVisible = 0;
362 | defaultConfigurationName = Release;
363 | };
364 | /* End XCConfigurationList section */
365 | };
366 | rootObject = 87C4E78228DE0B78005955DA /* Project object */;
367 | }
368 |
--------------------------------------------------------------------------------