├── .swift-version
├── demo.gif
├── DoubleSlider
├── .swiftlint.yml
├── DoubleSlider.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── DoubleSlider.xcscheme
│ └── project.pbxproj
├── DoubleSlider
│ ├── CALayers
│ │ ├── DoubleSliderLayerProtocol.swift
│ │ ├── DoubleSliderTrackLayer.swift
│ │ └── DoubleSliderThumbLayer.swift
│ ├── DoubleSlider
│ │ ├── DoubleSliderColors.swift
│ │ ├── DoubleSlider.h
│ │ ├── DoubleSliderDelegates.swift
│ │ ├── DoubleSliderTouchTracking.swift
│ │ ├── DoubleSliderUpdateFunctions.swift
│ │ └── DoubleSliderMain.swift
│ ├── Info.plist
│ └── Extensions
│ │ └── Extensions.swift
└── DoubleSliderTests
│ ├── Info.plist
│ └── DoubleSliderTests.swift
├── DoubleSliderDemo
├── .swiftlint.yml
├── DoubleSliderDemo.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── project.pbxproj
└── DoubleSliderDemo
│ ├── Extensions.swift
│ ├── Info.plist
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── ViewController.swift
│ └── Main.storyboard
├── Makefile
├── .github
└── workflows
│ ├── ci.yml
│ └── build.yml
├── DoubleSlider.xcworkspace
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── contents.xcworkspacedata
├── Package.swift
├── DoubleSlider.podspec
├── LICENSE
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.5.2
2 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhkaplan/DoubleSlider/HEAD/demo.gif
--------------------------------------------------------------------------------
/DoubleSlider/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - leading_whitespace
3 | - trailing_whitespace
4 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - leading_whitespace
3 | - trailing_whitespace
4 | - line_length
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios13.0-simulator"
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: macos-latest
8 | steps:
9 | - uses: action/checkout@v2
10 | - run: swift --version; make build
11 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DoubleSlider.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/CALayers/DoubleSliderLayerProtocol.swift:
--------------------------------------------------------------------------------
1 |
2 | // This is the protocol that all CALayer components
3 | // used by DoubleSlider conform to. It ensures they
4 | // contain a weak delegate to DoubleSlider
5 | protocol DoubleSliderLayer: AnyObject {
6 | var doubleSlider: DoubleSlider? { get set }
7 | }
8 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "DoubleSlider",
7 | platforms: [.iOS(.v11)],
8 | products: [.library(name: "DoubleSlider", targets: ["DoubleSlider"])],
9 | targets: [.target(name: "DoubleSlider", path: "DoubleSlider/DoubleSlider")]
10 | )
11 |
--------------------------------------------------------------------------------
/DoubleSlider.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: macos-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Version
17 | run: swift --version
18 | - name: Build
19 | run: make build
20 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | extension Array {
10 | func item(at index: Int) -> Element? {
11 | return (index < self.count && index >= 0) ? self[index] : nil
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSliderColors.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | // The Color enum is populated with the default color values that Apple uses in
4 | // standard UIKit controls
5 | enum Colors {
6 | static let defaultBlue = UIColor(red: 0.0, green: 0.51, blue: 0.98, alpha: 1.0)
7 | static let defaultGray = UIColor(red: 0.71, green: 0.71, blue: 0.71, alpha: 1.0)
8 | static let textGray = UIColor(red: 90.0 / 255.0, green: 90.0 / 255.0, blue: 95.0 / 255.0, alpha: 1.0)
9 | static let defaultWhite = UIColor.white
10 | }
11 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSlider.h:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSlider.h
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/04/02.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for DoubleSlider.
12 | FOUNDATION_EXPORT double DoubleSliderVersionNumber;
13 |
14 | //! Project version string for DoubleSlider.
15 | FOUNDATION_EXPORT const unsigned char DoubleSliderVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSliderTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.02
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/Extensions/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/04/02.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Double {
12 |
13 | // Rounds double to two decimal places
14 | var roundedToTwoPlaces: Double {
15 | let divisor = pow(10.0, 2.0)
16 | return (self * divisor).rounded() / divisor
17 | }
18 |
19 | // This is used to provide a formatted version of the label
20 | // string for the current upper/lower value
21 | var asRoundedAttributedString: NSAttributedString {
22 | return "\(self.roundedToTwoPlaces)".asAttributedString
23 | }
24 |
25 | }
26 |
27 | extension String {
28 |
29 | var asAttributedString: NSAttributedString {
30 | return NSAttributedString(string: self, attributes: DoubleSlider.labelAttributes)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/DoubleSlider.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "DoubleSlider"
3 | s.version = "1.0.0"
4 | s.summary = "DoubleSlider is a version of UISlider that has two draggable points —useful for choosing two points in a range."
5 | #s.description = "DoubleSlider is a version of UISlider that has two draggable points —useful for choosing two points in a range."
6 | s.homepage = "https://github.com/yhkaplan/doubleslider"
7 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
8 | s.license = { :type => "MIT", :file => "LICENSE" }
9 | s.author = { "yhkaplan" => "yhkaplan@gmail.com" }
10 | s.platform = :ios, "11.0"
11 | s.source = { :git => "https://github.com/yhkaplan/DoubleSlider.git", :tag => "#{s.version}" }
12 | s.source_files = "DoubleSlider/DoubleSlider/**/*.{h,swift}"
13 | s.exclude_files = "Classes/Exclude"
14 | # s.public_header_files = "Classes/**/*.h"
15 | s.framework = "UIKit", "CoreGraphics"
16 | end
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Joshua Kaplan
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 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSliderDelegates.swift:
--------------------------------------------------------------------------------
1 |
2 | // This protocol allows you to display a custom label
3 | // for each step index selected. Labels must be set to on
4 | // and the number of steps must be set.
5 | public protocol DoubleSliderLabelDelegate: AnyObject {
6 | func labelForStep(at index: Int) -> String?
7 | }
8 |
9 | // I choose not to combine the two delegates below in
10 | // order to make it clearer which protocol is being conformed to
11 | // and for what reason, as well as avoid an unneeded @objc annotation
12 |
13 | // This protocol allows you to use a Swiftier delegate pattern
14 | // rather than rely on notifications for updating values
15 | // Equivalent to `valueChanged` notification
16 | public protocol DoubleSliderValueChangedDelegate: AnyObject {
17 | func valueChanged(for doubleSlider: DoubleSlider)
18 | }
19 |
20 | // This protocol allows you to use a Swiftier delegate pattern
21 | // rather than rely on notifications for updating values
22 | // Equivalent to `editingDidEnd` notification
23 | public protocol DoubleSliderEditingDidEndDelegate: AnyObject {
24 | func editingDidEnd(for doubleSlider: DoubleSlider)
25 | }
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/Carthage/Carthage)
2 | [](swift.org)
3 | [](https://img.shields.io/cocoapods/v/DoubleSlider.svg)
4 | [](http://cocoapods.org/pods/DoubleSlider)
5 | # DoubleSlider
6 | DoubleSlider is a version of UISlider that has two draggable points —useful for choosing two points in a range.
7 |
8 | ## Requirements
9 | - iOS 12.0+
10 | - Xcode 12.0+
11 |
12 | ## Screenshots
13 | 
14 |
15 | ## Installation
16 |
17 | #### Swift Package Manager
18 |
19 | Add via Xcode in the [usual way](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app)
20 |
21 | #### CocoaPods
22 | You can use [CocoaPods](http://cocoapods.org/) to install `DoubleSlider` by adding it to your `Podfile`:
23 |
24 | ```ruby
25 | platform :ios, '11.0'
26 | use_frameworks!
27 | pod 'DoubleSlider'
28 | ```
29 | ``` swift
30 | import DoubleSlider
31 | ```
32 | #### Carthage
33 | Create a `Cartfile` that lists the framework and run `carthage update`. Follow the [instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios) to add `$(SRCROOT)/Carthage/Build/iOS/DoubleSlider.framework` to an iOS project.
34 |
35 | ```
36 | github "yhkaplan/DoubleSlider"
37 | ```
38 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
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 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/CALayers/DoubleSliderTrackLayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSliderTrackLayer.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 | import UIKit
11 |
12 | // DoubleSliderTrackLayer represents the colorized space between
13 | // the two thumb layers
14 | // I decided not to make this class open because it is very small,
15 | // meaning that developers could make their own custom CALayer
16 | // subclass in the same amount of time as it would take to subclass
17 | // this one
18 | public class DoubleSliderTrackLayer: CALayer, DoubleSliderLayer {
19 | weak var doubleSlider: DoubleSlider?
20 |
21 | override public func draw(in ctx: CGContext) {
22 | guard let slider = doubleSlider else { return }
23 |
24 | // Clip
25 | let cornerRadius = bounds.height * slider.roundedness / 2.0
26 | let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
27 | ctx.addPath(path.cgPath)
28 | ctx.fillPath()
29 |
30 | // Fill track
31 | ctx.setFillColor(slider.trackTintColor.cgColor)
32 | ctx.addPath(path.cgPath)
33 | ctx.fillPath()
34 |
35 | // Fill highlighted range
36 | ctx.setFillColor(slider.trackHighlightTintColor.cgColor)
37 | let lowerValuePosition = slider.positionForValue(value: slider.lowerValue)
38 | let upperValuePosition = slider.positionForValue(value: slider.upperValue)
39 | let rect = CGRect(x: lowerValuePosition,
40 | y: 0.0,
41 | width: upperValuePosition - lowerValuePosition,
42 | height: bounds.height)
43 | ctx.fill(rect)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/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 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/CALayers/DoubleSliderThumbLayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSliderThumbLayer.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 | import UIKit
11 | import Foundation
12 |
13 | // DoubleSliderThumbLayer represents the each of the draggable
14 | // thumb layers that indicate the minimum and maximum values
15 | // in the range
16 | // I decided not to make this class open because it is very small,
17 | // meaning that developers could make their own custom CALayer
18 | // subclass in the same amount of time as it would take to subclass
19 | // this one
20 | public class DoubleSliderThumbLayer: CALayer, DoubleSliderLayer {
21 | weak var doubleSlider: DoubleSlider?
22 |
23 | var isHighlighted: Bool = false {
24 | didSet {
25 | setNeedsDisplay()
26 | }
27 | }
28 |
29 | override public func draw(in ctx: CGContext) {
30 | guard let slider = doubleSlider else { return }
31 |
32 | let inset = slider.layerInset
33 | let thumbFrame = bounds.insetBy(dx: inset, dy: inset)
34 | let cornerRadius = thumbFrame.height * slider.roundedness / 2.0
35 | let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius)
36 |
37 | // Fill with shadow
38 | let shadowColor = Colors.defaultGray.cgColor
39 | ctx.setShadow(offset: CGSize(width: 0.0, height: 2.0), blur: 4.0, color: shadowColor)
40 | ctx.setFillColor(slider.thumbTintColor.cgColor)
41 | ctx.addPath(thumbPath.cgPath)
42 | ctx.fillPath()
43 |
44 | // Outline
45 | ctx.setStrokeColor(shadowColor)
46 | ctx.setLineWidth(0.25)
47 | ctx.addPath(thumbPath.cgPath)
48 | ctx.strokePath()
49 |
50 | // Set color for highlighted thumb picker (move somewhere else)??
51 | if isHighlighted {
52 | ctx.setFillColor(UIColor(white: 0.0, alpha: 0.1).cgColor)
53 | ctx.addPath(thumbPath.cgPath)
54 | ctx.fillPath()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DoubleSliderDemo
4 | //
5 | // Created by josh on 2018/04/02.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSliderTests/DoubleSliderTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSliderTests.swift
3 | // DoubleSliderTests
4 | //
5 | // Created by josh on 2018/04/16.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class DoubleSliderTests: XCTestCase {
12 |
13 | var labels: [String] = []
14 | var doubleSlider: DoubleSlider!
15 |
16 | override func setUp() {
17 | super.setUp()
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 | }
20 |
21 | override func tearDown() {
22 | doubleSlider.removeTarget(self, action: #selector(printVal(_:)), for: .valueChanged)
23 | super.tearDown()
24 | }
25 |
26 | func testExample() {
27 | // This is an example of a functional test case.
28 | // Use XCTAssert and related functions to verify your tests produce the correct results.
29 | }
30 |
31 | @objc func printVal(_ control: DoubleSlider) {
32 | print("Lower: \(control.lowerValue) Upper: \(control.upperValue)")
33 | }
34 |
35 | @objc func finishChanging(_ control: DoubleSlider) {
36 | print("Final Lower: \(control.stepIndexForLowerValue!) Final Upper: \(control.stepIndexForUpperValue!)")
37 | }
38 |
39 | private func makeLabels() {
40 | for num in stride(from: 0, to: 300, by: 10) {
41 | labels.append("$\(i)")
42 | }
43 | labels.append("No limit")
44 | }
45 |
46 | private func setupDoubleSlider() {
47 | let height: CGFloat = 38.0 //TODO: make this the default height
48 | let width = view.bounds.width - 40.0
49 | let frame = CGRect(x: backgroundView.bounds.minX,
50 | y: backgroundView.bounds.midY - (height / 2.0),
51 | width: width,
52 | height: height)
53 |
54 | doubleSlider = DoubleSlider(frame: frame)
55 | doubleSlider.translatesAutoresizingMaskIntoConstraints = false
56 |
57 | doubleSlider.labelDelegate = self
58 | doubleSlider.numberOfSteps = labels.count
59 | doubleSlider.smoothStepping = true
60 | doubleSlider.lowerLabelMargin = -20
61 | doubleSlider.upperLabelMargin = view.bounds.maxX
62 |
63 | doubleSlider.addTarget(self, action: #selector(printVal(_:)), for: .valueChanged)
64 | doubleSlider.addTarget(self, action: #selector(finishChanging(_:)), for: .editingDidEnd)
65 |
66 | backgroundView.addSubview(doubleSlider)
67 | }
68 | }
69 |
70 | class ViewController: DoubleSliderLabelDelegate {
71 | func labelForStep(at index: Int) -> String? {
72 | return labels.item(at: index)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider.xcodeproj/xcshareddata/xcschemes/DoubleSlider.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // DoubleSliderDemo
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DoubleSlider
11 |
12 | class ViewController: UIViewController {
13 |
14 | @IBOutlet weak var backgroundView: UIView!
15 | @IBOutlet weak var normalSlider: UISlider!
16 | @IBOutlet weak var redDoubleSlider: DoubleSlider!
17 |
18 | var labels: [String] = []
19 | var doubleSlider: DoubleSlider!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | makeLabels()
25 | setupDoubleSlider()
26 |
27 | redDoubleSlider.labelDelegate = self
28 | redDoubleSlider.numberOfSteps = labels.count
29 | redDoubleSlider.labelsAreHidden = false
30 | redDoubleSlider.smoothStepping = true
31 | }
32 |
33 | private func makeLabels() {
34 | for num in stride(from: 0, to: 200, by: 10) {
35 | labels.append("$\(num)")
36 | }
37 | labels.append("No limit")
38 | }
39 |
40 | private func setupDoubleSlider() {
41 | let height: CGFloat = 38.0 //TODO: make this the default height
42 | let width = view.bounds.width - 38.0
43 |
44 | let frame = CGRect(
45 | x: backgroundView.bounds.minX - 2.0,
46 | y: backgroundView.bounds.midY - (height / 2.0),
47 | width: width,
48 | height: height
49 | )
50 |
51 | doubleSlider = DoubleSlider(frame: frame)
52 | doubleSlider.translatesAutoresizingMaskIntoConstraints = false
53 |
54 | doubleSlider.labelDelegate = self
55 | doubleSlider.numberOfSteps = labels.count
56 | doubleSlider.smoothStepping = true
57 | let labelOffset: CGFloat = 8.0
58 | doubleSlider.lowerLabelMarginOffset = labelOffset
59 | doubleSlider.upperLabelMarginOffset = labelOffset
60 |
61 | doubleSlider.lowerValueStepIndex = 0
62 | doubleSlider.upperValueStepIndex = labels.count - 1
63 |
64 | // You can use traditional notifications
65 | doubleSlider.addTarget(self, action: #selector(printVal(_:)), for: .valueChanged)
66 | // Or Swifty delegates
67 | doubleSlider.editingDidEndDelegate = self
68 |
69 | backgroundView.addSubview(doubleSlider)
70 | }
71 |
72 | override func viewWillDisappear(_ animated: Bool) {
73 | super.viewWillDisappear(animated)
74 |
75 | doubleSlider.removeTarget(self, action: #selector(printVal(_:)), for: .valueChanged)
76 | }
77 |
78 | @objc func printVal(_ doubleSlider: DoubleSlider) {
79 | print("Lower: \(doubleSlider.lowerValue) Upper: \(doubleSlider.upperValue)")
80 | }
81 | }
82 |
83 | extension ViewController: DoubleSliderEditingDidEndDelegate {
84 | func editingDidEnd(for doubleSlider: DoubleSlider) {
85 | print("Lower Step Index: \(doubleSlider.lowerValueStepIndex) Upper Step Index: \(doubleSlider.upperValueStepIndex)")
86 | }
87 | }
88 |
89 | extension ViewController: DoubleSliderLabelDelegate {
90 | func labelForStep(at index: Int) -> String? {
91 | return labels.item(at: index)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSliderTouchTracking.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSliderTouchTracking.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension DoubleSlider {
12 |
13 | // MARK: - Private var
14 |
15 | private var minimumSpaceBetweenThumbOrigins: Double {
16 | return stepDistance ?? 0.05
17 | }
18 |
19 | // MARK: - Touch tracking
20 |
21 | override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
22 | previousLocation = touch.location(in: self)
23 |
24 | if lowerThumbLayer.frame.contains(previousLocation) {
25 | lowerThumbLayer.isHighlighted = true
26 |
27 | } else if upperThumbLayer.frame.contains(previousLocation) {
28 | upperThumbLayer.isHighlighted = true
29 | }
30 |
31 | return lowerThumbLayer.isHighlighted || upperThumbLayer.isHighlighted
32 | }
33 |
34 | override open func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
35 | let location = touch.location(in: self)
36 |
37 | // Check drag distance
38 | let dragDistance = Double(location.x - previousLocation.x)
39 | let dragValue = (maxValue - minValue) * dragDistance / Double(bounds.width - thumbWidth)
40 |
41 | previousLocation = location
42 |
43 | // Update values
44 | if lowerThumbLayer.isHighlighted {
45 | lowerValue = lowerValueAdjusted(for: dragValue)
46 |
47 | } else if upperThumbLayer.isHighlighted {
48 | upperValue = upperValueAdjusted(for: dragValue)
49 | }
50 |
51 | // Declare that a value was updated, called continuously
52 | sendActions(for: .valueChanged)
53 | valueChangedDelegate?.valueChanged(for: self)
54 |
55 | return true
56 | }
57 |
58 | override open func endTracking(_ touch: UITouch?, with event: UIEvent?) {
59 | lowerThumbLayer.isHighlighted = false
60 | upperThumbLayer.isHighlighted = false
61 |
62 | // Declare that the change finished, called at the end
63 | // of a drag session
64 | sendActions(for: .editingDidEnd)
65 | editingDidEndDelegate?.editingDidEnd(for: self)
66 | }
67 |
68 | // MARK: - Private funcs
69 |
70 | private func lowerValueAdjusted(for dragValue: Double) -> Double {
71 | var lowerTempValue = lowerValue + dragValue
72 | lowerTempValue = boundValue(value: lowerTempValue, lowerValue: minValue, upperValue: upperValue)
73 |
74 | // For step-like behavior
75 | if !smoothStepping, let steppedValue = steppedValue(for: lowerTempValue) {
76 | lowerTempValue = steppedValue
77 | }
78 |
79 | let highestPositionAllowed = upperValue - minimumSpaceBetweenThumbOrigins
80 |
81 | // To prevent excessive overlap
82 | if lowerTempValue > highestPositionAllowed {
83 | lowerTempValue = highestPositionAllowed
84 | }
85 |
86 | // To prevent a rare case in which 2 thumbs are barely near
87 | // enough to register as the same stepIndex/label
88 | if let lowerIndex = stepIndex(for: lowerTempValue),
89 | let upperIndex = stepIndex(for: upperValue),
90 | lowerIndex >= upperIndex {
91 |
92 | lowerTempValue = lowerValue
93 | }
94 |
95 | return lowerTempValue
96 | }
97 |
98 | private func upperValueAdjusted(for dragValue: Double) -> Double {
99 | var upperTempValue = upperValue + dragValue
100 | upperTempValue = boundValue(value: upperTempValue, lowerValue: lowerValue, upperValue: maxValue)
101 |
102 | // For step-like behavior
103 | if !smoothStepping, let steppedValue = steppedValue(for: upperTempValue) {
104 | upperTempValue = steppedValue
105 | }
106 |
107 | let lowestPositionAllowed = lowerValue + minimumSpaceBetweenThumbOrigins
108 |
109 | // To prevent excessive overlap
110 | if upperTempValue < lowestPositionAllowed {
111 | upperTempValue = lowestPositionAllowed
112 | }
113 |
114 | // To prevent a rare case in which 2 thumbs are barely near
115 | // enough to register as the same stepIndex/label
116 | if let upperIndex = stepIndex(for: upperTempValue),
117 | let lowerIndex = stepIndex(for: lowerValue),
118 | upperIndex <= lowerIndex {
119 |
120 | upperTempValue = upperValue
121 | }
122 |
123 | return upperTempValue
124 | }
125 |
126 | private func steppedValue(for value: Double) -> Double? {
127 | guard let stepDistance = stepDistance else { return nil }
128 |
129 | return round(value / stepDistance) * stepDistance
130 | }
131 |
132 | private func boundValue(value: Double, lowerValue: Double, upperValue: Double) -> Double {
133 | return min(max(value, lowerValue), upperValue)
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSliderUpdateFunctions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSliderUpdateFunctions.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // MARK: General update functions
12 |
13 | extension DoubleSlider {
14 |
15 | open func updateLayerFrames() {
16 | CATransaction.begin()
17 | // Prevents interaction while updating
18 | CATransaction.setDisableActions(true)
19 |
20 | // Get lower values
21 | let lowerThumbCenter = positionForValue(value: lowerValue)
22 | let lowerThumbMinX = lowerThumbCenter - thumbWidth / 2.0
23 |
24 | // Get upper values
25 | let upperThumbCenter = positionForValue(value: upperValue)
26 | let upperThumbMinX = upperThumbCenter - thumbWidth / 2.0
27 |
28 | lowerThumbLayer.frame = CGRect(x: lowerThumbMinX, y: 0.0, width: thumbWidth, height: thumbWidth)
29 | lowerThumbLayer.setNeedsDisplay()
30 |
31 | upperThumbLayer.frame = CGRect(x: upperThumbMinX, y: 0.0, width: thumbWidth, height: thumbWidth)
32 | upperThumbLayer.setNeedsDisplay()
33 |
34 | trackLayer.frame = bounds.insetBy(dx: layerInset, dy: 18.0)
35 | trackLayer.setNeedsDisplay()
36 |
37 | updateLabelValues()
38 | updateLabelSizes()
39 | updateLabelPositions()
40 |
41 | minLabel.alignmentMode = .center
42 | maxLabel.alignmentMode = .center
43 |
44 | CATransaction.commit()
45 | }
46 |
47 | func positionForValue(value: Double) -> CGFloat {
48 | let widthDifference = bounds.width - thumbWidth
49 | return widthDifference * CGFloat(value - minValue) / CGFloat(maxValue - minValue) + (thumbWidth / 2.0)
50 | }
51 | }
52 |
53 | // MARK: Label related funcs
54 |
55 | extension DoubleSlider {
56 |
57 | // If a custom label is set for the lower or upper value,
58 | // then that label is used instead of the raw value
59 | private func updateLabelValues() {
60 | if let stepIndex = stepIndex(for: lowerValue),
61 | let label = labelDelegate?.labelForStep(at: stepIndex) {
62 | minLabel.string = label.asAttributedString
63 |
64 | } else {
65 | minLabel.string = lowerValue.asRoundedAttributedString
66 | }
67 |
68 | if let stepIndex = stepIndex(for: upperValue),
69 | let label = labelDelegate?.labelForStep(at: stepIndex) {
70 | maxLabel.string = label.asAttributedString
71 |
72 | } else {
73 | maxLabel.string = upperValue.asRoundedAttributedString
74 | }
75 | setNeedsLayout()
76 | }
77 |
78 | private func labelCenter(for label: DoubleSliderThumbLayer) -> CGPoint {
79 | let labelY = trackLayer.frame.midY - (minLabel.frame.height / 2.0) - spaceBetweenThumbAndLabel
80 | return CGPoint(x: label.frame.midX, y: labelY)
81 | }
82 |
83 | private func updateLabelSizes() {
84 | [minLabel, maxLabel].forEach { label in
85 | let width = (label.string as? NSAttributedString)?.size().width ?? 55.0
86 | label.frame.size = CGSize(width: width, height: 20.0)
87 | }
88 | }
89 |
90 | private func updateLabelPositions() {
91 | let newMinLabelCenter = labelCenter(for: lowerThumbLayer)
92 | let newMaxLabelCenter = labelCenter(for: upperThumbLayer)
93 |
94 | let newRightmostXInMinLabel = newMinLabelCenter.x + minLabel.frame.width / 2.0
95 | let newLeftmostXInMaxLabel = newMaxLabelCenter.x - maxLabel.frame.width / 2.0
96 | let newSpaceBetweenLabels = newLeftmostXInMaxLabel - newRightmostXInMinLabel
97 |
98 | if newSpaceBetweenLabels > minimumSpaceBetweenLabels { // No position conflict between labels
99 | minLabel.position = newMinLabelCenter
100 | maxLabel.position = newMaxLabelCenter
101 |
102 | // Position conflict between lowerLabel and lowerLabelMargin
103 | if minLabel.frame.minX < lowerLabelMargin {
104 | minLabel.frame.origin.x = minLabel.frame.minX + 4.0
105 | }
106 |
107 | // Position conflict between upperLabel and upperLabelMargin
108 | if maxLabel.frame.maxX > upperLabelMargin {
109 | maxLabel.frame.origin.x = frame.width - maxLabel.frame.width - 4.0
110 | }
111 |
112 | } else { // Positions conflict between labels
113 | let increaseAmount: CGFloat = minimumSpaceBetweenLabels - newSpaceBetweenLabels
114 | minLabel.position = CGPoint(x: newMinLabelCenter.x - increaseAmount / 2.0, y: newMinLabelCenter.y)
115 | maxLabel.position = CGPoint(x: newMaxLabelCenter.x + increaseAmount / 2.0, y: newMaxLabelCenter.y)
116 |
117 | // Adjust labels positions when they collide at the left lowerLabelMargin
118 | if minLabel.frame.minX < lowerLabelMargin {
119 | minLabel.frame.origin.x = lowerLabelMargin
120 | maxLabel.frame.origin.x = minimumSpaceBetweenLabels + minLabel.frame.width
121 | }
122 |
123 | // Adjust labels positions when they collide at the right upperLabelMargin
124 | if maxLabel.frame.maxX > upperLabelMargin {
125 | maxLabel.frame.origin.x = upperLabelMargin - maxLabel.frame.width
126 | minLabel.frame.origin.x = maxLabel.frame.origin.x - minimumSpaceBetweenLabels - minLabel.frame.width
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider/DoubleSlider/DoubleSliderMain.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleSlider.swift
3 | // DoubleSlider
4 | //
5 | // Created by josh on 2018/03/30.
6 | // Copyright © 2018年 yhkaplan. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // The DoubleSlider class is the main class that implements the DoubleSlider
12 | // control. Its main methods are marked open, so they can be subclassed and
13 | // overridden to add or change behaviors
14 | @IBDesignable open class DoubleSlider: UIControl {
15 |
16 | // Public vars
17 |
18 | // Track values
19 | public private(set) var minValue: Double = 0.0
20 |
21 | public private(set) var maxValue: Double = 1.0
22 |
23 | @IBInspectable public var lowerValue: Double = 0.2 {
24 | didSet {
25 | updateLayerFrames()
26 | }
27 | }
28 |
29 | @IBInspectable public var upperValue: Double = 0.8 {
30 | didSet {
31 | updateLayerFrames()
32 | }
33 | }
34 |
35 | @IBInspectable public var numberOfSteps: Int = 0 {
36 | didSet {
37 | updateLayerFrames()
38 | }
39 | }
40 |
41 | // lowerValue as a step index (int)
42 | public var lowerValueStepIndex: Int {
43 | // Return 0 if steps don't exist
44 | get {
45 | return stepIndex(for: lowerValue) ?? 0
46 | }
47 | // Don't change lowerValue if steps aren't set
48 | set(newIndex) {
49 | lowerValue = value(for: newIndex) ?? lowerValue
50 | updateLayerFrames()
51 | }
52 | }
53 |
54 | // upperValue as a step index (int)
55 | public var upperValueStepIndex: Int {
56 | // Return 0 if steps don't exist
57 | get {
58 | return stepIndex(for: upperValue) ?? 0
59 | }
60 | // Don't change upperValue if steps aren't set
61 | set(newIndex) {
62 | upperValue = value(for: newIndex) ?? upperValue
63 | updateLayerFrames()
64 | }
65 | }
66 |
67 | // This bool turns off traditional stepping behavior,
68 | // allowing for custom labels set at given intervals
69 | // that don't "jump" from step to step, but instead
70 | // transition smoothly
71 | @IBInspectable public var smoothStepping: Bool = false {
72 | didSet {
73 | updateLayerFrames()
74 | }
75 | }
76 |
77 | // This inset is shared between the thumb layers and
78 | // the track layer so the track layer doesn't extend
79 | // beyond the thumbs. Also, this value makes sure
80 | // that the shadows found on the thumb layer aren't
81 | // cut off.
82 | @IBInspectable public var layerInset: CGFloat = 5.0 {
83 | didSet {
84 | updateLayerFrames()
85 | }
86 | }
87 |
88 | // Override this to show only one label, etc.
89 | @IBInspectable open var labelsAreHidden: Bool = false {
90 | didSet {
91 | minLabel.isHidden = labelsAreHidden
92 | maxLabel.isHidden = labelsAreHidden
93 | }
94 | }
95 |
96 | // MARK: - Colors
97 |
98 | @IBInspectable public var trackTintColor: UIColor = Colors.defaultGray {
99 | didSet {
100 | trackLayer.setNeedsDisplay()
101 | }
102 | }
103 |
104 | @IBInspectable public var trackHighlightTintColor: UIColor = Colors.defaultBlue {
105 | didSet {
106 | trackLayer.setNeedsDisplay()
107 | }
108 | }
109 |
110 | @IBInspectable public var thumbTintColor: UIColor = Colors.defaultWhite {
111 | didSet {
112 | lowerThumbLayer.setNeedsDisplay()
113 | upperThumbLayer.setNeedsDisplay()
114 | }
115 | }
116 |
117 | // MARK: - General appearance
118 |
119 | @IBInspectable public var roundedness: CGFloat = 1.0 {
120 | didSet {
121 | trackLayer.setNeedsDisplay()
122 | lowerThumbLayer.setNeedsDisplay()
123 | upperThumbLayer.setNeedsDisplay()
124 | }
125 | }
126 |
127 | // This is a class var so it can be overridden at will
128 | public class var labelAttributes: [NSAttributedString.Key: Any] {
129 | return [
130 | .font: UIFont.systemFont(ofSize: 14.0),
131 | .foregroundColor: Colors.textGray
132 | ]
133 | }
134 |
135 | @IBInspectable public var minimumSpaceBetweenLabels: CGFloat = 5.0 {
136 | didSet {
137 | updateLayerFrames()
138 | }
139 | }
140 |
141 | @IBInspectable public var spaceBetweenThumbAndLabel: CGFloat = 18.0 {
142 | didSet {
143 | updateLayerFrames()
144 | }
145 | }
146 |
147 | // The minimum distance from minX of the bounds that the lowerLabel can move to
148 | // Make this a positive value to allow the label to go beyond the bounds
149 | // Make this negative value to make the label stay away from the edge of the bounds
150 | @IBInspectable public var lowerLabelMarginOffset: CGFloat = 0.0
151 |
152 | // The maximum distance from maxX of the bounds that the upperLabel can move to
153 | // Make this a positive value to allow the label to go beyond the bounds
154 | // Make this negative value to make the label stay away from the edge of the bounds
155 | @IBInspectable public var upperLabelMarginOffset: CGFloat = 0.0
156 |
157 | // The furthest left that a label can move
158 | var lowerLabelMargin: CGFloat {
159 | return bounds.minX - lowerLabelMarginOffset
160 | }
161 |
162 | // The furthest right that a label can move
163 | var upperLabelMargin: CGFloat {
164 | return bounds.maxX + upperLabelMarginOffset
165 | }
166 |
167 | public weak var labelDelegate: DoubleSliderLabelDelegate? {
168 | didSet {
169 | updateLayerFrames()
170 | }
171 | }
172 |
173 | public weak var valueChangedDelegate: DoubleSliderValueChangedDelegate?
174 |
175 | public weak var editingDidEndDelegate: DoubleSliderEditingDidEndDelegate?
176 |
177 | // Render components
178 | public let trackLayer = DoubleSliderTrackLayer()
179 | public let lowerThumbLayer = DoubleSliderThumbLayer()
180 | public let upperThumbLayer = DoubleSliderThumbLayer()
181 | public let minLabel = CATextLayer()
182 | public let maxLabel = CATextLayer()
183 |
184 | var previousLocation = CGPoint()
185 |
186 | open var thumbWidth: CGFloat {
187 | return CGFloat(bounds.height)
188 | }
189 |
190 | override open var frame: CGRect {
191 | didSet {
192 | updateLayerFrames()
193 | }
194 | }
195 |
196 | override open var bounds: CGRect {
197 | // Without this, the sizing in AutoLayout would be off
198 | didSet {
199 | updateLayerFrames()
200 | }
201 | }
202 |
203 | // MARK: - Internal funcs/vars
204 |
205 | var stepDistance: Double? {
206 | guard numberOfSteps > 0 else { return nil }
207 |
208 | return maxValue / Double(numberOfSteps)
209 | }
210 |
211 | func value(for stepIndex: Int) -> Double? {
212 | guard let stepDistance = stepDistance else {
213 | return nil
214 | }
215 |
216 | // This prevents the thumb from looking slightly off at minValue
217 | if stepIndex == 0 { return minValue }
218 | // This prevents the thumb from looking slightly off at maxValue
219 | if stepIndex == numberOfSteps - 1 { return maxValue }
220 |
221 | return Double(stepIndex + 1) * stepDistance - (stepDistance / 2.0)
222 | }
223 |
224 | func stepIndex(for value: Double) -> Int? {
225 | guard numberOfSteps > 0 else { return nil }
226 |
227 | return Int(round((value - minValue) * Double(numberOfSteps - 1)))
228 | }
229 |
230 | // MARK: - Initializers
231 |
232 | public required init?(coder aDecoder: NSCoder) {
233 | super.init(coder: aDecoder)
234 | initialSetup()
235 | }
236 |
237 | public override init(frame: CGRect) {
238 | super.init(frame: frame)
239 | initialSetup()
240 | }
241 |
242 | // MARK: - Private func
243 |
244 | private func initialSetup() {
245 | // The reason that I put the upperThumbLayer before the lowerThumbLayer is to make the
246 | // lowerThumbLayer be above the upperThumbLayer in the z axis, which reflects the priority
247 | // logic when selecting one thumbLayer when they both overlap. Without this, the lowerLayer is
248 | // prioritized even though it is below the upperThumbLayer in the z axis
249 | [trackLayer, upperThumbLayer, lowerThumbLayer, minLabel, maxLabel].forEach { caLayer in
250 | // Set the doubleSlider delegate for CALayers that
251 | // conform to DoubleSliderLayer protocol
252 | if let caLayer = caLayer as? DoubleSliderLayer {
253 | caLayer.doubleSlider = self
254 | }
255 |
256 | caLayer.contentsScale = UIScreen.main.scale
257 | layer.addSublayer(caLayer)
258 | }
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo/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 |
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 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/DoubleSliderDemo/DoubleSliderDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 036F406020760DF9002AC7C0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036F405F20760DF8002AC7C0 /* ViewController.swift */; };
11 | 03763D762072443D000AFB80 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D752072443D000AFB80 /* AppDelegate.swift */; };
12 | 03763D7D2072443D000AFB80 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03763D7C2072443D000AFB80 /* Assets.xcassets */; };
13 | 03763D802072443D000AFB80 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03763D7E2072443D000AFB80 /* LaunchScreen.storyboard */; };
14 | 03763D8820724463000AFB80 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D8720724463000AFB80 /* Extensions.swift */; };
15 | 03763D8C2072448A000AFB80 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03763D8B2072448A000AFB80 /* Main.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 03125729207244E700AA8ACB /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 036F405F20760DF8002AC7C0 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
33 | 03763D722072443D000AFB80 /* DoubleSliderDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DoubleSliderDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
34 | 03763D752072443D000AFB80 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
35 | 03763D7C2072443D000AFB80 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
36 | 03763D7F2072443D000AFB80 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
37 | 03763D812072443D000AFB80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | 03763D8720724463000AFB80 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
39 | 03763D8B2072448A000AFB80 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | 03763D6F2072443D000AFB80 /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | );
48 | runOnlyForDeploymentPostprocessing = 0;
49 | };
50 | /* End PBXFrameworksBuildPhase section */
51 |
52 | /* Begin PBXGroup section */
53 | 03763D692072443D000AFB80 = {
54 | isa = PBXGroup;
55 | children = (
56 | 03763D742072443D000AFB80 /* DoubleSliderDemo */,
57 | 03763D732072443D000AFB80 /* Products */,
58 | );
59 | sourceTree = "";
60 | };
61 | 03763D732072443D000AFB80 /* Products */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 03763D722072443D000AFB80 /* DoubleSliderDemo.app */,
65 | );
66 | name = Products;
67 | sourceTree = "";
68 | };
69 | 03763D742072443D000AFB80 /* DoubleSliderDemo */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 03763D8720724463000AFB80 /* Extensions.swift */,
73 | 03763D752072443D000AFB80 /* AppDelegate.swift */,
74 | 036F405F20760DF8002AC7C0 /* ViewController.swift */,
75 | 03763D8B2072448A000AFB80 /* Main.storyboard */,
76 | 03763D7C2072443D000AFB80 /* Assets.xcassets */,
77 | 03763D7E2072443D000AFB80 /* LaunchScreen.storyboard */,
78 | 03763D812072443D000AFB80 /* Info.plist */,
79 | );
80 | path = DoubleSliderDemo;
81 | sourceTree = "";
82 | };
83 | /* End PBXGroup section */
84 |
85 | /* Begin PBXNativeTarget section */
86 | 03763D712072443D000AFB80 /* DoubleSliderDemo */ = {
87 | isa = PBXNativeTarget;
88 | buildConfigurationList = 03763D842072443D000AFB80 /* Build configuration list for PBXNativeTarget "DoubleSliderDemo" */;
89 | buildPhases = (
90 | 03763D6E2072443D000AFB80 /* Sources */,
91 | 03763D6F2072443D000AFB80 /* Frameworks */,
92 | 03763D702072443D000AFB80 /* Resources */,
93 | 03125729207244E700AA8ACB /* Embed Frameworks */,
94 | 0392EF0A2090412E00DEE1E3 /* ShellScript */,
95 | );
96 | buildRules = (
97 | );
98 | dependencies = (
99 | );
100 | name = DoubleSliderDemo;
101 | productName = DoubleSliderDemo;
102 | productReference = 03763D722072443D000AFB80 /* DoubleSliderDemo.app */;
103 | productType = "com.apple.product-type.application";
104 | };
105 | /* End PBXNativeTarget section */
106 |
107 | /* Begin PBXProject section */
108 | 03763D6A2072443D000AFB80 /* Project object */ = {
109 | isa = PBXProject;
110 | attributes = {
111 | LastSwiftUpdateCheck = 0920;
112 | LastUpgradeCheck = 1250;
113 | ORGANIZATIONNAME = yhkaplan;
114 | TargetAttributes = {
115 | 03763D712072443D000AFB80 = {
116 | CreatedOnToolsVersion = 9.2;
117 | ProvisioningStyle = Automatic;
118 | };
119 | };
120 | };
121 | buildConfigurationList = 03763D6D2072443D000AFB80 /* Build configuration list for PBXProject "DoubleSliderDemo" */;
122 | compatibilityVersion = "Xcode 8.0";
123 | developmentRegion = en;
124 | hasScannedForEncodings = 0;
125 | knownRegions = (
126 | en,
127 | Base,
128 | );
129 | mainGroup = 03763D692072443D000AFB80;
130 | productRefGroup = 03763D732072443D000AFB80 /* Products */;
131 | projectDirPath = "";
132 | projectRoot = "";
133 | targets = (
134 | 03763D712072443D000AFB80 /* DoubleSliderDemo */,
135 | );
136 | };
137 | /* End PBXProject section */
138 |
139 | /* Begin PBXResourcesBuildPhase section */
140 | 03763D702072443D000AFB80 /* Resources */ = {
141 | isa = PBXResourcesBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | 03763D8C2072448A000AFB80 /* Main.storyboard in Resources */,
145 | 03763D802072443D000AFB80 /* LaunchScreen.storyboard in Resources */,
146 | 03763D7D2072443D000AFB80 /* Assets.xcassets in Resources */,
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | /* End PBXResourcesBuildPhase section */
151 |
152 | /* Begin PBXShellScriptBuildPhase section */
153 | 0392EF0A2090412E00DEE1E3 /* ShellScript */ = {
154 | isa = PBXShellScriptBuildPhase;
155 | buildActionMask = 2147483647;
156 | files = (
157 | );
158 | inputPaths = (
159 | );
160 | outputPaths = (
161 | );
162 | runOnlyForDeploymentPostprocessing = 0;
163 | shellPath = /bin/sh;
164 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
165 | };
166 | /* End PBXShellScriptBuildPhase section */
167 |
168 | /* Begin PBXSourcesBuildPhase section */
169 | 03763D6E2072443D000AFB80 /* Sources */ = {
170 | isa = PBXSourcesBuildPhase;
171 | buildActionMask = 2147483647;
172 | files = (
173 | 036F406020760DF9002AC7C0 /* ViewController.swift in Sources */,
174 | 03763D8820724463000AFB80 /* Extensions.swift in Sources */,
175 | 03763D762072443D000AFB80 /* AppDelegate.swift in Sources */,
176 | );
177 | runOnlyForDeploymentPostprocessing = 0;
178 | };
179 | /* End PBXSourcesBuildPhase section */
180 |
181 | /* Begin PBXVariantGroup section */
182 | 03763D7E2072443D000AFB80 /* LaunchScreen.storyboard */ = {
183 | isa = PBXVariantGroup;
184 | children = (
185 | 03763D7F2072443D000AFB80 /* Base */,
186 | );
187 | name = LaunchScreen.storyboard;
188 | sourceTree = "";
189 | };
190 | /* End PBXVariantGroup section */
191 |
192 | /* Begin XCBuildConfiguration section */
193 | 03763D822072443D000AFB80 /* Debug */ = {
194 | isa = XCBuildConfiguration;
195 | buildSettings = {
196 | ALWAYS_SEARCH_USER_PATHS = NO;
197 | CLANG_ANALYZER_NONNULL = YES;
198 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
200 | CLANG_CXX_LIBRARY = "libc++";
201 | CLANG_ENABLE_MODULES = YES;
202 | CLANG_ENABLE_OBJC_ARC = YES;
203 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
204 | CLANG_WARN_BOOL_CONVERSION = YES;
205 | CLANG_WARN_COMMA = YES;
206 | CLANG_WARN_CONSTANT_CONVERSION = YES;
207 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
209 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
210 | CLANG_WARN_EMPTY_BODY = YES;
211 | CLANG_WARN_ENUM_CONVERSION = YES;
212 | CLANG_WARN_INFINITE_RECURSION = YES;
213 | CLANG_WARN_INT_CONVERSION = YES;
214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
218 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
219 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
220 | CLANG_WARN_STRICT_PROTOTYPES = YES;
221 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
222 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
223 | CLANG_WARN_UNREACHABLE_CODE = YES;
224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
225 | CODE_SIGN_IDENTITY = "iPhone Developer";
226 | COPY_PHASE_STRIP = NO;
227 | DEBUG_INFORMATION_FORMAT = dwarf;
228 | ENABLE_STRICT_OBJC_MSGSEND = YES;
229 | ENABLE_TESTABILITY = YES;
230 | GCC_C_LANGUAGE_STANDARD = gnu11;
231 | GCC_DYNAMIC_NO_PIC = NO;
232 | GCC_NO_COMMON_BLOCKS = YES;
233 | GCC_OPTIMIZATION_LEVEL = 0;
234 | GCC_PREPROCESSOR_DEFINITIONS = (
235 | "DEBUG=1",
236 | "$(inherited)",
237 | );
238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
240 | GCC_WARN_UNDECLARED_SELECTOR = YES;
241 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
242 | GCC_WARN_UNUSED_FUNCTION = YES;
243 | GCC_WARN_UNUSED_VARIABLE = YES;
244 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
245 | MTL_ENABLE_DEBUG_INFO = YES;
246 | ONLY_ACTIVE_ARCH = YES;
247 | SDKROOT = iphoneos;
248 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
249 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
250 | };
251 | name = Debug;
252 | };
253 | 03763D832072443D000AFB80 /* Release */ = {
254 | isa = XCBuildConfiguration;
255 | buildSettings = {
256 | ALWAYS_SEARCH_USER_PATHS = NO;
257 | CLANG_ANALYZER_NONNULL = YES;
258 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
260 | CLANG_CXX_LIBRARY = "libc++";
261 | CLANG_ENABLE_MODULES = YES;
262 | CLANG_ENABLE_OBJC_ARC = YES;
263 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
264 | CLANG_WARN_BOOL_CONVERSION = YES;
265 | CLANG_WARN_COMMA = YES;
266 | CLANG_WARN_CONSTANT_CONVERSION = YES;
267 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
269 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
270 | CLANG_WARN_EMPTY_BODY = YES;
271 | CLANG_WARN_ENUM_CONVERSION = YES;
272 | CLANG_WARN_INFINITE_RECURSION = YES;
273 | CLANG_WARN_INT_CONVERSION = YES;
274 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
275 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
276 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
278 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
279 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
280 | CLANG_WARN_STRICT_PROTOTYPES = YES;
281 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
282 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
283 | CLANG_WARN_UNREACHABLE_CODE = YES;
284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
285 | CODE_SIGN_IDENTITY = "iPhone Developer";
286 | COPY_PHASE_STRIP = NO;
287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
288 | ENABLE_NS_ASSERTIONS = NO;
289 | ENABLE_STRICT_OBJC_MSGSEND = YES;
290 | GCC_C_LANGUAGE_STANDARD = gnu11;
291 | GCC_NO_COMMON_BLOCKS = YES;
292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
294 | GCC_WARN_UNDECLARED_SELECTOR = YES;
295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
296 | GCC_WARN_UNUSED_FUNCTION = YES;
297 | GCC_WARN_UNUSED_VARIABLE = YES;
298 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
299 | MTL_ENABLE_DEBUG_INFO = NO;
300 | SDKROOT = iphoneos;
301 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
302 | VALIDATE_PRODUCT = YES;
303 | };
304 | name = Release;
305 | };
306 | 03763D852072443D000AFB80 /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | CODE_SIGN_STYLE = Automatic;
311 | DEVELOPMENT_TEAM = "";
312 | INFOPLIST_FILE = DoubleSliderDemo/Info.plist;
313 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
315 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSliderDemo;
316 | PRODUCT_NAME = "$(TARGET_NAME)";
317 | SWIFT_VERSION = 4.0;
318 | TARGETED_DEVICE_FAMILY = "1,2";
319 | };
320 | name = Debug;
321 | };
322 | 03763D862072443D000AFB80 /* Release */ = {
323 | isa = XCBuildConfiguration;
324 | buildSettings = {
325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
326 | CODE_SIGN_STYLE = Automatic;
327 | DEVELOPMENT_TEAM = "";
328 | INFOPLIST_FILE = DoubleSliderDemo/Info.plist;
329 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
330 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
331 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSliderDemo;
332 | PRODUCT_NAME = "$(TARGET_NAME)";
333 | SWIFT_VERSION = 4.0;
334 | TARGETED_DEVICE_FAMILY = "1,2";
335 | };
336 | name = Release;
337 | };
338 | /* End XCBuildConfiguration section */
339 |
340 | /* Begin XCConfigurationList section */
341 | 03763D6D2072443D000AFB80 /* Build configuration list for PBXProject "DoubleSliderDemo" */ = {
342 | isa = XCConfigurationList;
343 | buildConfigurations = (
344 | 03763D822072443D000AFB80 /* Debug */,
345 | 03763D832072443D000AFB80 /* Release */,
346 | );
347 | defaultConfigurationIsVisible = 0;
348 | defaultConfigurationName = Release;
349 | };
350 | 03763D842072443D000AFB80 /* Build configuration list for PBXNativeTarget "DoubleSliderDemo" */ = {
351 | isa = XCConfigurationList;
352 | buildConfigurations = (
353 | 03763D852072443D000AFB80 /* Debug */,
354 | 03763D862072443D000AFB80 /* Release */,
355 | );
356 | defaultConfigurationIsVisible = 0;
357 | defaultConfigurationName = Release;
358 | };
359 | /* End XCConfigurationList section */
360 | };
361 | rootObject = 03763D6A2072443D000AFB80 /* Project object */;
362 | }
363 |
--------------------------------------------------------------------------------
/DoubleSlider/DoubleSlider.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 030B81182085F67E00169A88 /* DoubleSliderLayerProtocol.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5620724317000AFB80 /* DoubleSliderLayerProtocol.swift */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 030B81192085F67E00169A88 /* DoubleSliderThumbLayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5420724317000AFB80 /* DoubleSliderThumbLayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
12 | 030B811A2085F67E00169A88 /* DoubleSliderTrackLayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5720724317000AFB80 /* DoubleSliderTrackLayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 030B811B2085F67E00169A88 /* DoubleSliderColors.swift in Headers */ = {isa = PBXBuildFile; fileRef = 0333886C207F22FE003F5981 /* DoubleSliderColors.swift */; settings = {ATTRIBUTES = (Public, ); }; };
14 | 030B811C2085F67E00169A88 /* DoubleSliderDelegates.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5E20724326000AFB80 /* DoubleSliderDelegates.swift */; settings = {ATTRIBUTES = (Public, ); }; };
15 | 030B811D2085F67E00169A88 /* DoubleSliderMain.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5D20724326000AFB80 /* DoubleSliderMain.swift */; settings = {ATTRIBUTES = (Public, ); }; };
16 | 030B811E2085F67E00169A88 /* DoubleSliderUpdateFunctions.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5C20724325000AFB80 /* DoubleSliderUpdateFunctions.swift */; settings = {ATTRIBUTES = (Public, ); }; };
17 | 030B811F2085F67E00169A88 /* DoubleSliderTouchTracking.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D5F20724326000AFB80 /* DoubleSliderTouchTracking.swift */; settings = {ATTRIBUTES = (Public, ); }; };
18 | 030B81202085F67E00169A88 /* Extensions.swift in Headers */ = {isa = PBXBuildFile; fileRef = 03763D6420724330000AFB80 /* Extensions.swift */; settings = {ATTRIBUTES = (Public, ); }; };
19 | 0333886D207F22FE003F5981 /* DoubleSliderColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0333886C207F22FE003F5981 /* DoubleSliderColors.swift */; };
20 | 03763D3C20724273000AFB80 /* DoubleSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = 03763D3A20724273000AFB80 /* DoubleSlider.h */; settings = {ATTRIBUTES = (Public, ); }; };
21 | 03763D5820724317000AFB80 /* DoubleSliderThumbLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5420724317000AFB80 /* DoubleSliderThumbLayer.swift */; };
22 | 03763D5A20724317000AFB80 /* DoubleSliderLayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5620724317000AFB80 /* DoubleSliderLayerProtocol.swift */; };
23 | 03763D5B20724317000AFB80 /* DoubleSliderTrackLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5720724317000AFB80 /* DoubleSliderTrackLayer.swift */; };
24 | 03763D6020724326000AFB80 /* DoubleSliderUpdateFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5C20724325000AFB80 /* DoubleSliderUpdateFunctions.swift */; };
25 | 03763D6120724326000AFB80 /* DoubleSliderMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5D20724326000AFB80 /* DoubleSliderMain.swift */; };
26 | 03763D6220724326000AFB80 /* DoubleSliderDelegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5E20724326000AFB80 /* DoubleSliderDelegates.swift */; };
27 | 03763D6320724326000AFB80 /* DoubleSliderTouchTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D5F20724326000AFB80 /* DoubleSliderTouchTracking.swift */; };
28 | 03763D6520724331000AFB80 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03763D6420724330000AFB80 /* Extensions.swift */; };
29 | 039F2E712085A1FA00A2DB79 /* DoubleSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = 03763D3A20724273000AFB80 /* DoubleSlider.h */; settings = {ATTRIBUTES = (Public, ); }; };
30 | 03DDABD52084AA7F00E2BB4A /* DoubleSliderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DDABD42084AA7F00E2BB4A /* DoubleSliderTests.swift */; };
31 | 03DDABD72084AA8000E2BB4A /* DoubleSlider.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03763D3720724273000AFB80 /* DoubleSlider.framework */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | 03DDABD82084AA8000E2BB4A /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 03763D2E20724273000AFB80 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 03763D3620724273000AFB80;
40 | remoteInfo = DoubleSlider;
41 | };
42 | /* End PBXContainerItemProxy section */
43 |
44 | /* Begin PBXFileReference section */
45 | 0333886C207F22FE003F5981 /* DoubleSliderColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleSliderColors.swift; sourceTree = ""; };
46 | 03763D3720724273000AFB80 /* DoubleSlider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DoubleSlider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 03763D3A20724273000AFB80 /* DoubleSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DoubleSlider.h; sourceTree = ""; };
48 | 03763D3B20724273000AFB80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
49 | 03763D5420724317000AFB80 /* DoubleSliderThumbLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderThumbLayer.swift; sourceTree = ""; };
50 | 03763D5620724317000AFB80 /* DoubleSliderLayerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderLayerProtocol.swift; sourceTree = ""; };
51 | 03763D5720724317000AFB80 /* DoubleSliderTrackLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderTrackLayer.swift; sourceTree = ""; };
52 | 03763D5C20724325000AFB80 /* DoubleSliderUpdateFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderUpdateFunctions.swift; sourceTree = ""; };
53 | 03763D5D20724326000AFB80 /* DoubleSliderMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderMain.swift; sourceTree = ""; };
54 | 03763D5E20724326000AFB80 /* DoubleSliderDelegates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderDelegates.swift; sourceTree = ""; };
55 | 03763D5F20724326000AFB80 /* DoubleSliderTouchTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderTouchTracking.swift; sourceTree = ""; };
56 | 03763D6420724330000AFB80 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
57 | 03DDABD22084AA7F00E2BB4A /* DoubleSliderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DoubleSliderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58 | 03DDABD42084AA7F00E2BB4A /* DoubleSliderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleSliderTests.swift; sourceTree = ""; };
59 | 03DDABD62084AA8000E2BB4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
60 | /* End PBXFileReference section */
61 |
62 | /* Begin PBXFrameworksBuildPhase section */
63 | 03763D3320724273000AFB80 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | );
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | 03DDABCF2084AA7F00E2BB4A /* Frameworks */ = {
71 | isa = PBXFrameworksBuildPhase;
72 | buildActionMask = 2147483647;
73 | files = (
74 | 03DDABD72084AA8000E2BB4A /* DoubleSlider.framework in Frameworks */,
75 | );
76 | runOnlyForDeploymentPostprocessing = 0;
77 | };
78 | /* End PBXFrameworksBuildPhase section */
79 |
80 | /* Begin PBXGroup section */
81 | 03763D2D20724273000AFB80 = {
82 | isa = PBXGroup;
83 | children = (
84 | 03763D3920724273000AFB80 /* DoubleSlider */,
85 | 03DDABD32084AA7F00E2BB4A /* DoubleSliderTests */,
86 | 03763D3820724273000AFB80 /* Products */,
87 | );
88 | sourceTree = "";
89 | };
90 | 03763D3820724273000AFB80 /* Products */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 03763D3720724273000AFB80 /* DoubleSlider.framework */,
94 | 03DDABD22084AA7F00E2BB4A /* DoubleSliderTests.xctest */,
95 | );
96 | name = Products;
97 | sourceTree = "";
98 | };
99 | 03763D3920724273000AFB80 /* DoubleSlider */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 03763D68207243D2000AFB80 /* CALayers */,
103 | 03763D67207243BF000AFB80 /* DoubleSlider */,
104 | 03763D66207243B7000AFB80 /* Extensions */,
105 | 03763D3B20724273000AFB80 /* Info.plist */,
106 | );
107 | path = DoubleSlider;
108 | sourceTree = "";
109 | };
110 | 03763D66207243B7000AFB80 /* Extensions */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 03763D6420724330000AFB80 /* Extensions.swift */,
114 | );
115 | path = Extensions;
116 | sourceTree = "";
117 | };
118 | 03763D67207243BF000AFB80 /* DoubleSlider */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 03763D3A20724273000AFB80 /* DoubleSlider.h */,
122 | 0333886C207F22FE003F5981 /* DoubleSliderColors.swift */,
123 | 03763D5E20724326000AFB80 /* DoubleSliderDelegates.swift */,
124 | 03763D5D20724326000AFB80 /* DoubleSliderMain.swift */,
125 | 03763D5C20724325000AFB80 /* DoubleSliderUpdateFunctions.swift */,
126 | 03763D5F20724326000AFB80 /* DoubleSliderTouchTracking.swift */,
127 | );
128 | path = DoubleSlider;
129 | sourceTree = "";
130 | };
131 | 03763D68207243D2000AFB80 /* CALayers */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 03763D5620724317000AFB80 /* DoubleSliderLayerProtocol.swift */,
135 | 03763D5420724317000AFB80 /* DoubleSliderThumbLayer.swift */,
136 | 03763D5720724317000AFB80 /* DoubleSliderTrackLayer.swift */,
137 | );
138 | path = CALayers;
139 | sourceTree = "";
140 | };
141 | 03DDABD32084AA7F00E2BB4A /* DoubleSliderTests */ = {
142 | isa = PBXGroup;
143 | children = (
144 | 03DDABD42084AA7F00E2BB4A /* DoubleSliderTests.swift */,
145 | 03DDABD62084AA8000E2BB4A /* Info.plist */,
146 | );
147 | path = DoubleSliderTests;
148 | sourceTree = "";
149 | };
150 | /* End PBXGroup section */
151 |
152 | /* Begin PBXHeadersBuildPhase section */
153 | 03763D3420724273000AFB80 /* Headers */ = {
154 | isa = PBXHeadersBuildPhase;
155 | buildActionMask = 2147483647;
156 | files = (
157 | 03763D3C20724273000AFB80 /* DoubleSlider.h in Headers */,
158 | 030B81182085F67E00169A88 /* DoubleSliderLayerProtocol.swift in Headers */,
159 | 030B81192085F67E00169A88 /* DoubleSliderThumbLayer.swift in Headers */,
160 | 030B811A2085F67E00169A88 /* DoubleSliderTrackLayer.swift in Headers */,
161 | 030B811B2085F67E00169A88 /* DoubleSliderColors.swift in Headers */,
162 | 030B811C2085F67E00169A88 /* DoubleSliderDelegates.swift in Headers */,
163 | 030B811D2085F67E00169A88 /* DoubleSliderMain.swift in Headers */,
164 | 030B811E2085F67E00169A88 /* DoubleSliderUpdateFunctions.swift in Headers */,
165 | 030B811F2085F67E00169A88 /* DoubleSliderTouchTracking.swift in Headers */,
166 | 030B81202085F67E00169A88 /* Extensions.swift in Headers */,
167 | );
168 | runOnlyForDeploymentPostprocessing = 0;
169 | };
170 | 039F2E7020859FFC00A2DB79 /* Headers */ = {
171 | isa = PBXHeadersBuildPhase;
172 | buildActionMask = 2147483647;
173 | files = (
174 | 039F2E712085A1FA00A2DB79 /* DoubleSlider.h in Headers */,
175 | );
176 | runOnlyForDeploymentPostprocessing = 0;
177 | };
178 | /* End PBXHeadersBuildPhase section */
179 |
180 | /* Begin PBXNativeTarget section */
181 | 03763D3620724273000AFB80 /* DoubleSlider */ = {
182 | isa = PBXNativeTarget;
183 | buildConfigurationList = 03763D3F20724273000AFB80 /* Build configuration list for PBXNativeTarget "DoubleSlider" */;
184 | buildPhases = (
185 | 03763D3220724273000AFB80 /* Sources */,
186 | 03763D3320724273000AFB80 /* Frameworks */,
187 | 03763D3420724273000AFB80 /* Headers */,
188 | 03763D3520724273000AFB80 /* Resources */,
189 | 0392EF092090410600DEE1E3 /* ShellScript */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | );
195 | name = DoubleSlider;
196 | productName = DoubleSlider;
197 | productReference = 03763D3720724273000AFB80 /* DoubleSlider.framework */;
198 | productType = "com.apple.product-type.framework";
199 | };
200 | 03DDABD12084AA7F00E2BB4A /* DoubleSliderTests */ = {
201 | isa = PBXNativeTarget;
202 | buildConfigurationList = 03DDABDC2084AA8000E2BB4A /* Build configuration list for PBXNativeTarget "DoubleSliderTests" */;
203 | buildPhases = (
204 | 03DDABCE2084AA7F00E2BB4A /* Sources */,
205 | 03DDABCF2084AA7F00E2BB4A /* Frameworks */,
206 | 039F2E7020859FFC00A2DB79 /* Headers */,
207 | 03DDABD02084AA7F00E2BB4A /* Resources */,
208 | );
209 | buildRules = (
210 | );
211 | dependencies = (
212 | 03DDABD92084AA8000E2BB4A /* PBXTargetDependency */,
213 | );
214 | name = DoubleSliderTests;
215 | productName = DoubleSliderTests;
216 | productReference = 03DDABD22084AA7F00E2BB4A /* DoubleSliderTests.xctest */;
217 | productType = "com.apple.product-type.bundle.unit-test";
218 | };
219 | /* End PBXNativeTarget section */
220 |
221 | /* Begin PBXProject section */
222 | 03763D2E20724273000AFB80 /* Project object */ = {
223 | isa = PBXProject;
224 | attributes = {
225 | LastSwiftUpdateCheck = 0920;
226 | LastUpgradeCheck = 1250;
227 | ORGANIZATIONNAME = yhkaplan;
228 | TargetAttributes = {
229 | 03763D3620724273000AFB80 = {
230 | CreatedOnToolsVersion = 9.2;
231 | LastSwiftMigration = 1150;
232 | ProvisioningStyle = Automatic;
233 | };
234 | 03DDABD12084AA7F00E2BB4A = {
235 | CreatedOnToolsVersion = 9.2;
236 | ProvisioningStyle = Automatic;
237 | };
238 | };
239 | };
240 | buildConfigurationList = 03763D3120724273000AFB80 /* Build configuration list for PBXProject "DoubleSlider" */;
241 | compatibilityVersion = "Xcode 8.0";
242 | developmentRegion = en;
243 | hasScannedForEncodings = 0;
244 | knownRegions = (
245 | en,
246 | Base,
247 | );
248 | mainGroup = 03763D2D20724273000AFB80;
249 | productRefGroup = 03763D3820724273000AFB80 /* Products */;
250 | projectDirPath = "";
251 | projectRoot = "";
252 | targets = (
253 | 03763D3620724273000AFB80 /* DoubleSlider */,
254 | 03DDABD12084AA7F00E2BB4A /* DoubleSliderTests */,
255 | );
256 | };
257 | /* End PBXProject section */
258 |
259 | /* Begin PBXResourcesBuildPhase section */
260 | 03763D3520724273000AFB80 /* Resources */ = {
261 | isa = PBXResourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | 03DDABD02084AA7F00E2BB4A /* Resources */ = {
268 | isa = PBXResourcesBuildPhase;
269 | buildActionMask = 2147483647;
270 | files = (
271 | );
272 | runOnlyForDeploymentPostprocessing = 0;
273 | };
274 | /* End PBXResourcesBuildPhase section */
275 |
276 | /* Begin PBXShellScriptBuildPhase section */
277 | 0392EF092090410600DEE1E3 /* ShellScript */ = {
278 | isa = PBXShellScriptBuildPhase;
279 | buildActionMask = 2147483647;
280 | files = (
281 | );
282 | inputPaths = (
283 | );
284 | outputPaths = (
285 | );
286 | runOnlyForDeploymentPostprocessing = 0;
287 | shellPath = /bin/sh;
288 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
289 | };
290 | /* End PBXShellScriptBuildPhase section */
291 |
292 | /* Begin PBXSourcesBuildPhase section */
293 | 03763D3220724273000AFB80 /* Sources */ = {
294 | isa = PBXSourcesBuildPhase;
295 | buildActionMask = 2147483647;
296 | files = (
297 | 03763D5820724317000AFB80 /* DoubleSliderThumbLayer.swift in Sources */,
298 | 0333886D207F22FE003F5981 /* DoubleSliderColors.swift in Sources */,
299 | 03763D6220724326000AFB80 /* DoubleSliderDelegates.swift in Sources */,
300 | 03763D5A20724317000AFB80 /* DoubleSliderLayerProtocol.swift in Sources */,
301 | 03763D6020724326000AFB80 /* DoubleSliderUpdateFunctions.swift in Sources */,
302 | 03763D6520724331000AFB80 /* Extensions.swift in Sources */,
303 | 03763D5B20724317000AFB80 /* DoubleSliderTrackLayer.swift in Sources */,
304 | 03763D6120724326000AFB80 /* DoubleSliderMain.swift in Sources */,
305 | 03763D6320724326000AFB80 /* DoubleSliderTouchTracking.swift in Sources */,
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | };
309 | 03DDABCE2084AA7F00E2BB4A /* Sources */ = {
310 | isa = PBXSourcesBuildPhase;
311 | buildActionMask = 2147483647;
312 | files = (
313 | 03DDABD52084AA7F00E2BB4A /* DoubleSliderTests.swift in Sources */,
314 | );
315 | runOnlyForDeploymentPostprocessing = 0;
316 | };
317 | /* End PBXSourcesBuildPhase section */
318 |
319 | /* Begin PBXTargetDependency section */
320 | 03DDABD92084AA8000E2BB4A /* PBXTargetDependency */ = {
321 | isa = PBXTargetDependency;
322 | target = 03763D3620724273000AFB80 /* DoubleSlider */;
323 | targetProxy = 03DDABD82084AA8000E2BB4A /* PBXContainerItemProxy */;
324 | };
325 | /* End PBXTargetDependency section */
326 |
327 | /* Begin XCBuildConfiguration section */
328 | 03763D3D20724273000AFB80 /* Debug */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ALWAYS_SEARCH_USER_PATHS = NO;
332 | CLANG_ANALYZER_NONNULL = YES;
333 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
334 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
335 | CLANG_CXX_LIBRARY = "libc++";
336 | CLANG_ENABLE_MODULES = YES;
337 | CLANG_ENABLE_OBJC_ARC = YES;
338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
339 | CLANG_WARN_BOOL_CONVERSION = YES;
340 | CLANG_WARN_COMMA = YES;
341 | CLANG_WARN_CONSTANT_CONVERSION = YES;
342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INFINITE_RECURSION = YES;
348 | CLANG_WARN_INT_CONVERSION = YES;
349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
355 | CLANG_WARN_STRICT_PROTOTYPES = YES;
356 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
357 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
358 | CLANG_WARN_UNREACHABLE_CODE = YES;
359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
360 | CODE_SIGN_IDENTITY = "iPhone Developer";
361 | COPY_PHASE_STRIP = NO;
362 | CURRENT_PROJECT_VERSION = 1;
363 | DEBUG_INFORMATION_FORMAT = dwarf;
364 | ENABLE_STRICT_OBJC_MSGSEND = YES;
365 | ENABLE_TESTABILITY = YES;
366 | GCC_C_LANGUAGE_STANDARD = gnu11;
367 | GCC_DYNAMIC_NO_PIC = NO;
368 | GCC_NO_COMMON_BLOCKS = YES;
369 | GCC_OPTIMIZATION_LEVEL = 0;
370 | GCC_PREPROCESSOR_DEFINITIONS = (
371 | "DEBUG=1",
372 | "$(inherited)",
373 | );
374 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
375 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
376 | GCC_WARN_UNDECLARED_SELECTOR = YES;
377 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
378 | GCC_WARN_UNUSED_FUNCTION = YES;
379 | GCC_WARN_UNUSED_VARIABLE = YES;
380 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
381 | MTL_ENABLE_DEBUG_INFO = YES;
382 | ONLY_ACTIVE_ARCH = YES;
383 | SDKROOT = iphoneos;
384 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
385 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
386 | VERSIONING_SYSTEM = "apple-generic";
387 | VERSION_INFO_PREFIX = "";
388 | };
389 | name = Debug;
390 | };
391 | 03763D3E20724273000AFB80 /* Release */ = {
392 | isa = XCBuildConfiguration;
393 | buildSettings = {
394 | ALWAYS_SEARCH_USER_PATHS = NO;
395 | CLANG_ANALYZER_NONNULL = YES;
396 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
397 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
398 | CLANG_CXX_LIBRARY = "libc++";
399 | CLANG_ENABLE_MODULES = YES;
400 | CLANG_ENABLE_OBJC_ARC = YES;
401 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
402 | CLANG_WARN_BOOL_CONVERSION = YES;
403 | CLANG_WARN_COMMA = YES;
404 | CLANG_WARN_CONSTANT_CONVERSION = YES;
405 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
406 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
407 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
408 | CLANG_WARN_EMPTY_BODY = YES;
409 | CLANG_WARN_ENUM_CONVERSION = YES;
410 | CLANG_WARN_INFINITE_RECURSION = YES;
411 | CLANG_WARN_INT_CONVERSION = YES;
412 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
413 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
414 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
415 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
416 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
417 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
418 | CLANG_WARN_STRICT_PROTOTYPES = YES;
419 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
420 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
421 | CLANG_WARN_UNREACHABLE_CODE = YES;
422 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
423 | CODE_SIGN_IDENTITY = "iPhone Developer";
424 | COPY_PHASE_STRIP = NO;
425 | CURRENT_PROJECT_VERSION = 1;
426 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
427 | ENABLE_NS_ASSERTIONS = NO;
428 | ENABLE_STRICT_OBJC_MSGSEND = YES;
429 | GCC_C_LANGUAGE_STANDARD = gnu11;
430 | GCC_NO_COMMON_BLOCKS = YES;
431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
433 | GCC_WARN_UNDECLARED_SELECTOR = YES;
434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
435 | GCC_WARN_UNUSED_FUNCTION = YES;
436 | GCC_WARN_UNUSED_VARIABLE = YES;
437 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
438 | MTL_ENABLE_DEBUG_INFO = NO;
439 | SDKROOT = iphoneos;
440 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
441 | VALIDATE_PRODUCT = YES;
442 | VERSIONING_SYSTEM = "apple-generic";
443 | VERSION_INFO_PREFIX = "";
444 | };
445 | name = Release;
446 | };
447 | 03763D4020724273000AFB80 /* Debug */ = {
448 | isa = XCBuildConfiguration;
449 | buildSettings = {
450 | CLANG_ENABLE_MODULES = YES;
451 | CODE_SIGN_IDENTITY = "";
452 | CODE_SIGN_STYLE = Automatic;
453 | DEFINES_MODULE = YES;
454 | DYLIB_COMPATIBILITY_VERSION = 1;
455 | DYLIB_CURRENT_VERSION = 1;
456 | DYLIB_INSTALL_NAME_BASE = "@rpath";
457 | INFOPLIST_FILE = DoubleSlider/Info.plist;
458 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
459 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
460 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
461 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSlider;
462 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
463 | SKIP_INSTALL = YES;
464 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
465 | SWIFT_VERSION = 5.0;
466 | TARGETED_DEVICE_FAMILY = "1,2";
467 | };
468 | name = Debug;
469 | };
470 | 03763D4120724273000AFB80 /* Release */ = {
471 | isa = XCBuildConfiguration;
472 | buildSettings = {
473 | CLANG_ENABLE_MODULES = YES;
474 | CODE_SIGN_IDENTITY = "";
475 | CODE_SIGN_STYLE = Automatic;
476 | DEFINES_MODULE = YES;
477 | DYLIB_COMPATIBILITY_VERSION = 1;
478 | DYLIB_CURRENT_VERSION = 1;
479 | DYLIB_INSTALL_NAME_BASE = "@rpath";
480 | INFOPLIST_FILE = DoubleSlider/Info.plist;
481 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
482 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
484 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSlider;
485 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
486 | SKIP_INSTALL = YES;
487 | SWIFT_VERSION = 5.0;
488 | TARGETED_DEVICE_FAMILY = "1,2";
489 | };
490 | name = Release;
491 | };
492 | 03DDABDA2084AA8000E2BB4A /* Debug */ = {
493 | isa = XCBuildConfiguration;
494 | buildSettings = {
495 | CODE_SIGN_STYLE = Automatic;
496 | INFOPLIST_FILE = DoubleSliderTests/Info.plist;
497 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
498 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSliderTests;
499 | PRODUCT_NAME = "$(TARGET_NAME)";
500 | SWIFT_VERSION = 4.0;
501 | TARGETED_DEVICE_FAMILY = "1,2";
502 | };
503 | name = Debug;
504 | };
505 | 03DDABDB2084AA8000E2BB4A /* Release */ = {
506 | isa = XCBuildConfiguration;
507 | buildSettings = {
508 | CODE_SIGN_STYLE = Automatic;
509 | INFOPLIST_FILE = DoubleSliderTests/Info.plist;
510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
511 | PRODUCT_BUNDLE_IDENTIFIER = yhkaplan.DoubleSliderTests;
512 | PRODUCT_NAME = "$(TARGET_NAME)";
513 | SWIFT_VERSION = 4.0;
514 | TARGETED_DEVICE_FAMILY = "1,2";
515 | };
516 | name = Release;
517 | };
518 | /* End XCBuildConfiguration section */
519 |
520 | /* Begin XCConfigurationList section */
521 | 03763D3120724273000AFB80 /* Build configuration list for PBXProject "DoubleSlider" */ = {
522 | isa = XCConfigurationList;
523 | buildConfigurations = (
524 | 03763D3D20724273000AFB80 /* Debug */,
525 | 03763D3E20724273000AFB80 /* Release */,
526 | );
527 | defaultConfigurationIsVisible = 0;
528 | defaultConfigurationName = Release;
529 | };
530 | 03763D3F20724273000AFB80 /* Build configuration list for PBXNativeTarget "DoubleSlider" */ = {
531 | isa = XCConfigurationList;
532 | buildConfigurations = (
533 | 03763D4020724273000AFB80 /* Debug */,
534 | 03763D4120724273000AFB80 /* Release */,
535 | );
536 | defaultConfigurationIsVisible = 0;
537 | defaultConfigurationName = Release;
538 | };
539 | 03DDABDC2084AA8000E2BB4A /* Build configuration list for PBXNativeTarget "DoubleSliderTests" */ = {
540 | isa = XCConfigurationList;
541 | buildConfigurations = (
542 | 03DDABDA2084AA8000E2BB4A /* Debug */,
543 | 03DDABDB2084AA8000E2BB4A /* Release */,
544 | );
545 | defaultConfigurationIsVisible = 0;
546 | defaultConfigurationName = Release;
547 | };
548 | /* End XCConfigurationList section */
549 | };
550 | rootObject = 03763D2E20724273000AFB80 /* Project object */;
551 | }
552 |
--------------------------------------------------------------------------------