├── Assets ├── about.png ├── SocialBanner.png ├── timingFunctions.png └── ALProgressRing.sketch ├── ExampleApp ├── iOS App │ ├── Sources │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── ipad-20x20.png │ │ │ │ ├── ipad-29x29.png │ │ │ │ ├── ipad-40x40.png │ │ │ │ ├── ipad-76x76.png │ │ │ │ ├── ipad-20x20@2x.png │ │ │ │ ├── ipad-29x29@2x.png │ │ │ │ ├── ipad-40x40@2x.png │ │ │ │ ├── ipad-76x76@2x.png │ │ │ │ ├── iphone-20x20@2x.png │ │ │ │ ├── iphone-20x20@3x.png │ │ │ │ ├── iphone-29x29@2x.png │ │ │ │ ├── iphone-29x29@3x.png │ │ │ │ ├── iphone-40x40@2x.png │ │ │ │ ├── iphone-40x40@3x.png │ │ │ │ ├── iphone-60x60@2x.png │ │ │ │ ├── iphone-60x60@3x.png │ │ │ │ ├── ipad-83.5x83.5@2x.png │ │ │ │ ├── ios-marketing-1024x1024.png │ │ │ │ └── Contents.json │ │ └── Info.plist │ ├── AppDelegate.swift │ └── Controllers │ │ ├── Launch │ │ └── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ ├── AnimationViewController.swift │ │ └── ViewController.swift └── ALRingView Example.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ ├── xcuserdata │ └── alxrguz.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── xcshareddata │ └── xcschemes │ │ └── iOS App.xcscheme │ └── project.pbxproj ├── .gitignore ├── Package.swift ├── ALProgressView.podspec ├── LICENSE ├── Sources └── ALProgressView │ ├── Model │ └── ALTimingFunction.swift │ └── Views │ ├── ALProgressBar.swift │ └── ALProgressRing.swift └── README.md /Assets/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/Assets/about.png -------------------------------------------------------------------------------- /Assets/SocialBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/Assets/SocialBanner.png -------------------------------------------------------------------------------- /Assets/timingFunctions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/Assets/timingFunctions.png -------------------------------------------------------------------------------- /Assets/ALProgressRing.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/Assets/ALProgressRing.sketch -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@3x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@3x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@3x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@3x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ios-marketing-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxrguz/ALProgressView/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ios-marketing-1024x1024.png -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/xcuserdata/alxrguz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # osX files 2 | .DS_Store 3 | .Trashes 4 | Icon 5 | Icon? 6 | 7 | ## Xcode Patch 8 | *.xcworkspace 9 | *.xcuserdata 10 | *.xcodeproj/* 11 | !*.xcodeproj/project.pbxproj 12 | !*.xcodeproj/xcshareddata/ 13 | !*.xcworkspace/contents.xcworkspacedata 14 | /*.gcno 15 | 16 | ### Xcode Patch ### 17 | **/xcshareddata/WorkspaceSettings.xcsettings 18 | 19 | # Swift Package Manager 20 | .swiftpm 21 | /.build 22 | 23 | ## Build generated 24 | build/ 25 | DerivedData/ -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "SnapKit", 6 | "repositoryURL": "https://github.com/SnapKit/SnapKit", 7 | "state": { 8 | "branch": null, 9 | "revision": "d458564516e5676af9c70b4f4b2a9178294f1bc6", 10 | "version": "5.0.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "ALProgressView", 8 | platforms: [ 9 | .iOS(.v10) 10 | ], 11 | products: [ 12 | .library(name: "ALProgressView", targets: ["ALProgressView"]), 13 | ], 14 | targets: [ 15 | .target(name: "ALProgressView"), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // 4 | // Created by Alexandr Guzenko on 17.08.2020. 5 | // Copyright © 2020. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | var window: UIWindow? 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | window = UIWindow() 15 | window?.rootViewController = MainViewController() 16 | window?.makeKeyAndVisible() 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /ALProgressView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "ALProgressView" 3 | spec.version = "2.0.0" 4 | spec.summary = "Animated and fully customizable progress ring with gradient" 5 | 6 | spec.homepage = "https://github.com/alxrguz/ALProgressView" 7 | spec.source = { :git => "https://github.com/alxrguz/ALProgressView.git", :tag => "#{spec.version}" } 8 | spec.license = { :type => "MIT", :file => "LICENSE" } 9 | 10 | spec.author = { "Alexandr Guzenko" => "alxrguz@icloud.com" } 11 | 12 | spec.platform = :ios 13 | spec.ios.framework = 'UIKit' 14 | spec.swift_version = ['4.2', '5.0'] 15 | spec.ios.deployment_target = "10.0" 16 | 17 | spec.source_files = "Sources/ALProgressView/**/*.swift" 18 | 19 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexandr Guzenko 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 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | ALProgressView 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/xcuserdata/alxrguz.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SnapKitPlayground (Playground) 1.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 3 13 | 14 | SnapKitPlayground (Playground) 2.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 4 20 | 21 | SnapKitPlayground (Playground) 3.xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 4 27 | 28 | SnapKitPlayground (Playground) 4.xcscheme 29 | 30 | isShown 31 | 32 | orderHint 33 | 5 34 | 35 | SnapKitPlayground (Playground) 5.xcscheme 36 | 37 | isShown 38 | 39 | orderHint 40 | 6 41 | 42 | SnapKitPlayground (Playground).xcscheme 43 | 44 | isShown 45 | 46 | orderHint 47 | 1 48 | 49 | iOS App.xcscheme_^#shared#^_ 50 | 51 | orderHint 52 | 0 53 | 54 | 55 | SuppressBuildableAutocreation 56 | 57 | BB3959F224EA71EE00B965F5 58 | 59 | primary 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Controllers/Launch/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 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Controllers/AnimationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationViewController.swift 3 | // iOS App 4 | // 5 | // Created by Alexandr Guzenko on 17.08.2021. 6 | // Copyright © 2021 alxrguz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ALProgressView 11 | 12 | final class AnimationViewController: UITableViewController { 13 | // MARK: - UI Elements 14 | 15 | private lazy var closeButton = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(dismissController)) 16 | 17 | // MARK: - Public Proporties 18 | var timingFunctionUpdated: ((ALTimingFunction) -> Void)? 19 | 20 | // MARK: - Private Proporties 21 | private let currentFunction: ALTimingFunction 22 | 23 | // MARK: - Life cycle 24 | init(currentFunction: ALTimingFunction) { 25 | self.currentFunction = currentFunction 26 | super.init(style: .insetGrouped) 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | setupView() 36 | } 37 | } 38 | 39 | // MARK: - Handlers 40 | private extension AnimationViewController { 41 | @objc func dismissController() { 42 | dismiss(animated: true) 43 | } 44 | } 45 | 46 | // MARK: - Open Methods 47 | extension AnimationViewController { 48 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 | ALTimingFunction.allCases.count 50 | } 51 | 52 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 53 | let cell = UITableViewCell() 54 | let data = ALTimingFunction.allCases[indexPath.row] 55 | cell.textLabel?.text = data.rawValue 56 | cell.accessoryType = data == currentFunction ? .checkmark : .none 57 | return cell 58 | } 59 | 60 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 61 | let data = ALTimingFunction.allCases[indexPath.row] 62 | timingFunctionUpdated?(data) 63 | dismissController() 64 | } 65 | } 66 | 67 | 68 | // MARK: - Layout Setup 69 | private extension AnimationViewController { 70 | func setupView() { 71 | navigationItem.title = "Timing Function" 72 | navigationItem.rightBarButtonItem = closeButton 73 | 74 | tableView.tintColor = .systemPink 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "iphone-20x20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "iphone-20x20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "iphone-29x29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "iphone-29x29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "iphone-40x40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "iphone-40x40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "iphone-60x60@2x.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "iphone-60x60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "ipad-20x20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "ipad-20x20@2x.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "ipad-29x29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "ipad-29x29@2x.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "ipad-40x40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "ipad-40x40@2x.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "ipad-76x76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "ipad-76x76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "ipad-83.5x83.5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "ios-marketing-1024x1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/xcshareddata/xcschemes/iOS App.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Sources/ALProgressView/Model/ALTimingFunction.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com) 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 | 23 | import UIKit.UIKitCore 24 | 25 | // https://easings.net 26 | public enum ALTimingFunction: String, CaseIterable, Hashable { 27 | case `default` 28 | case linear 29 | case easeIn 30 | case easeOut 31 | case easeInEaseOut 32 | case easeInSine 33 | case easeOutSine 34 | case easeInOutSine 35 | case easeInQuad 36 | case easeOutQuad 37 | case easeInOutQuad 38 | case easeInCubic 39 | case easeOutCubic 40 | case easeInOutCubic 41 | case easeInQuart 42 | case easeOutQuart 43 | case easeInOutQuart 44 | case easeInQuint 45 | case easeOutQuint 46 | case easeInOutQuint 47 | case easeInExpo 48 | case easeOutExpo 49 | case easeInOutExpo 50 | case easeInCirc 51 | case easeOutCirc 52 | case easeInOutCirc 53 | case easeInBack 54 | case easeOutBack 55 | case easeInOutBack 56 | 57 | var function: CAMediaTimingFunction { 58 | switch self { 59 | case .`default`: 60 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.default) 61 | case .linear: 62 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) 63 | case .easeIn: 64 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 65 | case .easeOut: 66 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 67 | case .easeInEaseOut: 68 | return CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) 69 | case .easeInSine: 70 | return CAMediaTimingFunction(controlPoints: 0.47, 0, 0.745, 0.715) 71 | case .easeOutSine: 72 | return CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1) 73 | case .easeInOutSine: 74 | return CAMediaTimingFunction(controlPoints: 0.445, 0.05, 0.55, 0.95) 75 | case .easeInQuad: 76 | return CAMediaTimingFunction(controlPoints: 0.55, 0.085, 0.68, 0.53) 77 | case .easeOutQuad: 78 | return CAMediaTimingFunction(controlPoints: 0.25, 0.46, 0.45, 0.94) 79 | case .easeInOutQuad: 80 | return CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955) 81 | case .easeInCubic: 82 | return CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19) 83 | case .easeOutCubic: 84 | return CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1) 85 | case .easeInOutCubic: 86 | return CAMediaTimingFunction(controlPoints: 0.645, 0.045, 0.355, 1) 87 | case .easeInQuart: 88 | return CAMediaTimingFunction(controlPoints: 0.895, 0.03, 0.685, 0.22) 89 | case .easeOutQuart: 90 | return CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1) 91 | case .easeInOutQuart: 92 | return CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1) 93 | case .easeInQuint: 94 | return CAMediaTimingFunction(controlPoints: 0.755, 0.05, 0.855, 0.06) 95 | case .easeOutQuint: 96 | return CAMediaTimingFunction(controlPoints: 0.23, 1, 0.32, 1) 97 | case .easeInOutQuint: 98 | return CAMediaTimingFunction(controlPoints: 0.86, 0, 0.07, 1) 99 | case .easeInExpo: 100 | return CAMediaTimingFunction(controlPoints: 0.95, 0.05, 0.795, 0.035) 101 | case .easeOutExpo: 102 | return CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1) 103 | case .easeInOutExpo: 104 | return CAMediaTimingFunction(controlPoints: 1, 0, 0, 1) 105 | case .easeInCirc: 106 | return CAMediaTimingFunction(controlPoints: 0.6, 0.04, 0.98, 0.335) 107 | case .easeOutCirc: 108 | return CAMediaTimingFunction(controlPoints: 0.075, 0.82, 0.165, 1) 109 | case .easeInOutCirc: 110 | return CAMediaTimingFunction(controlPoints: 0.785, 0.135, 0.15, 0.86) 111 | case .easeInBack: 112 | return CAMediaTimingFunction(controlPoints: 0.6, -0.28, 0.735, 0.045) 113 | case .easeOutBack: 114 | return CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.275) 115 | case .easeInOutBack: 116 | return CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ALProgressView 2 | 3 | 4 | 5 | 6 | 7 |   8 | 9 | ## Navigation 10 | 11 | - [Requirements](#requirements) 12 | - [Installation](#installation) 13 | - [Swift Package Manager](#Swift-Package-Manager) 14 | - [CocoaPods](#CocoaPods) 15 | - [Manually](#Manually) 16 | - [Usage](#usage) 17 | - [Quick Start](#Quick-Start) 18 | - [Customization](#Customization) 19 | - [General](#General) 20 | - [Colors](#colors) 21 | - [Animation](#Animation) 22 | - [ALProgressRing](#ALProgressRing) 23 | - [ALProgressBar](#ALProgressBar) 24 | - [License](#License) 25 | 26 | 27 | 28 | ## Requirements 29 | 30 | - iOS 10.0 + 31 | - Swift 4.2 + 32 | 33 | 34 | 35 | ## Installation 36 | 37 | #### Swift Package Manager 38 | 39 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies. 40 | 41 | To integrate **ALProgressView** click `File -> Swift Package -> Add Package Dependency` and insert: 42 | 43 | ```ogdl 44 | https://github.com/alxrguz/ALProgressView 45 | ``` 46 | 47 | #### CocoaPods 48 | 49 | **ALProgressView** is available through [CocoaPods](https://cocoapods.org/pods/ALProgressView). To install it, simply add the following line to your Podfile: 50 | 51 | ```ruby 52 | pod 'ALProgressView' 53 | ``` 54 | 55 | #### Manually 56 | 57 | If you prefer not to use either of the aforementioned dependency managers, you can integrate ALProgressView into your project manually. Put `Source/ALProgressView` folder in your Xcode project. 58 | 59 | 60 | 61 | ## Usage 62 | 63 | #### Quick Start 64 | 65 | ```swift 66 | import ALProgressView 67 | 68 | class MyViewController: UIViewController { 69 | 70 | private lazy var progressRing = ALProgressRing() 71 | private lazy var progressBar = ALProgressBar() 72 | 73 | override func viewDidLoad() { 74 | super.viewDidLoad() 75 | 76 | view.addSubview(progressRing) 77 | view.addSubview(progressRing) 78 | 79 | // MARK: ALProgressRing 80 | 81 | // Setup layout 82 | progressRing.translatesAutoresizingMaskIntoConstraints = false 83 | progressRing.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 84 | progressRing.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 85 | // Make sure to set the view size 86 | progressRing.widthAnchor.constraint(equalToConstant: 180).isActive = true 87 | progressRing.heightAnchor.constraint(equalToConstant: 180).isActive = true 88 | 89 | 90 | // MARK: ALProgressBar 91 | 92 | // Setup layout 93 | progressBar.translatesAutoresizingMaskIntoConstraints = false 94 | progressBar.topAnchor.constraint(equalTo: progressRing.bottomAnchor, constant: 20).isActive = true 95 | progressBar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 96 | progressBar.widthAnchor.constraint(equalTo: titleLabel.widthAnchor).isActive = true 97 | // Make sure to set the view height 98 | progressBar.heightAnchor.constraint(equalToConstant: 10).isActive = true 99 | 100 | 101 | // Setting progress 102 | progressRing.setProgress(0.8, animated: true) 103 | progressBar.setProgress(0.6, animated: true) 104 | } 105 | } 106 | ``` 107 | 108 | You can also run ExampleApp and play with the parameters there 109 | 110 | 111 | 112 | #### Customization 113 | 114 | ##### General 115 | 116 | ###### Colors 117 | 118 | You can customize the buttons, headers and indicators colors depending on their state. 119 | 120 | ```swift 121 | /// Set track color 122 | /// If you don't need a gradient, just set the same values for `startColor` and `endColor` 123 | progressView.startColor = .systemPink 124 | progressView.endColor = .systemRed 125 | 126 | /// Set groove color 127 | progressView.grooveColor = .systemRed 128 | ``` 129 | 130 | ###### Animation 131 | 132 | ```swift 133 | progressView.duration = 2 // Duration of the ring's fill animation. 134 | progressView.timingFunction = .easeOutExpo // Timing function of the ring's fill animation. 135 | 136 | /// The following use of setProgress (_ value: Int, animated: Bool) 137 | /// will take into account the new parameters 138 | progressView.setProgress(0.8, animated: true) 139 | ``` 140 | 141 | Let's take a look at the `timingFunction` parameter, it is of type `ALTimingFunction`, which contains the following types of animations: 142 | 143 | 144 | 145 | There is a good cheat sheet available [here](http://easings.net/). 146 | 147 | ##### AlProgressRing 148 | 149 | ```swift 150 | progressBar.startAngle = -.pi / 2 // The start angle of the ring to begin drawing. 151 | progressRing.endAngle = 1.5 * .pi // The end angle of the ring to end drawing. 152 | progressRing.startGradientPoint = .init(x: 0.5, y: 0) // The starting poin of the gradient 153 | progressRing.endGradientPoint = .init(x: 0.5, y: 1) // The ending position of the gradient. 154 | 155 | // Sets the line width for progress ring and "groove" ring 156 | progressRing.lineWidth = 10 157 | // Or, if you need to separate these parameters, use 158 | progressRing.ringWidth = 10 159 | progressRing.grooveWidth = 8 160 | ``` 161 | 162 | 163 | 164 | ##### AlProgressBar 165 | 166 | ```swift 167 | progressBar.barEdgeInset = 1.5 // Distance between groove and progress bar. 168 | ``` 169 | 170 | 171 | 172 | ## License 173 | 174 | **ALProgressView** is under MIT license. See the [LICENSE](https://github.com/alxrguz/ALProgressView/blob/main/LICENSE) file for more info. 175 | 176 | -------------------------------------------------------------------------------- /ExampleApp/iOS App/Controllers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // 4 | // Created by Alexandr Guzenko on 17.08.2020. 5 | // Copyright © 2020. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | import SnapKit 10 | import ALProgressView 11 | 12 | class MainViewController: UIViewController { 13 | 14 | // MARK: - UIElements 15 | 16 | private lazy var progressRing = ALProgressRing() 17 | private lazy var progressBar = ALProgressBar() 18 | private lazy var titleLabel = UILabel() 19 | private lazy var subtitleLabel = UILabel() 20 | private lazy var emojiLabel = UILabel() 21 | private lazy var actionButton = UIButton() 22 | private lazy var animationButton = UIButton() 23 | 24 | // MARK: - Private Properties 25 | private var currentFunction: ALTimingFunction = .easeOutExpo 26 | 27 | // MARK: - UIViewController 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | setupView() 32 | setupConstraints() 33 | setupAction() 34 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in 35 | progressRing.setProgress(0.8, animated: true) 36 | progressBar.setProgress(0.6, animated: true) 37 | } 38 | } 39 | } 40 | 41 | // MARK: - Handlers 42 | 43 | private extension MainViewController { 44 | @objc func randomizeTapped() { 45 | let progress = Float.random(in: 0...1) 46 | progressRing.setProgress(progress, animated: true) 47 | progressBar.setProgress(progress, animated: true) 48 | } 49 | } 50 | 51 | // MARK: - Private Methods 52 | 53 | private extension MainViewController { 54 | func setupAction() { 55 | actionButton.addTarget(self, action: #selector(randomizeTapped), for: .touchUpInside) 56 | animationButton.addTarget(self, action: #selector(presentTimingFunctions), for: .touchUpInside) 57 | } 58 | 59 | func updateTimingFunction(_ function: ALTimingFunction) { 60 | currentFunction = function 61 | progressRing.timingFunction = currentFunction 62 | progressBar.timingFunction = currentFunction 63 | animationButton.setTitle("Animation: ." + currentFunction.rawValue , for: .normal) 64 | } 65 | } 66 | 67 | // MARK: - Navigation 68 | 69 | private extension MainViewController { 70 | @objc func presentTimingFunctions() { 71 | let vc = AnimationViewController(currentFunction: currentFunction) 72 | let navVC = UINavigationController(rootViewController: vc) 73 | 74 | vc.timingFunctionUpdated = { [weak self] function in 75 | self?.updateTimingFunction(function) 76 | } 77 | 78 | present(navVC, animated: true) 79 | } 80 | } 81 | 82 | 83 | // MARK: - Setup Layout 84 | 85 | private extension MainViewController { 86 | func setupColors() { 87 | view.backgroundColor = .systemBackground 88 | titleLabel.textColor = .label 89 | subtitleLabel.textColor = .secondaryLabel 90 | actionButton.backgroundColor = .secondarySystemFill 91 | actionButton.setTitleColor(.systemPink, for: .normal) 92 | actionButton.setTitleColor(.systemPink.withAlphaComponent(0.7), for: .highlighted) 93 | 94 | animationButton.setTitleColor(.systemPink, for: .normal) 95 | animationButton.setTitleColor(.systemPink.withAlphaComponent(0.7), for: .highlighted) 96 | } 97 | 98 | func setupView() { 99 | setupColors() 100 | 101 | progressRing.lineWidth = 15 102 | 103 | emojiLabel.text = "✌️" 104 | emojiLabel.font = .systemFont(ofSize: 80, weight: .regular) 105 | 106 | titleLabel.text = "ALProgressView" 107 | titleLabel.font = .systemFont(ofSize: 32, weight: .bold) 108 | 109 | subtitleLabel.text = "Easy, simple, customizable" 110 | subtitleLabel.font = .systemFont(ofSize: 21, weight: .regular) 111 | 112 | actionButton.setTitle("Random progress", for: .normal) 113 | actionButton.titleLabel?.font = .systemFont(ofSize: 17, weight: .medium) 114 | actionButton.layer.cornerRadius = 12 115 | 116 | animationButton.setTitle("Animation: ." + currentFunction.rawValue , for: .normal) 117 | animationButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) 118 | } 119 | 120 | func setupConstraints() { 121 | view.addSubview(progressRing) 122 | progressRing.addSubview(emojiLabel) 123 | view.addSubview(titleLabel) 124 | view.addSubview(subtitleLabel) 125 | view.addSubview(progressBar) 126 | view.addSubview(actionButton) 127 | view.addSubview(animationButton) 128 | 129 | progressRing.snp.makeConstraints { 130 | $0.size.equalTo(180) 131 | $0.centerX.equalToSuperview() 132 | $0.top.equalTo(view.safeAreaLayoutGuide).offset(70) 133 | } 134 | 135 | emojiLabel.snp.makeConstraints { 136 | $0.center.equalToSuperview() 137 | } 138 | 139 | titleLabel.snp.makeConstraints { 140 | $0.top.equalTo(progressRing.snp.bottom).offset(40) 141 | $0.centerX.equalToSuperview() 142 | } 143 | 144 | subtitleLabel.snp.makeConstraints { 145 | $0.top.equalTo(titleLabel.snp.bottom).offset(8) 146 | $0.centerX.equalToSuperview() 147 | } 148 | 149 | progressBar.snp.makeConstraints { 150 | $0.top.equalTo(subtitleLabel.snp.bottom).offset(20) 151 | $0.width.equalTo(titleLabel) 152 | $0.height.equalTo(10) 153 | $0.centerX.equalToSuperview() 154 | } 155 | 156 | 157 | actionButton.snp.makeConstraints { 158 | $0.bottom.equalTo(animationButton.snp.top).offset(-10) 159 | $0.leading.trailing.equalToSuperview().inset(25) 160 | $0.height.equalTo(50) 161 | } 162 | 163 | animationButton.snp.makeConstraints { 164 | $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-10) 165 | $0.leading.trailing.equalToSuperview().inset(25) 166 | $0.height.equalTo(35) 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Sources/ALProgressView/Views/ALProgressBar.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com) 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 | 23 | import UIKit 24 | 25 | /// A fillable progress ring drawing. 26 | open class ALProgressBar: UIView { 27 | 28 | // MARK: Properties 29 | 30 | /// Distance between groove and progress bar. Default is 0 31 | public var barEdgeInset: CGFloat = 0 { 32 | didSet { configureBar(); styleBarLayer() } 33 | } 34 | 35 | /// The first gradient color of the track. 36 | public var startColor: UIColor = .systemPink { 37 | didSet { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } 38 | } 39 | 40 | /// The second gradient color of the track. 41 | public var endColor: UIColor = .systemRed { 42 | didSet { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } 43 | } 44 | 45 | /// The groove color in which the fillable ring resides. 46 | public var grooveColor: UIColor = UIColor.systemGray.withAlphaComponent(0.2) { 47 | didSet { barLayer.strokeColor = grooveColor.cgColor } 48 | } 49 | 50 | /// The starting poin of the gradient. Default is (x: 0, y: 0.5) 51 | public var startGradientPoint: CGPoint = .init(x: 0, y: 0.5) { 52 | didSet { gradientLayer.startPoint = startGradientPoint } 53 | } 54 | 55 | /// The ending position of the gradient. Default is (x: 1, y: 0.5) 56 | public var endGradientPoint: CGPoint = .init(x: 1, y: 0.5) { 57 | didSet { gradientLayer.endPoint = endGradientPoint } 58 | } 59 | 60 | /// Duration of the ring's fill animation. Default is 2.0 61 | public var duration: TimeInterval = 2.0 62 | 63 | /// Timing function of the ring's fill animation. Default is `.easeOutExpo` 64 | public var timingFunction: ALTimingFunction = .easeOutExpo 65 | 66 | /// The progress of the ring between 0 and 1. The ring will fill based on the value. 67 | public private(set) var progress: CGFloat = 0 68 | 69 | private let ringLayer: CAShapeLayer = { 70 | let layer = CAShapeLayer() 71 | layer.lineCap = .round 72 | layer.fillColor = nil 73 | layer.strokeStart = 0 74 | layer.strokeEnd = 1 75 | return layer 76 | }() 77 | 78 | private let barLayer: CAShapeLayer = { 79 | let layer = CAShapeLayer() 80 | layer.lineCap = .round 81 | layer.fillColor = nil 82 | layer.strokeStart = 0 83 | layer.strokeEnd = 1 84 | return layer 85 | }() 86 | 87 | private let gradientLayer = CAGradientLayer() 88 | 89 | // MARK: Life Cycle 90 | public init() { 91 | super.init(frame: .zero) 92 | setup() 93 | } 94 | 95 | override public init(frame: CGRect) { 96 | super.init(frame: frame) 97 | setup() 98 | } 99 | 100 | public required init?(coder: NSCoder) { 101 | super.init(coder: coder) 102 | setup() 103 | } 104 | 105 | public override func layoutSubviews() { 106 | super.layoutSubviews() 107 | configureBar() 108 | styleBarLayer() 109 | } 110 | 111 | public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 112 | super.traitCollectionDidChange(previousTraitCollection) 113 | styleBarLayer() 114 | } 115 | 116 | // MARK: Methods 117 | 118 | /// Set the progress value of the ring. The ring will fill based on the value. 119 | /// 120 | /// - Parameters: 121 | /// - value: Progress value between 0 and 1. 122 | /// - animated: Flag for the fill ring's animation. 123 | /// - completion: Closure called after animation ends 124 | public func setProgress(_ value: Float, animated: Bool, completion: (() -> Void)? = nil) { 125 | layoutIfNeeded() 126 | let value = CGFloat(min(value, 1.0)) 127 | let oldValue = ringLayer.presentation()?.strokeEnd ?? progress 128 | progress = value 129 | ringLayer.strokeEnd = progress 130 | guard animated else { 131 | layer.removeAllAnimations() 132 | ringLayer.removeAllAnimations() 133 | gradientLayer.removeAllAnimations() 134 | completion?() 135 | return 136 | } 137 | 138 | CATransaction.begin() 139 | let path = #keyPath(CAShapeLayer.strokeEnd) 140 | let fill = CABasicAnimation(keyPath: path) 141 | fill.fromValue = oldValue 142 | fill.toValue = value 143 | fill.duration = duration 144 | fill.timingFunction = timingFunction.function 145 | CATransaction.setCompletionBlock(completion) 146 | ringLayer.add(fill, forKey: "fill") 147 | CATransaction.commit() 148 | } 149 | 150 | 151 | private func setup() { 152 | preservesSuperviewLayoutMargins = true 153 | layer.addSublayer(barLayer) 154 | layer.addSublayer(gradientLayer) 155 | styleBarLayer() 156 | setProgress(Float(progress), animated: false) 157 | } 158 | 159 | private func styleBarLayer() { 160 | barLayer.strokeColor = grooveColor.cgColor 161 | barLayer.lineWidth = frame.height 162 | 163 | ringLayer.lineWidth = frame.height - (barEdgeInset * 2) 164 | ringLayer.strokeColor = startColor.cgColor 165 | ringLayer.strokeEnd = min(progress, 1.0) 166 | 167 | gradientLayer.colors = [startColor.cgColor, endColor.cgColor] 168 | gradientLayer.startPoint = startGradientPoint 169 | gradientLayer.endPoint = endGradientPoint 170 | } 171 | 172 | private func configureBar() { 173 | let inset = frame.height / 2 174 | let barInset = barEdgeInset / 2 175 | 176 | let grooveLinePath = UIBezierPath() 177 | grooveLinePath.move(to: CGPoint(x: inset, y: bounds.midY)) 178 | grooveLinePath.addLine(to: CGPoint(x: bounds.width - inset, y: bounds.midY)) 179 | barLayer.path = grooveLinePath.cgPath 180 | 181 | 182 | let barLinePath = UIBezierPath() 183 | barLinePath.move(to: CGPoint(x: inset + barInset, y: bounds.midY)) 184 | barLinePath.addLine(to: CGPoint(x: bounds.width - (inset + barInset), y: bounds.midY)) 185 | ringLayer.path = barLinePath.cgPath 186 | 187 | [barLayer, ringLayer, gradientLayer].forEach { $0.frame = bounds } 188 | gradientLayer.mask = ringLayer 189 | } 190 | } 191 | 192 | -------------------------------------------------------------------------------- /Sources/ALProgressView/Views/ALProgressRing.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com) 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 | 23 | import UIKit 24 | 25 | /// A fillable progress ring drawing. 26 | open class ALProgressRing: UIView { 27 | 28 | // MARK: Properties 29 | 30 | /// Sets the line width for progress ring and groove ring. 31 | /// - Note: If you need separate customization use the `ringWidth` and `grooveWidth` properties 32 | public var lineWidth: CGFloat = 10 { 33 | didSet { 34 | ringWidth = lineWidth 35 | grooveWidth = lineWidth 36 | } 37 | } 38 | 39 | /// The line width of the progress ring. 40 | public var ringWidth: CGFloat = 10 { 41 | didSet { 42 | ringLayer.lineWidth = ringWidth 43 | } 44 | } 45 | 46 | /// The line width of the groove ring. 47 | public var grooveWidth: CGFloat = 10 { 48 | didSet { 49 | grooveLayer.lineWidth = grooveWidth 50 | } 51 | } 52 | 53 | /// The first gradient color of the track. 54 | public var startColor: UIColor = .systemPink { 55 | didSet { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } 56 | } 57 | 58 | /// The second gradient color of the track. 59 | public var endColor: UIColor = .systemRed { 60 | didSet { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } 61 | } 62 | 63 | /// The groove color in which the fillable ring resides. 64 | public var grooveColor: UIColor = UIColor.systemGray.withAlphaComponent(0.2) { 65 | didSet { grooveLayer.strokeColor = grooveColor.cgColor } 66 | } 67 | 68 | /// The start angle of the ring to begin drawing. 69 | public var startAngle: CGFloat = -.pi / 2 { 70 | didSet { ringLayer.path = ringPath() } 71 | } 72 | 73 | /// The end angle of the ring to end drawing. 74 | public var endAngle: CGFloat = 1.5 * .pi { 75 | didSet { ringLayer.path = ringPath() } 76 | } 77 | 78 | /// The starting poin of the gradient. Default is (x: 0.5, y: 0) 79 | public var startGradientPoint: CGPoint = .init(x: 0.5, y: 0) { 80 | didSet { gradientLayer.startPoint = startGradientPoint } 81 | } 82 | 83 | /// The ending position of the gradient. Default is (x: 0.5, y: 1) 84 | public var endGradientPoint: CGPoint = .init(x: 0.5, y: 1) { 85 | didSet { gradientLayer.endPoint = endGradientPoint } 86 | } 87 | 88 | /// Duration of the ring's fill animation. Default is 2.0 89 | public var duration: TimeInterval = 2.0 90 | 91 | /// Timing function of the ring's fill animation. Default is `.easeOutExpo` 92 | public var timingFunction: ALTimingFunction = .easeOutExpo 93 | 94 | /// The radius of the ring. 95 | public var ringRadius: CGFloat { 96 | var radius = min(bounds.height, bounds.width) / 2 - ringWidth / 2 97 | if ringWidth < grooveWidth { 98 | radius -= (grooveWidth - ringWidth) / 2 99 | } 100 | return radius 101 | } 102 | 103 | /// The radius of the groove. 104 | public var grooveRadius: CGFloat { 105 | var radius = min(bounds.height, bounds.width) / 2 - grooveWidth / 2 106 | if grooveWidth < ringWidth { 107 | radius -= (ringWidth - grooveWidth) / 2 108 | } 109 | return radius 110 | } 111 | 112 | /// The progress of the ring between 0 and 1. The ring will fill based on the value. 113 | public private(set) var progress: CGFloat = 0 114 | 115 | private let ringLayer: CAShapeLayer = { 116 | let layer = CAShapeLayer() 117 | layer.lineCap = .round 118 | layer.fillColor = nil 119 | layer.strokeStart = 0 120 | return layer 121 | }() 122 | 123 | private let grooveLayer: CAShapeLayer = { 124 | let layer = CAShapeLayer() 125 | layer.lineCap = .round 126 | layer.fillColor = nil 127 | layer.strokeStart = 0 128 | layer.strokeEnd = 1 129 | return layer 130 | }() 131 | 132 | private let gradientLayer = CAGradientLayer() 133 | 134 | // MARK: Life Cycle 135 | public init() { 136 | super.init(frame: .zero) 137 | setup() 138 | } 139 | 140 | override public init(frame: CGRect) { 141 | super.init(frame: frame) 142 | setup() 143 | } 144 | 145 | public required init?(coder: NSCoder) { 146 | super.init(coder: coder) 147 | setup() 148 | } 149 | 150 | public override func layoutSubviews() { 151 | super.layoutSubviews() 152 | configureRing() 153 | styleRingLayer() 154 | } 155 | 156 | public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 157 | super.traitCollectionDidChange(previousTraitCollection) 158 | styleRingLayer() 159 | } 160 | 161 | // MARK: Methods 162 | 163 | /// Set the progress value of the ring. The ring will fill based on the value. 164 | /// 165 | /// - Parameters: 166 | /// - value: Progress value between 0 and 1. 167 | /// - animated: Flag for the fill ring's animation. 168 | /// - completion: Closure called after animation ends 169 | public func setProgress(_ value: Float, animated: Bool, completion: (() -> Void)? = nil) { 170 | layoutIfNeeded() 171 | let value = CGFloat(min(value, 1.0)) 172 | let oldValue = ringLayer.presentation()?.strokeEnd ?? progress 173 | progress = value 174 | ringLayer.strokeEnd = progress 175 | guard animated else { 176 | layer.removeAllAnimations() 177 | ringLayer.removeAllAnimations() 178 | gradientLayer.removeAllAnimations() 179 | completion?() 180 | return 181 | } 182 | 183 | CATransaction.begin() 184 | let path = #keyPath(CAShapeLayer.strokeEnd) 185 | let fill = CABasicAnimation(keyPath: path) 186 | fill.fromValue = oldValue 187 | fill.toValue = value 188 | fill.duration = duration 189 | fill.timingFunction = timingFunction.function 190 | CATransaction.setCompletionBlock(completion) 191 | ringLayer.add(fill, forKey: "fill") 192 | CATransaction.commit() 193 | } 194 | 195 | 196 | private func setup() { 197 | preservesSuperviewLayoutMargins = true 198 | layer.addSublayer(grooveLayer) 199 | layer.addSublayer(gradientLayer) 200 | styleRingLayer() 201 | } 202 | 203 | private func styleRingLayer() { 204 | grooveLayer.strokeColor = grooveColor.cgColor 205 | grooveLayer.lineWidth = grooveWidth 206 | 207 | ringLayer.lineWidth = ringWidth 208 | ringLayer.strokeColor = UIColor.black.cgColor 209 | ringLayer.strokeEnd = min(progress, 1.0) 210 | 211 | gradientLayer.colors = [startColor.cgColor, endColor.cgColor] 212 | gradientLayer.startPoint = CGPoint(x: 0.0, y: 0) 213 | gradientLayer.endPoint = CGPoint(x: 0.0, y: 1) 214 | 215 | gradientLayer.shadowColor = startColor.cgColor 216 | gradientLayer.shadowOffset = .zero 217 | } 218 | 219 | private func configureRing() { 220 | let ringPath = self.ringPath() 221 | let groovePath = self.groovePath() 222 | grooveLayer.frame = bounds 223 | grooveLayer.path = groovePath 224 | 225 | ringLayer.frame = bounds 226 | ringLayer.path = ringPath 227 | 228 | gradientLayer.frame = bounds 229 | gradientLayer.mask = ringLayer 230 | } 231 | 232 | private func ringPath() -> CGPath { 233 | let center = CGPoint(x: bounds.origin.x + frame.width / 2.0, y: bounds.origin.y + frame.height / 2.0) 234 | let circlePath = UIBezierPath(arcCenter: center, radius: ringRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) 235 | return circlePath.cgPath 236 | } 237 | 238 | private func groovePath() -> CGPath { 239 | let center = CGPoint(x: bounds.origin.x + frame.width / 2.0, y: bounds.origin.y + frame.height / 2.0) 240 | let circlePath = UIBezierPath(arcCenter: center, radius: grooveRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) 241 | return circlePath.cgPath 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /ExampleApp/ALRingView Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BB22433626CB8F73008A80C8 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BB22433526CB8F73008A80C8 /* SnapKit */; }; 11 | BB22433C26CB98E8008A80C8 /* AnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB22433B26CB98E8008A80C8 /* AnimationViewController.swift */; }; 12 | BB3959F724EA71EE00B965F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3959F624EA71EE00B965F5 /* AppDelegate.swift */; }; 13 | BB3959FB24EA71EE00B965F5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3959FA24EA71EE00B965F5 /* ViewController.swift */; }; 14 | BB395A0024EA71F200B965F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB3959FF24EA71F200B965F5 /* Assets.xcassets */; }; 15 | BB395A0324EA71F200B965F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */; }; 16 | BB65E1FF266669ED009B68D4 /* ALProgressView in Frameworks */ = {isa = PBXBuildFile; productRef = BB65E1FE266669ED009B68D4 /* ALProgressView */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | BB22433B26CB98E8008A80C8 /* AnimationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationViewController.swift; sourceTree = ""; }; 21 | BB3959F324EA71EE00B965F5 /* iOS App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | BB3959F624EA71EE00B965F5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | BB3959FA24EA71EE00B965F5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 24 | BB3959FF24EA71F200B965F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | BB395A0224EA71F200B965F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | BB395A0424EA71F200B965F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | BB65E1FC266669DF009B68D4 /* ALProgressView */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ALProgressView; path = ..; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | BB3959F024EA71EE00B965F5 /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | BB65E1FF266669ED009B68D4 /* ALProgressView in Frameworks */, 36 | BB22433626CB8F73008A80C8 /* SnapKit in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | BB22433726CB93EC008A80C8 /* Home */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | ); 47 | path = Home; 48 | sourceTree = ""; 49 | }; 50 | BB3959EA24EA71EE00B965F5 = { 51 | isa = PBXGroup; 52 | children = ( 53 | BB65E1FC266669DF009B68D4 /* ALProgressView */, 54 | BB3959F524EA71EE00B965F5 /* iOS App */, 55 | BB3959F424EA71EE00B965F5 /* Products */, 56 | BB65E1FD266669ED009B68D4 /* Frameworks */, 57 | ); 58 | sourceTree = ""; 59 | }; 60 | BB3959F424EA71EE00B965F5 /* Products */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | BB3959F324EA71EE00B965F5 /* iOS App.app */, 64 | ); 65 | name = Products; 66 | sourceTree = ""; 67 | }; 68 | BB3959F524EA71EE00B965F5 /* iOS App */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | BB3959F624EA71EE00B965F5 /* AppDelegate.swift */, 72 | BB395A0A24EA722A00B965F5 /* Controllers */, 73 | BB395A0B24EA723800B965F5 /* Sources */, 74 | ); 75 | path = "iOS App"; 76 | sourceTree = ""; 77 | }; 78 | BB395A0A24EA722A00B965F5 /* Controllers */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | BB22433726CB93EC008A80C8 /* Home */, 82 | BB395A0D24EA727000B965F5 /* Launch */, 83 | BB3959FA24EA71EE00B965F5 /* ViewController.swift */, 84 | BB22433B26CB98E8008A80C8 /* AnimationViewController.swift */, 85 | ); 86 | path = Controllers; 87 | sourceTree = ""; 88 | }; 89 | BB395A0B24EA723800B965F5 /* Sources */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | BB3959FF24EA71F200B965F5 /* Assets.xcassets */, 93 | BB395A0424EA71F200B965F5 /* Info.plist */, 94 | ); 95 | path = Sources; 96 | sourceTree = ""; 97 | }; 98 | BB395A0D24EA727000B965F5 /* Launch */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */, 102 | ); 103 | path = Launch; 104 | sourceTree = ""; 105 | }; 106 | BB65E1FD266669ED009B68D4 /* Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | ); 110 | name = Frameworks; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | BB3959F224EA71EE00B965F5 /* iOS App */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = BB395A0724EA71F200B965F5 /* Build configuration list for PBXNativeTarget "iOS App" */; 119 | buildPhases = ( 120 | BB3959EF24EA71EE00B965F5 /* Sources */, 121 | BB3959F024EA71EE00B965F5 /* Frameworks */, 122 | BB3959F124EA71EE00B965F5 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = "iOS App"; 129 | packageProductDependencies = ( 130 | BB65E1FE266669ED009B68D4 /* ALProgressView */, 131 | BB22433526CB8F73008A80C8 /* SnapKit */, 132 | ); 133 | productName = "Panda School"; 134 | productReference = BB3959F324EA71EE00B965F5 /* iOS App.app */; 135 | productType = "com.apple.product-type.application"; 136 | }; 137 | /* End PBXNativeTarget section */ 138 | 139 | /* Begin PBXProject section */ 140 | BB3959EB24EA71EE00B965F5 /* Project object */ = { 141 | isa = PBXProject; 142 | attributes = { 143 | LastSwiftUpdateCheck = 1160; 144 | LastUpgradeCheck = 1160; 145 | ORGANIZATIONNAME = alxrguz; 146 | TargetAttributes = { 147 | BB3959F224EA71EE00B965F5 = { 148 | CreatedOnToolsVersion = 11.6; 149 | }; 150 | }; 151 | }; 152 | buildConfigurationList = BB3959EE24EA71EE00B965F5 /* Build configuration list for PBXProject "ALRingView Example" */; 153 | compatibilityVersion = "Xcode 9.3"; 154 | developmentRegion = en; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | Base, 159 | ); 160 | mainGroup = BB3959EA24EA71EE00B965F5; 161 | packageReferences = ( 162 | BB22433426CB8F73008A80C8 /* XCRemoteSwiftPackageReference "SnapKit" */, 163 | ); 164 | productRefGroup = BB3959F424EA71EE00B965F5 /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | BB3959F224EA71EE00B965F5 /* iOS App */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXResourcesBuildPhase section */ 174 | BB3959F124EA71EE00B965F5 /* Resources */ = { 175 | isa = PBXResourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | BB395A0324EA71F200B965F5 /* LaunchScreen.storyboard in Resources */, 179 | BB395A0024EA71F200B965F5 /* Assets.xcassets in Resources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXResourcesBuildPhase section */ 184 | 185 | /* Begin PBXSourcesBuildPhase section */ 186 | BB3959EF24EA71EE00B965F5 /* Sources */ = { 187 | isa = PBXSourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | BB22433C26CB98E8008A80C8 /* AnimationViewController.swift in Sources */, 191 | BB3959FB24EA71EE00B965F5 /* ViewController.swift in Sources */, 192 | BB3959F724EA71EE00B965F5 /* AppDelegate.swift in Sources */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXSourcesBuildPhase section */ 197 | 198 | /* Begin PBXVariantGroup section */ 199 | BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */ = { 200 | isa = PBXVariantGroup; 201 | children = ( 202 | BB395A0224EA71F200B965F5 /* Base */, 203 | ); 204 | name = LaunchScreen.storyboard; 205 | sourceTree = ""; 206 | }; 207 | /* End PBXVariantGroup section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | BB395A0524EA71F200B965F5 /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_ENABLE_OBJC_WEAK = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 237 | CLANG_WARN_STRICT_PROTOTYPES = YES; 238 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 239 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 240 | CLANG_WARN_UNREACHABLE_CODE = YES; 241 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 242 | COPY_PHASE_STRIP = NO; 243 | DEBUG_INFORMATION_FORMAT = dwarf; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | ENABLE_TESTABILITY = YES; 246 | GCC_C_LANGUAGE_STANDARD = gnu11; 247 | GCC_DYNAMIC_NO_PIC = NO; 248 | GCC_NO_COMMON_BLOCKS = YES; 249 | GCC_OPTIMIZATION_LEVEL = 0; 250 | GCC_PREPROCESSOR_DEFINITIONS = ( 251 | "DEBUG=1", 252 | "$(inherited)", 253 | ); 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 261 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 262 | MTL_FAST_MATH = YES; 263 | ONLY_ACTIVE_ARCH = YES; 264 | SDKROOT = iphoneos; 265 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 266 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 267 | }; 268 | name = Debug; 269 | }; 270 | BB395A0624EA71F200B965F5 /* Release */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ALWAYS_SEARCH_USER_PATHS = NO; 274 | CLANG_ANALYZER_NONNULL = YES; 275 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_ENABLE_OBJC_WEAK = YES; 281 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_COMMA = YES; 284 | CLANG_WARN_CONSTANT_CONVERSION = YES; 285 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 288 | CLANG_WARN_EMPTY_BODY = YES; 289 | CLANG_WARN_ENUM_CONVERSION = YES; 290 | CLANG_WARN_INFINITE_RECURSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 294 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 297 | CLANG_WARN_STRICT_PROTOTYPES = YES; 298 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 299 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 300 | CLANG_WARN_UNREACHABLE_CODE = YES; 301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 302 | COPY_PHASE_STRIP = NO; 303 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 304 | ENABLE_NS_ASSERTIONS = NO; 305 | ENABLE_STRICT_OBJC_MSGSEND = YES; 306 | GCC_C_LANGUAGE_STANDARD = gnu11; 307 | GCC_NO_COMMON_BLOCKS = YES; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 315 | MTL_ENABLE_DEBUG_INFO = NO; 316 | MTL_FAST_MATH = YES; 317 | SDKROOT = iphoneos; 318 | SWIFT_COMPILATION_MODE = wholemodule; 319 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 320 | VALIDATE_PRODUCT = YES; 321 | }; 322 | name = Release; 323 | }; 324 | BB395A0824EA71F200B965F5 /* Debug */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 328 | CODE_SIGN_STYLE = Automatic; 329 | DEVELOPMENT_TEAM = RW83L2JUQB; 330 | INFOPLIST_FILE = "iOS App/Sources/Info.plist"; 331 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 332 | LD_RUNPATH_SEARCH_PATHS = ( 333 | "$(inherited)", 334 | "@executable_path/Frameworks", 335 | ); 336 | PRODUCT_BUNDLE_IDENTIFIER = alxrguz.alringview.example; 337 | PRODUCT_NAME = "$(TARGET_NAME)"; 338 | SWIFT_VERSION = 5.0; 339 | TARGETED_DEVICE_FAMILY = "1,2"; 340 | }; 341 | name = Debug; 342 | }; 343 | BB395A0924EA71F200B965F5 /* Release */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 347 | CODE_SIGN_STYLE = Automatic; 348 | DEVELOPMENT_TEAM = RW83L2JUQB; 349 | INFOPLIST_FILE = "iOS App/Sources/Info.plist"; 350 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 351 | LD_RUNPATH_SEARCH_PATHS = ( 352 | "$(inherited)", 353 | "@executable_path/Frameworks", 354 | ); 355 | PRODUCT_BUNDLE_IDENTIFIER = alxrguz.alringview.example; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | SWIFT_VERSION = 5.0; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | BB3959EE24EA71EE00B965F5 /* Build configuration list for PBXProject "ALRingView Example" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | BB395A0524EA71F200B965F5 /* Debug */, 369 | BB395A0624EA71F200B965F5 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | BB395A0724EA71F200B965F5 /* Build configuration list for PBXNativeTarget "iOS App" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | BB395A0824EA71F200B965F5 /* Debug */, 378 | BB395A0924EA71F200B965F5 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | 385 | /* Begin XCRemoteSwiftPackageReference section */ 386 | BB22433426CB8F73008A80C8 /* XCRemoteSwiftPackageReference "SnapKit" */ = { 387 | isa = XCRemoteSwiftPackageReference; 388 | repositoryURL = "https://github.com/SnapKit/SnapKit"; 389 | requirement = { 390 | kind = upToNextMajorVersion; 391 | minimumVersion = 5.0.1; 392 | }; 393 | }; 394 | /* End XCRemoteSwiftPackageReference section */ 395 | 396 | /* Begin XCSwiftPackageProductDependency section */ 397 | BB22433526CB8F73008A80C8 /* SnapKit */ = { 398 | isa = XCSwiftPackageProductDependency; 399 | package = BB22433426CB8F73008A80C8 /* XCRemoteSwiftPackageReference "SnapKit" */; 400 | productName = SnapKit; 401 | }; 402 | BB65E1FE266669ED009B68D4 /* ALProgressView */ = { 403 | isa = XCSwiftPackageProductDependency; 404 | productName = ALProgressView; 405 | }; 406 | /* End XCSwiftPackageProductDependency section */ 407 | }; 408 | rootObject = BB3959EB24EA71EE00B965F5 /* Project object */; 409 | } 410 | --------------------------------------------------------------------------------