├── .gitignore ├── Screenshot.png ├── Example ├── Assets.xcassets │ ├── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── AppDelegate.swift ├── SceneDelegate.swift ├── Info.plist ├── ViewController.swift └── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Example.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── Package.swift ├── WARangeSlider.podspec ├── LICENSE ├── README.md └── Sources └── WARangeSlider └── RangeSlider.swift /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Carthage/* 3 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warchimede/RangeSlider/HEAD/Screenshot.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | 3 | import PackageDescription 4 | 5 | let name = "WARangeSlider" 6 | let platforms: [SupportedPlatform] = [.iOS(.v12)] 7 | let products: [Product] = [.library(name: name, targets: [name])] 8 | let targets: [Target] = [.target(name: name)] 9 | let package = Package(name: name, 10 | platforms: platforms, 11 | products: products, 12 | targets: targets, 13 | swiftLanguageVersions: [.v5]) 14 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @main 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { true } 7 | 8 | // MARK: UISceneSession Lifecycle 9 | 10 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 11 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WARangeSlider.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "WARangeSlider" 3 | s.version = "1.2.0" 4 | s.summary = "A simple range slider made in Swift" 5 | s.description = "This pod provides a simple range slider." 6 | s.homepage = "https://github.com/warchimede/RangeSlider" 7 | s.license = "MIT" 8 | s.author = { "William Archimede" => "william.archimede@gmail.com" } 9 | s.source = { 10 | :git => "https://github.com/warchimede/RangeSlider.git", 11 | :tag => s.version.to_s 12 | } 13 | s.swift_version = '5.7' 14 | s.platform = :ios, '12.0' 15 | s.source_files = "Sources/WARangeSlider/RangeSlider.swift" 16 | end 17 | -------------------------------------------------------------------------------- /Example/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 4 | 5 | var window: UIWindow? 6 | 7 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 8 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 9 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 10 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 11 | guard let _ = (scene as? UIWindowScene) else { return } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 William Archimede 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController { 4 | let rangeSlider1 = RangeSlider(frame: CGRect.zero) 5 | let rangeSlider2 = RangeSlider(frame: CGRect.zero) 6 | 7 | override func viewDidLoad() { 8 | super.viewDidLoad() 9 | // Do any additional setup after loading the view, typically from a nib. 10 | rangeSlider2.trackHighlightTintColor = UIColor.red 11 | rangeSlider2.curvaceousness = 0.0 12 | 13 | view.addSubview(rangeSlider1) 14 | view.addSubview(rangeSlider2) 15 | 16 | rangeSlider1.addTarget(self, action: #selector(ViewController.rangeSliderValueChanged(_:)), for: .valueChanged) 17 | } 18 | 19 | override func viewDidLayoutSubviews() { 20 | let margin: CGFloat = 20.0 21 | let width = view.bounds.width - 2.0 * margin 22 | rangeSlider1.frame = CGRect(x: margin, y: margin + 100, width: width, height: 31.0) 23 | rangeSlider2.frame = CGRect(x: margin + 20, y: 5 * margin + 100, 24 | width: width - 40, height: 40) 25 | } 26 | 27 | @objc func rangeSliderValueChanged(_ rangeSlider: RangeSlider) { 28 | print("Range slider value changed: (\(rangeSlider.lowerValue) , \(rangeSlider.upperValue))") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RangeSlider 2 | 3 | [![License](https://img.shields.io/cocoapods/l/WARangeSlider.svg?style=flat)](http://cocoapods.org/pods/WARangeSlider) 4 | [![Platform](https://img.shields.io/cocoapods/p/WARangeSlider.svg?style=flat)](http://cocoapods.org/pods/WARangeSlider) 5 | [![Version](https://img.shields.io/cocoapods/v/WARangeSlider.svg?style=flat)](http://cocoapods.org/pods/WARangeSlider) 6 | 7 | ## Summary 8 | 9 | A simple range slider made in Swift. 10 | 11 | ## Screenshot 12 | 13 | ![](https://github.com/warchimede/RangeSlider/blob/master/Screenshot.png?raw=true) 14 | 15 | ## Use 16 | 17 | This control is **IBDesignable** and uses the **target-action** pattern for change notifications. 18 | 19 | In order to be notified when either thumb value changes, register for **.valueChanged**: 20 | 21 | ```swift 22 | let rangeSlider = RangeSlider(frame: frame) 23 | view.addSubView(rangeSlider) 24 | rangeSlider.addTarget(self, action: #selector(viewController.rangeSliderValueChanged(_:)), for: .valueChanged) 25 | ``` 26 | 27 | ## Configuration 28 | 29 | The range slider can be customized and information can be accessed through these properties : 30 | 31 | - `minimumValue` : The minimum possible value of the range 32 | - `maximumValue` : The maximum possible value of the range 33 | - `lowerValue` : The value corresponding to the left thumb current position 34 | - `upperValue` : The value corresponding to the right thumb current position 35 | - `trackTintColor` : The track color 36 | - `trackHighlightTintColor` : The color of the section of the track located between the two thumbs 37 | - `thumbTintColor`: The thumb color 38 | - `thumbBorderColor`: The thumb border color 39 | - `thumbBorderWidth`: The width of the thumb border 40 | - `curvaceousness` : From 0.0 for square thumbs to 1.0 for circle thumbs 41 | 42 | ## Installation 43 | 44 | RangeSlider is available through Swift Package Manager and [CocoaPods](http://cocoapods.org). 45 | 46 | ### Cocoapods 47 | 48 | To install it, simply add the following line to your Podfile : 49 | 50 | ```ruby 51 | pod "WARangeSlider" 52 | ``` 53 | 54 | ## Author 55 | 56 | [William Archimede](https://github.com/warchimede) 57 | 58 | ## License 59 | 60 | RangeSlider is available under the MIT License 61 | -------------------------------------------------------------------------------- /Example/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 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Sources/WARangeSlider/RangeSlider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import QuartzCore 3 | 4 | class RangeSliderTrackLayer: CALayer { 5 | weak var rangeSlider: RangeSlider? 6 | 7 | override func draw(in ctx: CGContext) { 8 | guard let slider = rangeSlider else { 9 | return 10 | } 11 | 12 | // Clip 13 | let cornerRadius = bounds.height * slider.curvaceousness / 2.0 14 | let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius) 15 | ctx.addPath(path.cgPath) 16 | 17 | // Fill the track 18 | ctx.setFillColor(slider.trackTintColor.cgColor) 19 | ctx.addPath(path.cgPath) 20 | ctx.fillPath() 21 | 22 | // Fill the highlighted range 23 | ctx.setFillColor(slider.trackHighlightTintColor.cgColor) 24 | let lowerValuePosition = CGFloat(slider.positionForValue(slider.lowerValue)) 25 | let upperValuePosition = CGFloat(slider.positionForValue(slider.upperValue)) 26 | let rect = CGRect(x: lowerValuePosition, y: 0.0, width: upperValuePosition - lowerValuePosition, height: bounds.height) 27 | ctx.fill(rect) 28 | } 29 | } 30 | 31 | class RangeSliderThumbLayer: CALayer { 32 | 33 | var highlighted: Bool = false { 34 | didSet { 35 | setNeedsDisplay() 36 | } 37 | } 38 | weak var rangeSlider: RangeSlider? 39 | 40 | var strokeColor: UIColor = UIColor.gray { 41 | didSet { 42 | setNeedsDisplay() 43 | } 44 | } 45 | var lineWidth: CGFloat = 0.5 { 46 | didSet { 47 | setNeedsDisplay() 48 | } 49 | } 50 | 51 | override func draw(in ctx: CGContext) { 52 | guard let slider = rangeSlider else { 53 | return 54 | } 55 | 56 | let thumbFrame = bounds.insetBy(dx: 2.0, dy: 2.0) 57 | let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0 58 | let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius) 59 | 60 | // Fill 61 | ctx.setFillColor(slider.thumbTintColor.cgColor) 62 | ctx.addPath(thumbPath.cgPath) 63 | ctx.fillPath() 64 | 65 | // Outline 66 | ctx.setStrokeColor(strokeColor.cgColor) 67 | ctx.setLineWidth(lineWidth) 68 | ctx.addPath(thumbPath.cgPath) 69 | ctx.strokePath() 70 | 71 | if highlighted { 72 | ctx.setFillColor(UIColor(white: 0.0, alpha: 0.1).cgColor) 73 | ctx.addPath(thumbPath.cgPath) 74 | ctx.fillPath() 75 | } 76 | } 77 | } 78 | 79 | @IBDesignable 80 | public class RangeSlider: UIControl { 81 | @IBInspectable public var minimumValue: Double = 0.0 { 82 | willSet(newValue) { 83 | assert(newValue < maximumValue, "RangeSlider: minimumValue should be lower than maximumValue") 84 | } 85 | didSet { 86 | updateLayerFrames() 87 | } 88 | } 89 | 90 | @IBInspectable public var maximumValue: Double = 1.0 { 91 | willSet(newValue) { 92 | assert(newValue > minimumValue, "RangeSlider: maximumValue should be greater than minimumValue") 93 | } 94 | didSet { 95 | updateLayerFrames() 96 | } 97 | } 98 | 99 | @IBInspectable public var lowerValue: Double = 0.2 { 100 | didSet { 101 | if lowerValue < minimumValue { 102 | lowerValue = minimumValue 103 | } 104 | updateLayerFrames() 105 | } 106 | } 107 | 108 | @IBInspectable public var upperValue: Double = 0.8 { 109 | didSet { 110 | if upperValue > maximumValue { 111 | upperValue = maximumValue 112 | } 113 | updateLayerFrames() 114 | } 115 | } 116 | 117 | var gapBetweenThumbs: Double { 118 | return 0.5 * Double(thumbWidth) * (maximumValue - minimumValue) / Double(bounds.width) 119 | } 120 | 121 | @IBInspectable public var trackTintColor: UIColor = UIColor(white: 0.9, alpha: 1.0) { 122 | didSet { 123 | trackLayer.setNeedsDisplay() 124 | } 125 | } 126 | 127 | @IBInspectable public var trackHighlightTintColor: UIColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0) { 128 | didSet { 129 | trackLayer.setNeedsDisplay() 130 | } 131 | } 132 | 133 | @IBInspectable public var thumbTintColor: UIColor = UIColor.white { 134 | didSet { 135 | lowerThumbLayer.setNeedsDisplay() 136 | upperThumbLayer.setNeedsDisplay() 137 | } 138 | } 139 | 140 | @IBInspectable public var thumbBorderColor: UIColor = UIColor.gray { 141 | didSet { 142 | lowerThumbLayer.strokeColor = thumbBorderColor 143 | upperThumbLayer.strokeColor = thumbBorderColor 144 | } 145 | } 146 | 147 | @IBInspectable public var thumbBorderWidth: CGFloat = 0.5 { 148 | didSet { 149 | lowerThumbLayer.lineWidth = thumbBorderWidth 150 | upperThumbLayer.lineWidth = thumbBorderWidth 151 | } 152 | } 153 | 154 | @IBInspectable public var curvaceousness: CGFloat = 1.0 { 155 | didSet { 156 | if curvaceousness < 0.0 { 157 | curvaceousness = 0.0 158 | } 159 | 160 | if curvaceousness > 1.0 { 161 | curvaceousness = 1.0 162 | } 163 | 164 | trackLayer.setNeedsDisplay() 165 | lowerThumbLayer.setNeedsDisplay() 166 | upperThumbLayer.setNeedsDisplay() 167 | } 168 | } 169 | 170 | fileprivate var previouslocation = CGPoint() 171 | 172 | fileprivate let trackLayer = RangeSliderTrackLayer() 173 | fileprivate let lowerThumbLayer = RangeSliderThumbLayer() 174 | fileprivate let upperThumbLayer = RangeSliderThumbLayer() 175 | 176 | fileprivate var thumbWidth: CGFloat { 177 | return CGFloat(bounds.height) 178 | } 179 | 180 | override public var frame: CGRect { 181 | didSet { 182 | updateLayerFrames() 183 | } 184 | } 185 | 186 | override public init(frame: CGRect) { 187 | super.init(frame: frame) 188 | initializeLayers() 189 | } 190 | 191 | required public init?(coder: NSCoder) { 192 | super.init(coder: coder) 193 | initializeLayers() 194 | } 195 | 196 | override public func layoutSublayers(of: CALayer) { 197 | super.layoutSublayers(of:layer) 198 | updateLayerFrames() 199 | } 200 | 201 | fileprivate func initializeLayers() { 202 | layer.backgroundColor = UIColor.clear.cgColor 203 | 204 | trackLayer.rangeSlider = self 205 | trackLayer.contentsScale = UIScreen.main.scale 206 | layer.addSublayer(trackLayer) 207 | 208 | lowerThumbLayer.rangeSlider = self 209 | lowerThumbLayer.contentsScale = UIScreen.main.scale 210 | layer.addSublayer(lowerThumbLayer) 211 | 212 | upperThumbLayer.rangeSlider = self 213 | upperThumbLayer.contentsScale = UIScreen.main.scale 214 | layer.addSublayer(upperThumbLayer) 215 | } 216 | 217 | func updateLayerFrames() { 218 | CATransaction.begin() 219 | CATransaction.setDisableActions(true) 220 | 221 | trackLayer.frame = bounds.insetBy(dx: 0.0, dy: bounds.height/3) 222 | trackLayer.setNeedsDisplay() 223 | 224 | let lowerThumbCenter = CGFloat(positionForValue(lowerValue)) 225 | lowerThumbLayer.frame = CGRect(x: lowerThumbCenter - thumbWidth/2.0, y: 0.0, width: thumbWidth, height: thumbWidth) 226 | lowerThumbLayer.setNeedsDisplay() 227 | 228 | let upperThumbCenter = CGFloat(positionForValue(upperValue)) 229 | upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth/2.0, y: 0.0, width: thumbWidth, height: thumbWidth) 230 | upperThumbLayer.setNeedsDisplay() 231 | 232 | CATransaction.commit() 233 | } 234 | 235 | func positionForValue(_ value: Double) -> Double { 236 | return Double(bounds.width - thumbWidth) * (value - minimumValue) / 237 | (maximumValue - minimumValue) + Double(thumbWidth/2.0) 238 | } 239 | 240 | func boundValue(_ value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double { 241 | return min(max(value, lowerValue), upperValue) 242 | } 243 | 244 | 245 | // MARK: - Touches 246 | 247 | override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 248 | previouslocation = touch.location(in: self) 249 | 250 | // Hit test the thumb layers 251 | if lowerThumbLayer.frame.contains(previouslocation) { 252 | lowerThumbLayer.highlighted = true 253 | } else if upperThumbLayer.frame.contains(previouslocation) { 254 | upperThumbLayer.highlighted = true 255 | } 256 | 257 | return lowerThumbLayer.highlighted || upperThumbLayer.highlighted 258 | } 259 | 260 | override public func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 261 | let location = touch.location(in: self) 262 | 263 | // Determine by how much the user has dragged 264 | let deltaLocation = Double(location.x - previouslocation.x) 265 | let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - bounds.height) 266 | 267 | previouslocation = location 268 | 269 | // Update the values 270 | if lowerThumbLayer.highlighted { 271 | lowerValue = boundValue(lowerValue + deltaValue, toLowerValue: minimumValue, upperValue: upperValue - gapBetweenThumbs) 272 | } else if upperThumbLayer.highlighted { 273 | upperValue = boundValue(upperValue + deltaValue, toLowerValue: lowerValue + gapBetweenThumbs, upperValue: maximumValue) 274 | } 275 | 276 | sendActions(for: .valueChanged) 277 | 278 | return true 279 | } 280 | 281 | override public func endTracking(_ touch: UITouch?, with event: UIEvent?) { 282 | lowerThumbLayer.highlighted = false 283 | upperThumbLayer.highlighted = false 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 93360D122AB0C3DC003C0669 /* RangeSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93360D112AB0C3DC003C0669 /* RangeSlider.swift */; }; 11 | 93C8129D2AAA3F3C005CEA65 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C8129C2AAA3F3C005CEA65 /* AppDelegate.swift */; }; 12 | 93C8129F2AAA3F3C005CEA65 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C8129E2AAA3F3C005CEA65 /* SceneDelegate.swift */; }; 13 | 93C812A12AAA3F3C005CEA65 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C812A02AAA3F3C005CEA65 /* ViewController.swift */; }; 14 | 93C812A42AAA3F3C005CEA65 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 93C812A22AAA3F3C005CEA65 /* Main.storyboard */; }; 15 | 93C812A62AAA3F3C005CEA65 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93C812A52AAA3F3C005CEA65 /* Assets.xcassets */; }; 16 | 93C812A92AAA3F3C005CEA65 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 93C812A72AAA3F3C005CEA65 /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 93360D112AB0C3DC003C0669 /* RangeSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RangeSlider.swift; path = Sources/WARangeSlider/RangeSlider.swift; sourceTree = SOURCE_ROOT; }; 21 | 93C812992AAA3F3C005CEA65 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 93C8129C2AAA3F3C005CEA65 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 93C8129E2AAA3F3C005CEA65 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 24 | 93C812A02AAA3F3C005CEA65 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 93C812A32AAA3F3C005CEA65 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 93C812A52AAA3F3C005CEA65 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 93C812A82AAA3F3C005CEA65 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 93C812AA2AAA3F3C005CEA65 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 93C812962AAA3F3C005CEA65 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 93C812902AAA3F3C005CEA65 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 93C8129B2AAA3F3C005CEA65 /* Example */, 46 | 93C8129A2AAA3F3C005CEA65 /* Products */, 47 | 93C812B42AAA41F4005CEA65 /* Frameworks */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 93C8129A2AAA3F3C005CEA65 /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 93C812992AAA3F3C005CEA65 /* Example.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 93C8129B2AAA3F3C005CEA65 /* Example */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 93360D112AB0C3DC003C0669 /* RangeSlider.swift */, 63 | 93C8129C2AAA3F3C005CEA65 /* AppDelegate.swift */, 64 | 93C8129E2AAA3F3C005CEA65 /* SceneDelegate.swift */, 65 | 93C812A02AAA3F3C005CEA65 /* ViewController.swift */, 66 | 93C812A22AAA3F3C005CEA65 /* Main.storyboard */, 67 | 93C812A52AAA3F3C005CEA65 /* Assets.xcassets */, 68 | 93C812A72AAA3F3C005CEA65 /* LaunchScreen.storyboard */, 69 | 93C812AA2AAA3F3C005CEA65 /* Info.plist */, 70 | ); 71 | path = Example; 72 | sourceTree = ""; 73 | }; 74 | 93C812B42AAA41F4005CEA65 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | ); 78 | name = Frameworks; 79 | sourceTree = ""; 80 | }; 81 | /* End PBXGroup section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | 93C812982AAA3F3C005CEA65 /* Example */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = 93C812AD2AAA3F3C005CEA65 /* Build configuration list for PBXNativeTarget "Example" */; 87 | buildPhases = ( 88 | 93C812952AAA3F3C005CEA65 /* Sources */, 89 | 93C812962AAA3F3C005CEA65 /* Frameworks */, 90 | 93C812972AAA3F3C005CEA65 /* Resources */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = Example; 97 | packageProductDependencies = ( 98 | ); 99 | productName = Example; 100 | productReference = 93C812992AAA3F3C005CEA65 /* Example.app */; 101 | productType = "com.apple.product-type.application"; 102 | }; 103 | /* End PBXNativeTarget section */ 104 | 105 | /* Begin PBXProject section */ 106 | 93C812912AAA3F3C005CEA65 /* Project object */ = { 107 | isa = PBXProject; 108 | attributes = { 109 | BuildIndependentTargetsInParallel = 1; 110 | LastSwiftUpdateCheck = 1430; 111 | LastUpgradeCheck = 1430; 112 | TargetAttributes = { 113 | 93C812982AAA3F3C005CEA65 = { 114 | CreatedOnToolsVersion = 14.3.1; 115 | }; 116 | }; 117 | }; 118 | buildConfigurationList = 93C812942AAA3F3C005CEA65 /* Build configuration list for PBXProject "Example" */; 119 | compatibilityVersion = "Xcode 14.0"; 120 | developmentRegion = en; 121 | hasScannedForEncodings = 0; 122 | knownRegions = ( 123 | en, 124 | Base, 125 | ); 126 | mainGroup = 93C812902AAA3F3C005CEA65; 127 | productRefGroup = 93C8129A2AAA3F3C005CEA65 /* Products */; 128 | projectDirPath = ""; 129 | projectRoot = ""; 130 | targets = ( 131 | 93C812982AAA3F3C005CEA65 /* Example */, 132 | ); 133 | }; 134 | /* End PBXProject section */ 135 | 136 | /* Begin PBXResourcesBuildPhase section */ 137 | 93C812972AAA3F3C005CEA65 /* Resources */ = { 138 | isa = PBXResourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 93C812A92AAA3F3C005CEA65 /* LaunchScreen.storyboard in Resources */, 142 | 93C812A62AAA3F3C005CEA65 /* Assets.xcassets in Resources */, 143 | 93C812A42AAA3F3C005CEA65 /* Main.storyboard in Resources */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXResourcesBuildPhase section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | 93C812952AAA3F3C005CEA65 /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 93360D122AB0C3DC003C0669 /* RangeSlider.swift in Sources */, 155 | 93C812A12AAA3F3C005CEA65 /* ViewController.swift in Sources */, 156 | 93C8129D2AAA3F3C005CEA65 /* AppDelegate.swift in Sources */, 157 | 93C8129F2AAA3F3C005CEA65 /* SceneDelegate.swift in Sources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXSourcesBuildPhase section */ 162 | 163 | /* Begin PBXVariantGroup section */ 164 | 93C812A22AAA3F3C005CEA65 /* Main.storyboard */ = { 165 | isa = PBXVariantGroup; 166 | children = ( 167 | 93C812A32AAA3F3C005CEA65 /* Base */, 168 | ); 169 | name = Main.storyboard; 170 | sourceTree = ""; 171 | }; 172 | 93C812A72AAA3F3C005CEA65 /* LaunchScreen.storyboard */ = { 173 | isa = PBXVariantGroup; 174 | children = ( 175 | 93C812A82AAA3F3C005CEA65 /* Base */, 176 | ); 177 | name = LaunchScreen.storyboard; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXVariantGroup section */ 181 | 182 | /* Begin XCBuildConfiguration section */ 183 | 93C812AB2AAA3F3C005CEA65 /* Debug */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_ANALYZER_NONNULL = YES; 188 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_ENABLE_OBJC_WEAK = YES; 193 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_COMMA = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 198 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 199 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 200 | CLANG_WARN_EMPTY_BODY = YES; 201 | CLANG_WARN_ENUM_CONVERSION = YES; 202 | CLANG_WARN_INFINITE_RECURSION = YES; 203 | CLANG_WARN_INT_CONVERSION = YES; 204 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 205 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 206 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 208 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 209 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 210 | CLANG_WARN_STRICT_PROTOTYPES = YES; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 213 | CLANG_WARN_UNREACHABLE_CODE = YES; 214 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 215 | COPY_PHASE_STRIP = NO; 216 | DEBUG_INFORMATION_FORMAT = dwarf; 217 | ENABLE_STRICT_OBJC_MSGSEND = YES; 218 | ENABLE_TESTABILITY = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu11; 220 | GCC_DYNAMIC_NO_PIC = NO; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_OPTIMIZATION_LEVEL = 0; 223 | GCC_PREPROCESSOR_DEFINITIONS = ( 224 | "DEBUG=1", 225 | "$(inherited)", 226 | ); 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 234 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 235 | MTL_FAST_MATH = YES; 236 | ONLY_ACTIVE_ARCH = YES; 237 | SDKROOT = iphoneos; 238 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 239 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 240 | }; 241 | name = Debug; 242 | }; 243 | 93C812AC2AAA3F3C005CEA65 /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_ENABLE_OBJC_WEAK = YES; 253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_COMMA = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 260 | CLANG_WARN_EMPTY_BODY = YES; 261 | CLANG_WARN_ENUM_CONVERSION = YES; 262 | CLANG_WARN_INFINITE_RECURSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | COPY_PHASE_STRIP = NO; 276 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 277 | ENABLE_NS_ASSERTIONS = NO; 278 | ENABLE_STRICT_OBJC_MSGSEND = YES; 279 | GCC_C_LANGUAGE_STANDARD = gnu11; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 282 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 283 | GCC_WARN_UNDECLARED_SELECTOR = YES; 284 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 285 | GCC_WARN_UNUSED_FUNCTION = YES; 286 | GCC_WARN_UNUSED_VARIABLE = YES; 287 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 288 | MTL_ENABLE_DEBUG_INFO = NO; 289 | MTL_FAST_MATH = YES; 290 | SDKROOT = iphoneos; 291 | SWIFT_COMPILATION_MODE = wholemodule; 292 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 293 | VALIDATE_PRODUCT = YES; 294 | }; 295 | name = Release; 296 | }; 297 | 93C812AE2AAA3F3C005CEA65 /* Debug */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 301 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 302 | CODE_SIGN_STYLE = Automatic; 303 | CURRENT_PROJECT_VERSION = 1; 304 | GENERATE_INFOPLIST_FILE = YES; 305 | INFOPLIST_FILE = Example/Info.plist; 306 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 307 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 308 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 309 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 310 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 311 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 312 | LD_RUNPATH_SEARCH_PATHS = ( 313 | "$(inherited)", 314 | "@executable_path/Frameworks", 315 | ); 316 | MARKETING_VERSION = 1.0; 317 | PRODUCT_BUNDLE_IDENTIFIER = wa.Example; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | SWIFT_EMIT_LOC_STRINGS = YES; 320 | SWIFT_VERSION = 5.0; 321 | TARGETED_DEVICE_FAMILY = "1,2"; 322 | }; 323 | name = Debug; 324 | }; 325 | 93C812AF2AAA3F3C005CEA65 /* Release */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 329 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 330 | CODE_SIGN_STYLE = Automatic; 331 | CURRENT_PROJECT_VERSION = 1; 332 | GENERATE_INFOPLIST_FILE = YES; 333 | INFOPLIST_FILE = Example/Info.plist; 334 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 335 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 336 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 337 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 338 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 339 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 340 | LD_RUNPATH_SEARCH_PATHS = ( 341 | "$(inherited)", 342 | "@executable_path/Frameworks", 343 | ); 344 | MARKETING_VERSION = 1.0; 345 | PRODUCT_BUNDLE_IDENTIFIER = wa.Example; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | SWIFT_EMIT_LOC_STRINGS = YES; 348 | SWIFT_VERSION = 5.0; 349 | TARGETED_DEVICE_FAMILY = "1,2"; 350 | }; 351 | name = Release; 352 | }; 353 | /* End XCBuildConfiguration section */ 354 | 355 | /* Begin XCConfigurationList section */ 356 | 93C812942AAA3F3C005CEA65 /* Build configuration list for PBXProject "Example" */ = { 357 | isa = XCConfigurationList; 358 | buildConfigurations = ( 359 | 93C812AB2AAA3F3C005CEA65 /* Debug */, 360 | 93C812AC2AAA3F3C005CEA65 /* Release */, 361 | ); 362 | defaultConfigurationIsVisible = 0; 363 | defaultConfigurationName = Release; 364 | }; 365 | 93C812AD2AAA3F3C005CEA65 /* Build configuration list for PBXNativeTarget "Example" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 93C812AE2AAA3F3C005CEA65 /* Debug */, 369 | 93C812AF2AAA3F3C005CEA65 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | /* End XCConfigurationList section */ 375 | }; 376 | rootObject = 93C812912AAA3F3C005CEA65 /* Project object */; 377 | } 378 | --------------------------------------------------------------------------------