├── MBDoubleSliderExample
├── example.png
├── Documents.png
├── ibInspectable.png
├── ViewController.swift
├── AppDelegate.swift
├── MBCustomKnob.swift
├── Info.plist
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── MBTransparentWindow.swift
├── MBFilledView.swift
├── MBDoubleSlider.swift
└── Base.lproj
│ └── Main.storyboard
├── MBDoubleSliderExample.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── Viorel.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── MBDoubleSliderExample.xcscheme
└── project.pbxproj
├── LICENSE
└── README.md
/MBDoubleSliderExample/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-Viorel/MBDoubleSliderExample/HEAD/MBDoubleSliderExample/example.png
--------------------------------------------------------------------------------
/MBDoubleSliderExample/Documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-Viorel/MBDoubleSliderExample/HEAD/MBDoubleSliderExample/Documents.png
--------------------------------------------------------------------------------
/MBDoubleSliderExample/ibInspectable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-Viorel/MBDoubleSliderExample/HEAD/MBDoubleSliderExample/ibInspectable.png
--------------------------------------------------------------------------------
/MBDoubleSliderExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MBDoubleSliderExample
4 | //
5 | // Created by Viorel Porumbescu on 19/01/2017.
6 | // Copyright © 2017 Viorel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | override var representedObject: Any? {
20 | didSet {
21 | // Update the view, if already loaded.
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MBDoubleSliderExample
4 | //
5 | // Created by Viorel Porumbescu on 19/01/2017.
6 | // Copyright © 2017 Viorel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample.xcodeproj/xcuserdata/Viorel.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MBDoubleSliderExample.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | F28C7A281E314C71009059C5
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/MBCustomKnob.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MBCustomKnob.swift
3 | // customDoubleSlider
4 | //
5 | // Created by Viorel Porumbescu on 18/10/15.
6 | // Copyright (c) 2015 Viorel Porumbescu. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 |
12 | @IBDesignable
13 | class MBCustomKnob: NSView {
14 |
15 | var color:NSColor = NSColor.white
16 | var borderColor: NSColor = NSColor.lightGray.withAlphaComponent(0.5)
17 |
18 | override func draw(_ dirtyRect: NSRect) {
19 | super.draw(dirtyRect)
20 | self.wantsLayer = true
21 | self.layer?.cornerRadius = self.frame.height / 2
22 | self.layer?.borderColor = borderColor.cgColor
23 | self.layer?.borderWidth = 1.2
24 | self.layer?.backgroundColor = color.cgColor
25 | self.layer?.masksToBounds = false
26 |
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Cornel Viorel
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 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Viorel. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/MBDoubleSliderExample/MBTransparentWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MBTransparentWindow.swift
3 | // iClock
4 | //
5 | // Created by Viorel Porumbescu on 23/02/16.
6 | // Copyright © 2016 Viorel Porumbescu. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class MBTransparentWindow: NSWindow {
12 |
13 | // Override init to make window completely transparent and movable by background
14 | override init(contentRect: NSRect, styleMask aStyle: NSWindowStyleMask, backing bufferingType: NSBackingStoreType, defer flag: Bool) {
15 |
16 | super.init(contentRect: contentRect, styleMask: aStyle, backing: bufferingType, defer: flag)
17 | self.backgroundColor = NSColor.clear
18 | self.level = 0
19 | self.isOpaque = false
20 | self.hasShadow = false
21 | self.titleVisibility = NSWindowTitleVisibility.hidden
22 | self.titlebarAppearsTransparent = true
23 | self.isMovableByWindowBackground = true
24 | self.standardWindowButton(NSWindowButton.miniaturizeButton)?.isHidden = true
25 | self.standardWindowButton(NSWindowButton.zoomButton)?.isHidden = true
26 | self.standardWindowButton(NSWindowButton.closeButton)?.isHidden = false
27 | }
28 |
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MBDoubleSliderExample
2 | 
3 | [](https://swift.org)
4 | [](https://developer.apple.com/xcode)
5 |
6 | CustomDoubleSlider for macOs written in swift 3.0
7 |
8 | A double slider uset to select a range. The range is specific for time, but can be adaptad to use anywhere.
9 |
10 | 
11 |
12 | ## IBInspectable properties
13 | 
14 |
15 | ### Requirements
16 | - macOS
17 | - Xcode 8.0+
18 |
19 | ## Developer website
20 |
21 | A team is created from the passion of a common goal or from the interest of profit. The passion will generate respect for what you believe in, but greed will lead to demagogy - the cancer of every group. We are an united team with the passion to create truly meaningful tools, perfected so that it will make every user feel he can do everything. Intuitive for a beginner, complete for professionals.
22 |
23 |
[Minglebit website](https://minglebit.com)
24 |
25 |
[OnTime PRO-Ultimate time tool](https://itunes.apple.com/us/app/iclock-pro-the-ultimate-time-tool/id1244748504?ls=1&mt=12)
26 |
27 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample.xcodeproj/xcuserdata/Viorel.xcuserdatad/xcschemes/MBDoubleSliderExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
69 |
70 |
74 |
75 |
76 |
77 |
78 |
79 |
85 |
87 |
93 |
94 |
95 |
96 |
98 |
99 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/MBFilledView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MBFilledView.swift
3 | // iClock
4 | //
5 | // Created by Viorel Porumbescu on 23/02/16.
6 | // Copyright © 2016 Viorel Porumbescu. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @IBDesignable
12 | class MBFilledView: NSView {
13 |
14 |
15 | @IBInspectable var backgroundColor:NSColor = NSColor.black.withAlphaComponent(0.5) { didSet { self.needsDisplay = true} }
16 | /**
17 | This kind of view will lwt you to round
18 | */
19 | @IBInspectable var cornerRadius: CGFloat = 5.0 { didSet { self.needsDisplay = true} }
20 | @IBInspectable var radiusTopLeft:Bool = true { didSet { self.needsDisplay = true} }
21 | @IBInspectable var radiusTopRight:Bool = true { didSet { self.needsDisplay = true} }
22 | @IBInspectable var radiusBottomLeft: Bool = true { didSet { self.needsDisplay = true} }
23 | @IBInspectable var radiusBottomRight:Bool = true { didSet { self.needsDisplay = true} }
24 |
25 | @IBInspectable var hasShadow: Bool = false { didSet { self.needsDisplay = true} }
26 | @IBInspectable var shadowColor:NSColor = NSColor.black { didSet { self.needsDisplay = true} }
27 | @IBInspectable var xShadowPosition:Int = 2 { didSet { self.needsDisplay = true} }
28 | @IBInspectable var yShadowPosition: Int = 1 { didSet { self.needsDisplay = true} }
29 |
30 |
31 | @IBInspectable var hasVisualEffectView:Bool = false { didSet{ self.setupLayers()}}
32 | @IBInspectable var materialView:Int = 9
33 |
34 |
35 | required init?(coder: NSCoder) {
36 | super.init(coder: coder)
37 | self.setupLayers()
38 | }
39 |
40 |
41 | override init(frame frameRect: NSRect) {
42 | super.init(frame: frameRect)
43 | self.setupLayers()
44 | }
45 |
46 | func setupLayers() {
47 |
48 | if self.hasVisualEffectView {
49 | let view = NSVisualEffectView(frame: self.bounds)
50 | view.identifier = "1112"
51 | view.blendingMode = NSVisualEffectBlendingMode.behindWindow
52 | view.state = NSVisualEffectState.active
53 | view.wantsLayer = true
54 | view.layer?.masksToBounds = true
55 | view.layer?.cornerRadius = cornerRadius
56 | if let met = NSVisualEffectMaterial(rawValue: materialView) {
57 | view.material = met
58 | } else {
59 | view.material = .ultraDark
60 | }
61 | self.addSubview(view, positioned: .below, relativeTo: nil)
62 | } else {
63 | for view in self.subviews {
64 | if view.identifier == "1112" {
65 | view.removeFromSuperview()
66 | }
67 | }
68 | }
69 | }
70 |
71 |
72 | override func draw(_ dirtyRect: NSRect) {
73 | super.draw(dirtyRect)
74 | // Configure layers & colors //
75 | if hasVisualEffectView {
76 | setupLayers()
77 | }
78 | self.wantsLayer = true
79 | // Configure shadow //
80 | if hasShadow {
81 | self.layer?.shadowOffset = NSSize(width: xShadowPosition, height: yShadowPosition)
82 | self.layer?.shadowOpacity = 0.85
83 | self.layer?.shadowColor = shadowColor.cgColor
84 | }
85 | self.layer?.backgroundColor = NSColor.clear.cgColor
86 | self.layer?.masksToBounds = true
87 | if radiusTopLeft && radiusTopRight && radiusBottomLeft && radiusBottomRight {
88 | self.layer?.cornerRadius = cornerRadius
89 | self.layer?.backgroundColor = backgroundColor.cgColor
90 | } else {
91 |
92 | //Set background color and radius
93 | let rectangleRect = self.frame
94 | let rectangleInnerRect = NSInsetRect(rectangleRect, cornerRadius, cornerRadius)
95 | let rectanglePath = NSBezierPath()
96 | radiusBottomLeft ? rectanglePath.appendArc(withCenter: NSMakePoint(NSMinX(rectangleInnerRect), NSMinY(rectangleInnerRect)), radius: cornerRadius, startAngle: 180, endAngle: 270) : rectanglePath.move(to: NSMakePoint(NSMinX(rectangleRect), NSMinY(rectangleRect)))
97 | radiusBottomRight ? rectanglePath.appendArc(withCenter: NSMakePoint(NSMaxX(rectangleInnerRect), NSMinY(rectangleInnerRect)), radius: cornerRadius, startAngle: 270, endAngle: 360) : rectanglePath.line(to: NSMakePoint(NSMaxX(rectangleRect), NSMinY(rectangleRect)))
98 | radiusTopRight ? rectanglePath.appendArc(withCenter: NSMakePoint(NSMaxX(rectangleInnerRect), NSMaxY(rectangleInnerRect)), radius: cornerRadius, startAngle: 0, endAngle: 90) : rectanglePath.line(to: NSMakePoint(NSMaxX(rectangleRect), NSMaxY(rectangleRect)))
99 | radiusTopLeft ? rectanglePath.appendArc(withCenter: NSMakePoint(NSMinX(rectangleInnerRect), NSMaxY(rectangleInnerRect)), radius: cornerRadius, startAngle: 90, endAngle: 180) : rectanglePath.line(to: NSMakePoint(NSMinX(rectangleRect), NSMaxY(rectangleRect)))
100 | rectanglePath.close()
101 |
102 | backgroundColor.set()
103 | rectanglePath.fill()
104 | }
105 | }
106 |
107 |
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/MBDoubleSlider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MBDoubleSlider.swift
3 | // customDoubleSlider
4 | //
5 | // Created by Viorel Porumbescu on 18/10/15.
6 | // Copyright (c) 2015 Viorel Porumbescu. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | protocol MBDoubleSliderDelegate {
12 | func controller(_ controller: MBDoubleSlider , didChangeFirstValue: CGFloat , secondValue: CGFloat)
13 | // TODO: shold be an optional protocol.
14 | // func controller(_ controller: MBDoubleSlider, willBeginChangeValues first:CGFloat, second:CGFloat)
15 | }
16 |
17 | @IBDesignable
18 | class MBDoubleSlider: NSControl {
19 |
20 | @IBInspectable var backgroundLineColor: NSColor = NSColor(red:0.780, green:0.780, blue:0.780, alpha:1)
21 | @IBInspectable var selectionLineColor: NSColor = NSColor(red:0.231, green:0.600, blue:0.988, alpha:1)
22 | @IBInspectable var textColor:NSColor = NSColor.gray {didSet {initViews()}}
23 | // TODO: Add option to hide info labels
24 |
25 | var minValue: CGFloat = 0
26 | var maxValue: CGFloat = 100
27 |
28 | var firstValue: CGFloat = 0 { didSet{ if firstValue < minValue || firstValue > secondValue { firstValue = minValue }; self.needsDisplay = true}}
29 | var secondValue: CGFloat = 100 { didSet{ if secondValue < firstValue || secondValue > maxValue { secondValue = maxValue } ; self.needsDisplay = true}}
30 |
31 | var minimValue:CGFloat = 10
32 |
33 | var delegate: MBDoubleSliderDelegate?
34 |
35 | // Custom Private Vars
36 | fileprivate var firstKnob:MBCustomKnob = MBCustomKnob()
37 | fileprivate var secondKnob:MBCustomKnob = MBCustomKnob()
38 | fileprivate var firstLabel:NSTextField = NSTextField(frame: NSMakeRect(0, -2, 30, 20))
39 | fileprivate var secondLabel:NSTextField = NSTextField(frame: NSMakeRect(0, -2, 30, 20))
40 | fileprivate var yOrigin:CGFloat = 0
41 | fileprivate var lineMaxWidh: CGFloat = 0
42 | fileprivate var shouldMoveFirst:Bool = false
43 | fileprivate var shouldMoveLast: Bool = false
44 |
45 |
46 |
47 |
48 |
49 | override init(frame frameRect: NSRect) {
50 | super.init(frame: frameRect)
51 | setUpLabels()
52 | // let shadow = NSShadow()
53 | // shadow.shadowColor = NSColor.black.withAlphaComponent(0.5)
54 | // shadow.shadowOffset = NSMakeSize(0.1, 0.1)
55 | // shadow.shadowBlurRadius = 3
56 | //
57 | // firstKnob.shadow = shadow
58 | // secondKnob.shadow = shadow
59 | }
60 |
61 |
62 |
63 | required init?(coder: NSCoder) {
64 | super.init(coder: coder)
65 | setUpLabels()
66 | // let shadow = NSShadow()
67 | // shadow.shadowColor = NSColor.black.withAlphaComponent(0.5)
68 | // shadow.shadowOffset = NSMakeSize(0.1, 0.1)
69 | // shadow.shadowBlurRadius = 3
70 | //
71 | // firstKnob.shadow = shadow
72 | // secondKnob.shadow = shadow
73 | }
74 |
75 |
76 |
77 |
78 |
79 | override func draw(_ dirtyRect: NSRect) {
80 | super.draw(dirtyRect)
81 | initViews()
82 | }
83 |
84 |
85 |
86 | var trackingArea:NSTrackingArea!
87 | override func updateTrackingAreas() {
88 | if trackingArea != nil {
89 | self.removeTrackingArea(trackingArea)
90 | }
91 | trackingArea = NSTrackingArea(rect: self.bounds, options: [NSTrackingAreaOptions.enabledDuringMouseDrag , NSTrackingAreaOptions.mouseMoved, NSTrackingAreaOptions.activeAlways, .cursorUpdate], owner: self, userInfo: nil)
92 | self.addTrackingArea(trackingArea)
93 | }
94 |
95 |
96 |
97 |
98 | /// Mouse down event : We test if current mouse location is inside of first or second knob. If yes, then we
99 | // tell that selected knob that it can be moved
100 | override func mouseDown(with theEvent: NSEvent) {
101 | let loc = self.convert(theEvent.locationInWindow, from: self.window?.contentView)
102 |
103 | if NSPointInRect(loc, firstKnob.frame) {
104 | shouldMoveFirst = true
105 | }
106 | if NSPointInRect(loc, secondKnob.frame) {
107 | shouldMoveLast = true
108 | }
109 | }
110 |
111 | // Mouse dragged Event : if is any selected knob we will move to new position, and we calculate
112 | // new the new slider values
113 | override func mouseDragged(with theEvent: NSEvent) {
114 | let loc = self.convert(theEvent.locationInWindow, from: self.window?.contentView)
115 |
116 | let _ = self.convert(theEvent.locationInWindow, to: self)
117 |
118 | if shouldMoveFirst {
119 | let minim = CGFloat(0)
120 | let maxim = secondKnob.frame.origin.x - firstKnob.frame.width - ((lineMaxWidh / maxValue) * minimValue )
121 | if loc.x > minim && loc.x < maxim {
122 | firstValue = (loc.x * maxValue) / lineMaxWidh
123 | if loc.x < minim {
124 | firstValue = (minim * maxValue) / lineMaxWidh
125 | }
126 | if loc.x > maxim {
127 | firstValue = (maxim * maxValue) / lineMaxWidh
128 | }
129 | }else if loc.x < minim {
130 | firstValue = (minim * maxValue) / lineMaxWidh
131 | }
132 | self.needsDisplay = true
133 | create()
134 | }
135 | if shouldMoveLast {
136 | let minim = firstKnob.frame.origin.x + secondKnob.frame.width + ((lineMaxWidh / maxValue) * minimValue )
137 | let maxim = lineMaxWidh
138 | if loc.x > minim && loc.x < maxim {
139 | secondValue = (loc.x * maxValue) / lineMaxWidh
140 | if loc.x < minim {
141 | secondValue = (minim * maxValue) / lineMaxWidh
142 | }
143 | if loc.x > maxim {
144 | secondValue = (maxim * maxValue) / lineMaxWidh
145 | }
146 | } else if loc.x > maxim {
147 | secondValue = (maxim * maxValue) / lineMaxWidh
148 | }
149 | self.needsDisplay = true
150 | create()
151 | }
152 | }
153 |
154 | //Mouse up event : We "deselect" both knobs.
155 | override func mouseUp(with theEvent: NSEvent) {
156 | shouldMoveLast = false
157 | shouldMoveFirst = false
158 | }
159 |
160 | func initViews() {
161 | let textOrigin: CGFloat = 0
162 | let knobSize = self.frame.height * 0.4
163 | lineMaxWidh = self.frame.width - knobSize
164 | yOrigin = self.frame.height * 0.6
165 | let firstX = (firstValue * lineMaxWidh) / maxValue
166 | let secondX = (secondValue * lineMaxWidh) / maxValue
167 | firstKnob.setFrameSize(NSMakeSize(knobSize, knobSize))
168 | secondKnob.setFrameSize(NSMakeSize(knobSize, knobSize))
169 | firstKnob.setFrameOrigin(NSMakePoint(firstX, yOrigin))
170 | secondKnob.setFrameOrigin(NSMakePoint(secondX, yOrigin))
171 | firstLabel.setFrameOrigin(NSMakePoint(firstX, textOrigin))
172 | secondLabel.setFrameOrigin(NSMakePoint(secondX, textOrigin))
173 |
174 | firstLabel.textColor = textColor
175 | secondLabel.textColor = textColor
176 |
177 | // Center text Label if is posible
178 | if firstX > 8 {
179 | firstLabel.setFrameOrigin(NSMakePoint(firstX - 8, textOrigin))
180 | }
181 | if secondX < lineMaxWidh - 8 {
182 | secondLabel.setFrameOrigin(NSMakePoint(secondX - 8, textOrigin))
183 | }
184 |
185 | if secondX > lineMaxWidh - 8 {
186 | secondLabel.setFrameOrigin(NSMakePoint(lineMaxWidh - 16, textOrigin))
187 | }
188 | if (firstLabel.frame.origin.x + NSWidth(firstLabel.frame) ) > secondLabel.frame.origin.x {
189 | let size = (secondLabel.frame.origin.x - (firstLabel.frame.origin.x + NSWidth(firstLabel.frame) )) / 2
190 | var state = true
191 | if firstX < 8 {
192 | state = false
193 | secondLabel.setFrameOrigin(NSMakePoint(secondLabel.frame.origin.x - size - size, textOrigin))
194 | }
195 | if secondX > lineMaxWidh - 8 {
196 | state = false
197 | firstLabel.setFrameOrigin(NSMakePoint(firstLabel.frame.origin.x + size + size, textOrigin))
198 | }
199 | if state {
200 | firstLabel.setFrameOrigin(NSMakePoint(firstLabel.frame.origin.x + size, textOrigin))
201 | secondLabel.setFrameOrigin(NSMakePoint(secondLabel.frame.origin.x - size, textOrigin))
202 | }
203 | }
204 | firstLabel.stringValue = secondsToMinute(firstValue)
205 | secondLabel.stringValue = secondsToMinute(secondValue)
206 | // Draw background line
207 | let backgroundLine = NSBezierPath()
208 | backgroundLine.move(to: NSMakePoint(knobSize * 0.5, self.frame.height * 0.8))
209 | backgroundLine.line(to: NSMakePoint(lineMaxWidh + knobSize * 0.5 , self.frame.height * 0.8))
210 | backgroundLine.lineCapStyle = NSLineCapStyle.roundLineCapStyle
211 | backgroundLine.lineWidth = 3
212 | backgroundLineColor.set()
213 | backgroundLine.stroke()
214 | ///Draw selection line (the line between knobs)
215 | let selectionLine = NSBezierPath()
216 | selectionLine.move(to: NSMakePoint(firstX + knobSize / 2 , self.frame.height * 0.8))
217 | selectionLine.line(to: NSMakePoint(secondX + knobSize / 2 , self.frame.height * 0.8))
218 | selectionLine.lineCapStyle = NSLineCapStyle.roundLineCapStyle
219 | selectionLine.lineWidth = 3
220 | selectionLineColor.setStroke()
221 | selectionLine.stroke()
222 |
223 | Swift.print(self.subviews.count)
224 | self.addSubview(firstKnob)
225 | self.addSubview(secondKnob)
226 | self.addSubview(firstLabel)
227 | self.addSubview(secondLabel)
228 | }
229 |
230 |
231 | func setUpLabels(){
232 | firstLabel.isBordered = false
233 | firstLabel.identifier = "10"
234 | firstLabel.isEditable = false
235 | firstLabel.isSelectable = false
236 | firstLabel.stringValue = "00:00"
237 | firstLabel.backgroundColor = NSColor.white.withAlphaComponent(0)
238 | firstLabel.font = NSFont(name: "HelveticaNeue", size: 10)
239 | firstLabel.textColor = NSColor.gray
240 | firstLabel.alignment = NSTextAlignment.left
241 |
242 | secondLabel.isBordered = false
243 | secondLabel.identifier = "10"
244 | secondLabel.isEditable = false
245 | secondLabel.isSelectable = false
246 | secondLabel.stringValue = "00:00"
247 | secondLabel.backgroundColor = NSColor.white.withAlphaComponent(0)
248 | secondLabel.font = NSFont(name: "HelveticaNeue", size: 10)
249 | secondLabel.textColor = NSColor.gray
250 | secondLabel.alignment = NSTextAlignment.left
251 |
252 | }
253 |
254 | /// If has a delegate we will send changed notification, and new values for slider.
255 | /// Also we trigger action if this control has one.
256 | func create() {
257 | if self.action != nil {
258 | NSApp.sendAction(self.action!, to: self.target, from: self)
259 | }
260 |
261 | if let delegate = self.delegate {
262 | delegate.controller(self, didChangeFirstValue: firstValue, secondValue: secondValue)
263 | }
264 | }
265 |
266 | /// Convert seconds to 00:00 format
267 | fileprivate func secondsToMinute(_ sec:CGFloat) -> String{
268 | let intSec = Int(sec)
269 | let min = intSec / 60
270 | let remainS = intSec % 60
271 | let str = NSString(format: "%02d:%02d", min, remainS)
272 | return String(str)
273 | }
274 |
275 | }
276 |
277 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F28C7A2D1E314C71009059C5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A2C1E314C71009059C5 /* AppDelegate.swift */; };
11 | F28C7A2F1E314C71009059C5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A2E1E314C71009059C5 /* ViewController.swift */; };
12 | F28C7A311E314C71009059C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F28C7A301E314C71009059C5 /* Assets.xcassets */; };
13 | F28C7A341E314C71009059C5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F28C7A321E314C71009059C5 /* Main.storyboard */; };
14 | F28C7A3D1E314C7A009059C5 /* MBCustomKnob.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A3B1E314C7A009059C5 /* MBCustomKnob.swift */; };
15 | F28C7A3E1E314C7A009059C5 /* MBDoubleSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A3C1E314C7A009059C5 /* MBDoubleSlider.swift */; };
16 | F28C7A411E314F01009059C5 /* MBTransparentWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A401E314F01009059C5 /* MBTransparentWindow.swift */; };
17 | F28C7A441E314F6F009059C5 /* MBFilledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C7A431E314F6F009059C5 /* MBFilledView.swift */; };
18 | F28C7A461E3157F3009059C5 /* example.png in Resources */ = {isa = PBXBuildFile; fileRef = F28C7A451E3157F3009059C5 /* example.png */; };
19 | F28C7A481E315924009059C5 /* ibInspectable.png in Resources */ = {isa = PBXBuildFile; fileRef = F28C7A471E315924009059C5 /* ibInspectable.png */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | F28C7A291E314C71009059C5 /* MBDoubleSliderExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MBDoubleSliderExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | F28C7A2C1E314C71009059C5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
25 | F28C7A2E1E314C71009059C5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
26 | F28C7A301E314C71009059C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | F28C7A331E314C71009059C5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
28 | F28C7A351E314C71009059C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | F28C7A3B1E314C7A009059C5 /* MBCustomKnob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBCustomKnob.swift; sourceTree = ""; };
30 | F28C7A3C1E314C7A009059C5 /* MBDoubleSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBDoubleSlider.swift; sourceTree = ""; };
31 | F28C7A401E314F01009059C5 /* MBTransparentWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBTransparentWindow.swift; sourceTree = ""; };
32 | F28C7A431E314F6F009059C5 /* MBFilledView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBFilledView.swift; sourceTree = ""; };
33 | F28C7A451E3157F3009059C5 /* example.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = example.png; sourceTree = ""; };
34 | F28C7A471E315924009059C5 /* ibInspectable.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ibInspectable.png; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | F28C7A261E314C71009059C5 /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | F28C7A201E314C71009059C5 = {
49 | isa = PBXGroup;
50 | children = (
51 | F28C7A2B1E314C71009059C5 /* MBDoubleSliderExample */,
52 | F28C7A2A1E314C71009059C5 /* Products */,
53 | );
54 | sourceTree = "";
55 | };
56 | F28C7A2A1E314C71009059C5 /* Products */ = {
57 | isa = PBXGroup;
58 | children = (
59 | F28C7A291E314C71009059C5 /* MBDoubleSliderExample.app */,
60 | );
61 | name = Products;
62 | sourceTree = "";
63 | };
64 | F28C7A2B1E314C71009059C5 /* MBDoubleSliderExample */ = {
65 | isa = PBXGroup;
66 | children = (
67 | F28C7A2C1E314C71009059C5 /* AppDelegate.swift */,
68 | F28C7A2E1E314C71009059C5 /* ViewController.swift */,
69 | F28C7A421E314F17009059C5 /* OtherResouces */,
70 | F28C7A3F1E314C7F009059C5 /* MBDoubleSlider */,
71 | F28C7A301E314C71009059C5 /* Assets.xcassets */,
72 | F28C7A321E314C71009059C5 /* Main.storyboard */,
73 | F28C7A351E314C71009059C5 /* Info.plist */,
74 | );
75 | path = MBDoubleSliderExample;
76 | sourceTree = "";
77 | };
78 | F28C7A3F1E314C7F009059C5 /* MBDoubleSlider */ = {
79 | isa = PBXGroup;
80 | children = (
81 | F28C7A3B1E314C7A009059C5 /* MBCustomKnob.swift */,
82 | F28C7A3C1E314C7A009059C5 /* MBDoubleSlider.swift */,
83 | );
84 | name = MBDoubleSlider;
85 | sourceTree = "";
86 | };
87 | F28C7A421E314F17009059C5 /* OtherResouces */ = {
88 | isa = PBXGroup;
89 | children = (
90 | F28C7A451E3157F3009059C5 /* example.png */,
91 | F28C7A471E315924009059C5 /* ibInspectable.png */,
92 | F28C7A401E314F01009059C5 /* MBTransparentWindow.swift */,
93 | F28C7A431E314F6F009059C5 /* MBFilledView.swift */,
94 | );
95 | name = OtherResouces;
96 | sourceTree = "";
97 | };
98 | /* End PBXGroup section */
99 |
100 | /* Begin PBXNativeTarget section */
101 | F28C7A281E314C71009059C5 /* MBDoubleSliderExample */ = {
102 | isa = PBXNativeTarget;
103 | buildConfigurationList = F28C7A381E314C71009059C5 /* Build configuration list for PBXNativeTarget "MBDoubleSliderExample" */;
104 | buildPhases = (
105 | F28C7A251E314C71009059C5 /* Sources */,
106 | F28C7A261E314C71009059C5 /* Frameworks */,
107 | F28C7A271E314C71009059C5 /* Resources */,
108 | );
109 | buildRules = (
110 | );
111 | dependencies = (
112 | );
113 | name = MBDoubleSliderExample;
114 | productName = MBDoubleSliderExample;
115 | productReference = F28C7A291E314C71009059C5 /* MBDoubleSliderExample.app */;
116 | productType = "com.apple.product-type.application";
117 | };
118 | /* End PBXNativeTarget section */
119 |
120 | /* Begin PBXProject section */
121 | F28C7A211E314C71009059C5 /* Project object */ = {
122 | isa = PBXProject;
123 | attributes = {
124 | LastSwiftUpdateCheck = 0820;
125 | LastUpgradeCheck = 0820;
126 | ORGANIZATIONNAME = Viorel;
127 | TargetAttributes = {
128 | F28C7A281E314C71009059C5 = {
129 | CreatedOnToolsVersion = 8.2.1;
130 | DevelopmentTeam = X34UB4R785;
131 | ProvisioningStyle = Automatic;
132 | };
133 | };
134 | };
135 | buildConfigurationList = F28C7A241E314C71009059C5 /* Build configuration list for PBXProject "MBDoubleSliderExample" */;
136 | compatibilityVersion = "Xcode 3.2";
137 | developmentRegion = English;
138 | hasScannedForEncodings = 0;
139 | knownRegions = (
140 | en,
141 | Base,
142 | );
143 | mainGroup = F28C7A201E314C71009059C5;
144 | productRefGroup = F28C7A2A1E314C71009059C5 /* Products */;
145 | projectDirPath = "";
146 | projectRoot = "";
147 | targets = (
148 | F28C7A281E314C71009059C5 /* MBDoubleSliderExample */,
149 | );
150 | };
151 | /* End PBXProject section */
152 |
153 | /* Begin PBXResourcesBuildPhase section */
154 | F28C7A271E314C71009059C5 /* Resources */ = {
155 | isa = PBXResourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | F28C7A461E3157F3009059C5 /* example.png in Resources */,
159 | F28C7A481E315924009059C5 /* ibInspectable.png in Resources */,
160 | F28C7A311E314C71009059C5 /* Assets.xcassets in Resources */,
161 | F28C7A341E314C71009059C5 /* Main.storyboard in Resources */,
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXResourcesBuildPhase section */
166 |
167 | /* Begin PBXSourcesBuildPhase section */
168 | F28C7A251E314C71009059C5 /* Sources */ = {
169 | isa = PBXSourcesBuildPhase;
170 | buildActionMask = 2147483647;
171 | files = (
172 | F28C7A3E1E314C7A009059C5 /* MBDoubleSlider.swift in Sources */,
173 | F28C7A441E314F6F009059C5 /* MBFilledView.swift in Sources */,
174 | F28C7A2F1E314C71009059C5 /* ViewController.swift in Sources */,
175 | F28C7A2D1E314C71009059C5 /* AppDelegate.swift in Sources */,
176 | F28C7A411E314F01009059C5 /* MBTransparentWindow.swift in Sources */,
177 | F28C7A3D1E314C7A009059C5 /* MBCustomKnob.swift in Sources */,
178 | );
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXSourcesBuildPhase section */
182 |
183 | /* Begin PBXVariantGroup section */
184 | F28C7A321E314C71009059C5 /* Main.storyboard */ = {
185 | isa = PBXVariantGroup;
186 | children = (
187 | F28C7A331E314C71009059C5 /* Base */,
188 | );
189 | name = Main.storyboard;
190 | sourceTree = "";
191 | };
192 | /* End PBXVariantGroup section */
193 |
194 | /* Begin XCBuildConfiguration section */
195 | F28C7A361E314C71009059C5 /* Debug */ = {
196 | isa = XCBuildConfiguration;
197 | buildSettings = {
198 | ALWAYS_SEARCH_USER_PATHS = NO;
199 | CLANG_ANALYZER_NONNULL = YES;
200 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
201 | CLANG_CXX_LIBRARY = "libc++";
202 | CLANG_ENABLE_MODULES = YES;
203 | CLANG_ENABLE_OBJC_ARC = YES;
204 | CLANG_WARN_BOOL_CONVERSION = YES;
205 | CLANG_WARN_CONSTANT_CONVERSION = YES;
206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
207 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
208 | CLANG_WARN_EMPTY_BODY = YES;
209 | CLANG_WARN_ENUM_CONVERSION = YES;
210 | CLANG_WARN_INFINITE_RECURSION = YES;
211 | CLANG_WARN_INT_CONVERSION = YES;
212 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
213 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
214 | CLANG_WARN_UNREACHABLE_CODE = YES;
215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
216 | CODE_SIGN_IDENTITY = "-";
217 | COPY_PHASE_STRIP = NO;
218 | DEBUG_INFORMATION_FORMAT = dwarf;
219 | ENABLE_STRICT_OBJC_MSGSEND = YES;
220 | ENABLE_TESTABILITY = YES;
221 | GCC_C_LANGUAGE_STANDARD = gnu99;
222 | GCC_DYNAMIC_NO_PIC = NO;
223 | GCC_NO_COMMON_BLOCKS = YES;
224 | GCC_OPTIMIZATION_LEVEL = 0;
225 | GCC_PREPROCESSOR_DEFINITIONS = (
226 | "DEBUG=1",
227 | "$(inherited)",
228 | );
229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
231 | GCC_WARN_UNDECLARED_SELECTOR = YES;
232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
233 | GCC_WARN_UNUSED_FUNCTION = YES;
234 | GCC_WARN_UNUSED_VARIABLE = YES;
235 | MACOSX_DEPLOYMENT_TARGET = 10.12;
236 | MTL_ENABLE_DEBUG_INFO = YES;
237 | ONLY_ACTIVE_ARCH = YES;
238 | SDKROOT = macosx;
239 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
240 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
241 | };
242 | name = Debug;
243 | };
244 | F28C7A371E314C71009059C5 /* Release */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | ALWAYS_SEARCH_USER_PATHS = NO;
248 | CLANG_ANALYZER_NONNULL = YES;
249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
250 | CLANG_CXX_LIBRARY = "libc++";
251 | CLANG_ENABLE_MODULES = YES;
252 | CLANG_ENABLE_OBJC_ARC = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
256 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
257 | CLANG_WARN_EMPTY_BODY = YES;
258 | CLANG_WARN_ENUM_CONVERSION = YES;
259 | CLANG_WARN_INFINITE_RECURSION = YES;
260 | CLANG_WARN_INT_CONVERSION = YES;
261 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
262 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | CODE_SIGN_IDENTITY = "-";
266 | COPY_PHASE_STRIP = NO;
267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
268 | ENABLE_NS_ASSERTIONS = NO;
269 | ENABLE_STRICT_OBJC_MSGSEND = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu99;
271 | GCC_NO_COMMON_BLOCKS = YES;
272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
274 | GCC_WARN_UNDECLARED_SELECTOR = YES;
275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
276 | GCC_WARN_UNUSED_FUNCTION = YES;
277 | GCC_WARN_UNUSED_VARIABLE = YES;
278 | MACOSX_DEPLOYMENT_TARGET = 10.12;
279 | MTL_ENABLE_DEBUG_INFO = NO;
280 | SDKROOT = macosx;
281 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
282 | };
283 | name = Release;
284 | };
285 | F28C7A391E314C71009059C5 /* Debug */ = {
286 | isa = XCBuildConfiguration;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | COMBINE_HIDPI_IMAGES = YES;
290 | DEVELOPMENT_TEAM = X34UB4R785;
291 | INFOPLIST_FILE = MBDoubleSliderExample/Info.plist;
292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
293 | PRODUCT_BUNDLE_IDENTIFIER = com.viorel.MBDoubleSliderExample;
294 | PRODUCT_NAME = "$(TARGET_NAME)";
295 | SWIFT_VERSION = 3.0;
296 | };
297 | name = Debug;
298 | };
299 | F28C7A3A1E314C71009059C5 /* Release */ = {
300 | isa = XCBuildConfiguration;
301 | buildSettings = {
302 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
303 | COMBINE_HIDPI_IMAGES = YES;
304 | DEVELOPMENT_TEAM = X34UB4R785;
305 | INFOPLIST_FILE = MBDoubleSliderExample/Info.plist;
306 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
307 | PRODUCT_BUNDLE_IDENTIFIER = com.viorel.MBDoubleSliderExample;
308 | PRODUCT_NAME = "$(TARGET_NAME)";
309 | SWIFT_VERSION = 3.0;
310 | };
311 | name = Release;
312 | };
313 | /* End XCBuildConfiguration section */
314 |
315 | /* Begin XCConfigurationList section */
316 | F28C7A241E314C71009059C5 /* Build configuration list for PBXProject "MBDoubleSliderExample" */ = {
317 | isa = XCConfigurationList;
318 | buildConfigurations = (
319 | F28C7A361E314C71009059C5 /* Debug */,
320 | F28C7A371E314C71009059C5 /* Release */,
321 | );
322 | defaultConfigurationIsVisible = 0;
323 | defaultConfigurationName = Release;
324 | };
325 | F28C7A381E314C71009059C5 /* Build configuration list for PBXNativeTarget "MBDoubleSliderExample" */ = {
326 | isa = XCConfigurationList;
327 | buildConfigurations = (
328 | F28C7A391E314C71009059C5 /* Debug */,
329 | F28C7A3A1E314C71009059C5 /* Release */,
330 | );
331 | defaultConfigurationIsVisible = 0;
332 | defaultConfigurationName = Release;
333 | };
334 | /* End XCConfigurationList section */
335 | };
336 | rootObject = F28C7A211E314C71009059C5 /* Project object */;
337 | }
338 |
--------------------------------------------------------------------------------
/MBDoubleSliderExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
--------------------------------------------------------------------------------