Updated and improved tests. Fixed sync layout issue and updated UIControlEvents to events that make more sense. Added tests for brightness slider.

This commit is contained in:
Jonathan Cardasis 2019-09-08 19:16:58 -04:00
parent 5046808248
commit 16b9f6aa6c
15 changed files with 414 additions and 142 deletions

View File

@ -23,6 +23,9 @@
FC4387CB22625DA800F739F1 /* SliderHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CA22625DA800F739F1 /* SliderHandleView.swift */; }; FC4387CB22625DA800F739F1 /* SliderHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CA22625DA800F739F1 /* SliderHandleView.swift */; };
FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CC2262B82600F739F1 /* ChromaControlStylable.swift */; }; FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CC2262B82600F739F1 /* ChromaControlStylable.swift */; };
FC4387DE226974FD00F739F1 /* UIColor+Brightness.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */; }; FC4387DE226974FD00F739F1 /* UIColor+Brightness.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */; };
FC89B7E02325B07F00D007AB /* ChromaBrightnessSliderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC89B7DF2325B07F00D007AB /* ChromaBrightnessSliderTests.swift */; };
FC89B7E22325B8B900D007AB /* FakeTouch.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC89B7E12325B8B900D007AB /* FakeTouch.swift */; };
FC89B7E42325C24F00D007AB /* UIView+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC89B7E32325C24F00D007AB /* UIView+Utils.swift */; };
FCCA42A5226022A800BE2FF9 /* ColorWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */; }; FCCA42A5226022A800BE2FF9 /* ColorWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */; };
FCCA42A7226023F000BE2FF9 /* ChromaColorHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */; }; FCCA42A7226023F000BE2FF9 /* ChromaColorHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */; };
FCCA42AA2260329900BE2FF9 /* UIView+DropShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */; }; FCCA42AA2260329900BE2FF9 /* UIView+DropShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */; };
@ -30,7 +33,6 @@
FCE07C01227B4C8B00920217 /* UIColor+TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */; }; FCE07C01227B4C8B00920217 /* UIColor+TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */; };
FCE07C04227B4DB900920217 /* UIView+DropShadowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C03227B4DB900920217 /* UIView+DropShadowTests.swift */; }; FCE07C04227B4DB900920217 /* UIView+DropShadowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C03227B4DB900920217 /* UIView+DropShadowTests.swift */; };
FCE07C06227B525000920217 /* ChromaControlStylableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */; }; FCE07C06227B525000920217 /* ChromaControlStylableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */; };
FCE07C08228A0F5C00920217 /* SliderTrackViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C07228A0F5C00920217 /* SliderTrackViewTests.swift */; };
FCE07C0A228A0F6800920217 /* SliderHandleViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */; }; FCE07C0A228A0F6800920217 /* SliderHandleViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */; };
FCE07C0C228A0F7500920217 /* ChromaColorHandleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */; }; FCE07C0C228A0F7500920217 /* ChromaColorHandleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */; };
FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */; }; FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */; };
@ -93,6 +95,9 @@
FC4387CA22625DA800F739F1 /* SliderHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandleView.swift; sourceTree = "<group>"; }; FC4387CA22625DA800F739F1 /* SliderHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandleView.swift; sourceTree = "<group>"; };
FC4387CC2262B82600F739F1 /* ChromaControlStylable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaControlStylable.swift; sourceTree = "<group>"; }; FC4387CC2262B82600F739F1 /* ChromaControlStylable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaControlStylable.swift; sourceTree = "<group>"; };
FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Brightness.swift"; sourceTree = "<group>"; }; FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Brightness.swift"; sourceTree = "<group>"; };
FC89B7DF2325B07F00D007AB /* ChromaBrightnessSliderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaBrightnessSliderTests.swift; sourceTree = "<group>"; };
FC89B7E12325B8B900D007AB /* FakeTouch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeTouch.swift; sourceTree = "<group>"; };
FC89B7E32325C24F00D007AB /* UIView+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Utils.swift"; sourceTree = "<group>"; };
FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorWheelView.swift; sourceTree = "<group>"; }; FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorWheelView.swift; sourceTree = "<group>"; };
FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorHandle.swift; sourceTree = "<group>"; }; FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorHandle.swift; sourceTree = "<group>"; };
FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+DropShadow.swift"; sourceTree = "<group>"; }; FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+DropShadow.swift"; sourceTree = "<group>"; };
@ -101,7 +106,6 @@
FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+TestHelpers.swift"; sourceTree = "<group>"; }; FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+TestHelpers.swift"; sourceTree = "<group>"; };
FCE07C03227B4DB900920217 /* UIView+DropShadowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+DropShadowTests.swift"; sourceTree = "<group>"; }; FCE07C03227B4DB900920217 /* UIView+DropShadowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+DropShadowTests.swift"; sourceTree = "<group>"; };
FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaControlStylableTests.swift; sourceTree = "<group>"; }; FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaControlStylableTests.swift; sourceTree = "<group>"; };
FCE07C07228A0F5C00920217 /* SliderTrackViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderTrackViewTests.swift; sourceTree = "<group>"; };
FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandleViewTests.swift; sourceTree = "<group>"; }; FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandleViewTests.swift; sourceTree = "<group>"; };
FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorHandleTests.swift; sourceTree = "<group>"; }; FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorHandleTests.swift; sourceTree = "<group>"; };
FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorPicker.swift; sourceTree = "<group>"; }; FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorPicker.swift; sourceTree = "<group>"; };
@ -137,7 +141,7 @@
3503B8301F2689BC00750356 /* Source */ = { 3503B8301F2689BC00750356 /* Source */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FC4387C722625C5F00F739F1 /* Helpers */, FC4387C722625C5F00F739F1 /* Supporting Views */,
FCCA42A82260325F00BE2FF9 /* Extensions */, FCCA42A82260325F00BE2FF9 /* Extensions */,
3503B8311F2689BC00750356 /* ChromaColorPicker.h */, 3503B8311F2689BC00750356 /* ChromaColorPicker.h */,
3503B8321F2689BC00750356 /* Info.plist */, 3503B8321F2689BC00750356 /* Info.plist */,
@ -186,9 +190,10 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FCE07BFF227B4C7A00920217 /* TestHelpers */, FCE07BFF227B4C7A00920217 /* TestHelpers */,
FCE07C02227B4D9500920217 /* Helpers */, FCE07C02227B4D9500920217 /* Supporting Views */,
FCCA42AD226038BD00BE2FF9 /* Extensions */, FCCA42AD226038BD00BE2FF9 /* Extensions */,
FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */, FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */,
FC89B7DF2325B07F00D007AB /* ChromaBrightnessSliderTests.swift */,
FCCA42AB226038A400BE2FF9 /* ColorWheelViewTests.swift */, FCCA42AB226038A400BE2FF9 /* ColorWheelViewTests.swift */,
FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */, FCE07C05227B525000920217 /* ChromaControlStylableTests.swift */,
FC1BD8C12207D7B700817AF3 /* Info.plist */, FC1BD8C12207D7B700817AF3 /* Info.plist */,
@ -196,14 +201,14 @@
path = Tests; path = Tests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FC4387C722625C5F00F739F1 /* Helpers */ = { FC4387C722625C5F00F739F1 /* Supporting Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */, FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */,
FC4387CA22625DA800F739F1 /* SliderHandleView.swift */, FC4387CA22625DA800F739F1 /* SliderHandleView.swift */,
FC4387C822625C7000F739F1 /* SliderTrackView.swift */, FC4387C822625C7000F739F1 /* SliderTrackView.swift */,
); );
path = Helpers; path = "Supporting Views";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FCCA42A82260325F00BE2FF9 /* Extensions */ = { FCCA42A82260325F00BE2FF9 /* Extensions */ = {
@ -211,6 +216,7 @@
children = ( children = (
FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */, FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */,
FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */, FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */,
FC89B7E32325C24F00D007AB /* UIView+Utils.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -228,18 +234,18 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */, FCE07C00227B4C8B00920217 /* UIColor+TestHelpers.swift */,
FC89B7E12325B8B900D007AB /* FakeTouch.swift */,
); );
path = TestHelpers; path = TestHelpers;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FCE07C02227B4D9500920217 /* Helpers */ = { FCE07C02227B4D9500920217 /* Supporting Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */, FCE07C0B228A0F7500920217 /* ChromaColorHandleTests.swift */,
FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */, FCE07C09228A0F6800920217 /* SliderHandleViewTests.swift */,
FCE07C07228A0F5C00920217 /* SliderTrackViewTests.swift */,
); );
path = Helpers; path = "Supporting Views";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@ -395,6 +401,7 @@
files = ( files = (
FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */, FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */,
FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */, FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */,
FC89B7E42325C24F00D007AB /* UIView+Utils.swift in Sources */,
FCCA42AA2260329900BE2FF9 /* UIView+DropShadow.swift in Sources */, FCCA42AA2260329900BE2FF9 /* UIView+DropShadow.swift in Sources */,
FC4387DE226974FD00F739F1 /* UIColor+Brightness.swift in Sources */, FC4387DE226974FD00F739F1 /* UIColor+Brightness.swift in Sources */,
FC4387C62262556600F739F1 /* ChromaBrightnessSlider.swift in Sources */, FC4387C62262556600F739F1 /* ChromaBrightnessSlider.swift in Sources */,
@ -419,11 +426,12 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
FCE07C06227B525000920217 /* ChromaControlStylableTests.swift in Sources */, FCE07C06227B525000920217 /* ChromaControlStylableTests.swift in Sources */,
FC89B7E02325B07F00D007AB /* ChromaBrightnessSliderTests.swift in Sources */,
FC89B7E22325B8B900D007AB /* FakeTouch.swift in Sources */,
FCE07C0C228A0F7500920217 /* ChromaColorHandleTests.swift in Sources */, FCE07C0C228A0F7500920217 /* ChromaColorHandleTests.swift in Sources */,
FC1BD8C02207D7B700817AF3 /* ChromaColorPickerTests.swift in Sources */, FC1BD8C02207D7B700817AF3 /* ChromaColorPickerTests.swift in Sources */,
FCE07BFE227B4A8100920217 /* UIColor+BrightnessTests.swift in Sources */, FCE07BFE227B4A8100920217 /* UIColor+BrightnessTests.swift in Sources */,
FCE07C0A228A0F6800920217 /* SliderHandleViewTests.swift in Sources */, FCE07C0A228A0F6800920217 /* SliderHandleViewTests.swift in Sources */,
FCE07C08228A0F5C00920217 /* SliderTrackViewTests.swift in Sources */,
FC4387C422603AE900F739F1 /* ColorWheelViewTests.swift in Sources */, FC4387C422603AE900F739F1 /* ColorWheelViewTests.swift in Sources */,
FCE07C01227B4C8B00920217 /* UIColor+TestHelpers.swift in Sources */, FCE07C01227B4C8B00920217 /* UIColor+TestHelpers.swift in Sources */,
FCE07C04227B4DB900920217 /* UIView+DropShadowTests.swift in Sources */, FCE07C04227B4DB900920217 /* UIView+DropShadowTests.swift in Sources */,

View File

@ -78,17 +78,18 @@ addTarget(self, action: #selector(sliderDidValueChange(_:)), for: .valueChanged)
_ChromaColorPicker_ _ChromaColorPicker_
| Event | Description | | Event | Description |
| :-----------------:|:-------------| | :-----------------:|:-------------|
| `.touchDown` | Called when a handle is first grabbed. | | `.touchDown`(no) | Called when a handle is first grabbed. |
| `.touchUpInside` | Called when a handle is let go. | | `.touchUpInside`(no) | Called when a handle is let go. |
| `.valueChanged` | Called whenever the color has changed. | | `.valueChanged` | Called whenever the color has changed. |
| `.touchDragInside` | Called when a handle has moved via a drag action. | | `.touchDragInside`(no) | Called when a handle has moved via a drag action. |
| `.editingDidEnd` | Called when either a handle is let go or slider is let go. | | `.touchUpInside` | Called when a handle is released. |
_ChromaBrightnessSlider_ _ChromaBrightnessSlider_
| Event | Description | | Event | Description |
| :-----------------:|:-------------| | :-----------------:|:-------------|
| `.touchDown` | Called when a the slider is grabbed. |
| `.valueChanged` | Called whenever the slider is moved and the value has changed. | | `.valueChanged` | Called whenever the slider is moved and the value has changed. |
| `.editingDidEnd` | Called when the slider handle is released. | | `.touchUpInside` | Called when the slider handle is released. |
##### Example ##### Example
```Swift ```Swift

View File

@ -29,15 +29,15 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
public let handle = SliderHandleView() public let handle = SliderHandleView()
public var borderWidth: CGFloat = 4.0 { public var borderWidth: CGFloat = 4.0 {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var borderColor: UIColor = .white { public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var showsShadow: Bool = true { public var showsShadow: Bool = true {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
//MARK: - Initialization //MARK: - Initialization
@ -79,7 +79,11 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let location = touch.location(in: self) let location = touch.location(in: self)
return interactableBounds.contains(location) let shouldBeginTracking = interactableBounds.contains(location)
if shouldBeginTracking {
sendActions(for: .touchDown)
}
return shouldBeginTracking
} }
public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
@ -87,13 +91,13 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
let clampedPositionX: CGFloat = max(0, min(location.x, confiningTrackFrame.width)) let clampedPositionX: CGFloat = max(0, min(location.x, confiningTrackFrame.width))
let value = clampedPositionX / confiningTrackFrame.width let value = clampedPositionX / confiningTrackFrame.width
updateControl(to: value) currentValue = value
sendActions(for: .valueChanged) sendActions(for: .valueChanged)
return true return true
} }
public override func endTracking(_ touch: UITouch?, with event: UIEvent?) { public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
sendActions(for: .editingDidEnd) sendActions(for: .touchUpInside)
} }
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
@ -103,47 +107,6 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
return super.point(inside: point, with: event) return super.point(inside: point, with: event)
} }
// MARK: - Private
internal let sliderTrackView = SliderTrackView()
/// The amount of padding caused by visual stylings
internal var horizontalPadding: CGFloat {
return sliderTrackView.layer.cornerRadius / 2.0
}
internal var confiningTrackFrame: CGRect {
return sliderTrackView.frame.insetBy(dx: horizontalPadding, dy: 0)
}
internal var interactableBounds: CGRect {
let horizontalOffset = -(handle.bounds.width / 2) + horizontalPadding
return bounds.insetBy(dx: horizontalOffset, dy: 0)
}
internal func commonInit() {
backgroundColor = .clear
setupSliderTrackView()
setupSliderHandleView()
updateTrackColor(to: trackColor)
}
internal func setupSliderTrackView() {
sliderTrackView.isUserInteractionEnabled = false
sliderTrackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(sliderTrackView)
NSLayoutConstraint.activate([
sliderTrackView.leadingAnchor.constraint(equalTo: leadingAnchor),
sliderTrackView.trailingAnchor.constraint(equalTo: trailingAnchor),
sliderTrackView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.75),
sliderTrackView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
internal func setupSliderHandleView() {
handle.isUserInteractionEnabled = false
addSubview(handle)
}
internal func updateShadowIfNeeded() { internal func updateShadowIfNeeded() {
let views = [handle, sliderTrackView] let views = [handle, sliderTrackView]
@ -155,7 +118,48 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
} }
} }
internal func updateControl(to value: CGFloat) { // MARK: - Private
private let sliderTrackView = SliderTrackView()
/// The amount of padding caused by visual stylings
private var horizontalPadding: CGFloat {
return sliderTrackView.layer.cornerRadius / 2.0
}
private var confiningTrackFrame: CGRect {
return sliderTrackView.frame.insetBy(dx: horizontalPadding, dy: 0)
}
private var interactableBounds: CGRect {
let horizontalOffset = -(handle.bounds.width / 2) + horizontalPadding
return bounds.insetBy(dx: horizontalOffset, dy: 0)
}
private func commonInit() {
backgroundColor = .clear
setupSliderTrackView()
setupSliderHandleView()
updateTrackColor(to: trackColor)
}
private func setupSliderTrackView() {
sliderTrackView.isUserInteractionEnabled = false
sliderTrackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(sliderTrackView)
NSLayoutConstraint.activate([
sliderTrackView.leadingAnchor.constraint(equalTo: leadingAnchor),
sliderTrackView.trailingAnchor.constraint(equalTo: trailingAnchor),
sliderTrackView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.75),
sliderTrackView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
private func setupSliderHandleView() {
handle.isUserInteractionEnabled = false
addSubview(handle)
}
private func updateControl(to value: CGFloat) {
let brightness = 1 - max(0, min(1, value)) let brightness = 1 - max(0, min(1, value))
var hue: CGFloat = 0 var hue: CGFloat = 0
var saturation: CGFloat = 0 var saturation: CGFloat = 0
@ -171,7 +175,7 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
moveHandle(to: value) moveHandle(to: value)
} }
internal func updateTrackColor(to color: UIColor) { private func updateTrackColor(to color: UIColor) {
var hue: CGFloat = 0 var hue: CGFloat = 0
var saturation: CGFloat = 0 var saturation: CGFloat = 0
var brightness: CGFloat = 0 var brightness: CGFloat = 0
@ -183,14 +187,14 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
currentValue = 1 - brightness currentValue = 1 - brightness
} }
internal func updateTrackViewGradient(for color: UIColor) { private func updateTrackViewGradient(for color: UIColor) {
CATransaction.begin() CATransaction.begin()
CATransaction.setDisableActions(true) CATransaction.setDisableActions(true)
sliderTrackView.gradientValues = (color, .black) sliderTrackView.gradientValues = (color, .black)
CATransaction.commit() CATransaction.commit()
} }
internal func moveHandle(to value: CGFloat) { private func moveHandle(to value: CGFloat) {
let clampedValue = max(0, min(1, value)) let clampedValue = max(0, min(1, value))
let xPos = (clampedValue * confiningTrackFrame.width) + horizontalPadding let xPos = (clampedValue * confiningTrackFrame.width) + horizontalPadding
let size = CGSize(width: bounds.height * 1.15, height: bounds.height) let size = CGSize(width: bounds.height * 1.15, height: bounds.height)

View File

@ -21,15 +21,15 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
public weak var delegate: ChromaColorPickerDelegate? public weak var delegate: ChromaColorPickerDelegate?
@IBInspectable public var borderWidth: CGFloat = 6.0 { @IBInspectable public var borderWidth: CGFloat = 6.0 {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
@IBInspectable public var borderColor: UIColor = .white { @IBInspectable public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
@IBInspectable public var showsShadow: Bool = true { @IBInspectable public var showsShadow: Bool = true {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
/// A brightness slider attached via the `connect(_:)` method. /// A brightness slider attached via the `connect(_:)` method.
@ -41,7 +41,7 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
/// The size handles should be displayed at. /// The size handles should be displayed at.
public var handleSize: CGSize = defaultHandleSize { public var handleSize: CGSize = defaultHandleSize {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
/// An extension to handles' hitboxes in the +Y direction. /// An extension to handles' hitboxes in the +Y direction.
@ -156,7 +156,7 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
if let handle = currentHandle { if let handle = currentHandle {
animateHandleScale(handle, shouldGrow: false) animateHandleScale(handle, shouldGrow: false)
} }
sendActions(for: .editingDidEnd) sendActions(for: .touchUpInside)
} }
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View File

@ -60,6 +60,7 @@ public class ColorWheelView: UIView {
Returns the color on the wheel on a given point relative to the view. nil is returned if Returns the color on the wheel on a given point relative to the view. nil is returned if
the point does not exist within the bounds of the color wheel. the point does not exist within the bounds of the color wheel.
*/ */
// TODO: replace this function with a mathmatically based one in ChromaColorPicker
public func pixelColor(at point: CGPoint) -> UIColor? { public func pixelColor(at point: CGPoint) -> UIColor? {
guard pointIsInColorWheel(point) else { return nil } guard pointIsInColorWheel(point) else { return nil }

View File

@ -0,0 +1,18 @@
//
// UIView+Utils.swift
// ChromaColorPicker
//
// Created by Jon Cardasis on 9/8/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import UIKit
internal extension UIView {
/// Forces the view to layout synchronously and immediately
func layoutNow() {
setNeedsLayout()
layoutIfNeeded()
}
}

View File

@ -12,7 +12,7 @@ public class ChromaColorHandle: UIView, ChromaControlStylable {
/// Current selected color of the handle. /// Current selected color of the handle.
public var color: UIColor = .black { public var color: UIColor = .black {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
/// An image to display in the handle. Updates `accessoryView` to be a UIImageView. /// An image to display in the handle. Updates `accessoryView` to be a UIImageView.
@ -36,19 +36,19 @@ public class ChromaColorHandle: UIView, ChromaControlStylable {
/// The amount an accessory view's frame should be inset by. /// The amount an accessory view's frame should be inset by.
public var accessoryViewEdgeInsets: UIEdgeInsets = .zero { public var accessoryViewEdgeInsets: UIEdgeInsets = .zero {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var borderWidth: CGFloat = 3.0 { public var borderWidth: CGFloat = 3.0 {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var borderColor: UIColor = .white { public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var showsShadow: Bool = true { public var showsShadow: Bool = true {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
// MARK: - Initialization // MARK: - Initialization

View File

@ -15,11 +15,11 @@ public class SliderHandleView: UIView {
} }
public var borderWidth: CGFloat = 3.0 { public var borderWidth: CGFloat = 3.0 {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
public var borderColor: UIColor = .white { public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() } didSet { layoutNow() }
} }
override public init(frame: CGRect) { override public init(frame: CGRect) {
@ -41,24 +41,18 @@ public class SliderHandleView: UIView {
} }
// MARK: - Private // MARK: - Private
internal let handleLayer = CAShapeLayer() private let handleLayer = CAShapeLayer()
internal struct CornerPoint { private func commonInit() {
let center: CGPoint
let startAngle: CGFloat
let endAngle: CGFloat
}
internal func commonInit() {
layer.addSublayer(handleLayer) layer.addSublayer(handleLayer)
updateHandleColor(to: handleColor) updateHandleColor(to: handleColor)
} }
internal func updateHandleColor(to color: UIColor) { private func updateHandleColor(to color: UIColor) {
handleLayer.fillColor = color.cgColor handleLayer.fillColor = color.cgColor
} }
internal func makeRoundedTrianglePath(width: CGFloat, height: CGFloat, radius: CGFloat) -> CGPath { private func makeRoundedTrianglePath(width: CGFloat, height: CGFloat, radius: CGFloat) -> CGPath {
let point1 = CGPoint(x: -width / 2, y: height / 2) let point1 = CGPoint(x: -width / 2, y: height / 2)
let point2 = CGPoint(x: 0, y: -height / 2) let point2 = CGPoint(x: 0, y: -height / 2)
let point3 = CGPoint(x: width / 2, y: height / 2) let point3 = CGPoint(x: width / 2, y: height / 2)

View File

@ -0,0 +1,251 @@
//
// ChromaBrightnessSliderTests.swift
// ChromaColorPickerTests
//
// Created by Jon Cardasis on 9/8/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import XCTest
@testable import ChromaColorPicker
class ChromaBrightnessSliderTests: XCTestCase {
// MARK: - Properties
func testCurrentColorShouldReturnBlueWhenTrackColorIsBlueAndCurrentValueIsZero() {
// Given
let subject = ChromaBrightnessSlider()
let expectedColor: UIColor = .blue
// When
subject.trackColor = .blue
subject.currentValue = 0.0
// Then
XCTAssertEqual(subject.currentColor, expectedColor)
}
func testCurrentColorShouldReturnBlackWhenTrackColorIsAnyAndCurrentValueIsOne() {
// Given
let subject = ChromaBrightnessSlider()
let expectedColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
// When
subject.trackColor = .blue
subject.currentValue = 1.0
// Then
XCTAssertEqual(subject.currentColor, expectedColor)
}
func testBorderWidthShouldUpdateSliderTrack() {
// Given
let subject = ChromaBrightnessSlider()
let sliderTrack = subject.test_sliderTrackView
let expectedBorderWidth: CGFloat = 11
// When
subject.borderWidth = expectedBorderWidth
// Then
XCTAssertEqual(sliderTrack.layer.borderWidth, expectedBorderWidth)
}
func testBorderColorShouldUpdateSliderTrack() {
// Given
let subject = ChromaBrightnessSlider()
let sliderTrack = subject.test_sliderTrackView
let expectedBorderColor: UIColor = .green
// When
subject.borderColor = expectedBorderColor
// Then
XCTAssertEqual(sliderTrack.layer.borderColor!, expectedBorderColor.cgColor)
}
// MARK: - Layout
func testShadowsAreRenderedOnBothHandleAndTrackView() {
// Given
let subject = ChromaBrightnessSlider()
let sliderTrack = subject.test_sliderTrackView
// When
subject.showsShadow = true
// Then
XCTAssertGreaterThan(sliderTrack.layer.shadowOpacity, 0)
XCTAssertGreaterThan(subject.handle.layer.shadowOpacity, 0)
}
func testShadowsAreRemovedFromHandleAndTrackView() {
// Given
let subject = ChromaBrightnessSlider()
let sliderTrack = subject.test_sliderTrackView
// When
subject.showsShadow = false
// Then
XCTAssertEqual(sliderTrack.layer.shadowOpacity, 0)
XCTAssertEqual(subject.handle.layer.shadowOpacity, 0)
}
// MARK: - Convenience Functions
func testConnectCallsConnectFunctionOfColorPicker() {
// Given
let subject = ChromaBrightnessSlider()
let colorPicker = FakeColorPicker()
// When
subject.connect(to: colorPicker)
// Then
XCTAssertTrue(colorPicker.didCallConnect)
}
func testValueShouldReturn1WhenBrightnessIs0() {
// Given
let subject = ChromaBrightnessSlider()
// When
let value = subject.value(brightness: 0)
// Then
XCTAssertEqual(value, 1, accuracy: 0.0001)
}
func testValueShouldReturn0WhenBrightnessIs1() {
// Given
let subject = ChromaBrightnessSlider()
// When
let value = subject.value(brightness: 1)
// Then
XCTAssertEqual(value, 0, accuracy: 0.0001)
}
func testValueShouldReturn0WhenBrightnessIsLargerThan1() {
// Given
let subject = ChromaBrightnessSlider()
// When
let value = subject.value(brightness: 100)
// Then
XCTAssertEqual(value, 0, accuracy: 0.0001)
}
func testValueShouldReturn1WhenBrightnessIsLessThan0() {
// Given
let subject = ChromaBrightnessSlider()
// When
let value = subject.value(brightness: -100)
// Then
XCTAssertEqual(value, 1, accuracy: 0.0001)
}
func testValueShouldReturn0_25WhenBrightnessIs0_75() {
// Given
let subject = ChromaBrightnessSlider()
// When
let value = subject.value(brightness: 0.75)
// Then
XCTAssertEqual(value, 0.25, accuracy: 0.0001)
}
// MARK: - Control
func testBeginTrackingSendsTouchDownActionWhenTouchIsInBounds() {
// Given
let subject = ChromaBrightnessSlider(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
subject.layoutIfNeeded()
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.bounds.midX,
y: subject.bounds.midY))
let eventReceiver = FakeEventReceiver(listensFor: .touchDown)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent(_:)), for: .touchDown)
var eventDidTrigger = false
eventReceiver.eventCaught = {
eventDidTrigger = true
}
// When
let shouldTrack = subject.beginTracking(fakeTouch, with: nil)
// Then
XCTAssertTrue(eventDidTrigger)
XCTAssertTrue(shouldTrack)
}
func testBeginTrackingDoesNotSendTouchDownActionWhenTouchIsOutOfBounds() {
// Given
let subject = ChromaBrightnessSlider(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
subject.layoutIfNeeded()
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: -100, y: subject.bounds.midY))
let eventReceiver = FakeEventReceiver(listensFor: .touchDown)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent(_:)), for: .touchDown)
var eventDidTrigger = false
eventReceiver.eventCaught = {
eventDidTrigger = true
}
// When
let shouldTrack = subject.beginTracking(fakeTouch, with: nil)
// Then
XCTAssertFalse(eventDidTrigger)
XCTAssertFalse(shouldTrack)
}
func testContinueTrackingSendsValueChangedActionAndHasClampedValue() {
// Given
let subject = ChromaBrightnessSlider(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
subject.layoutIfNeeded()
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.bounds.width + 100, y: subject.bounds.midY))
let eventReceiver = FakeEventReceiver(listensFor: .valueChanged)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent(_:)), for: .valueChanged)
var eventDidTrigger = false
var value: CGFloat?
eventReceiver.eventCaught = {
eventDidTrigger = true
value = subject.currentValue
}
// When
let _ = subject.continueTracking(fakeTouch, with: nil)
// Then
XCTAssertTrue(eventDidTrigger)
XCTAssertEqual(value, 1.0, "ChromaBrightnessSlider did not clamp value when touch is out of bounds")
}
func testEndTrackingSendsTouchUpInsideAction() {
}
}
fileprivate extension ChromaBrightnessSlider {
var test_sliderTrackView: SliderTrackView {
return subviews.first(where: { $0 is SliderTrackView }) as! SliderTrackView
}
}
fileprivate class FakeColorPicker: ChromaColorPicker {
var didCallConnect: Bool = false
override func connect(_ slider: ChromaBrightnessSlider) {
didCallConnect = true
}
}

View File

@ -153,52 +153,42 @@ class ChromaColorPickerTests: XCTestCase {
setCurrentHandle(to: makeFakeHandle()) setCurrentHandle(to: makeFakeHandle())
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX, let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX,
y: subject.colorWheelView.bounds.midY)) y: subject.colorWheelView.bounds.midY))
let eventReceiver = FakeEventReceiver() let eventReceiver = FakeEventReceiver(listensFor: .valueChanged)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .valueChanged) subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .valueChanged)
let expectation = XCTestExpectation(description: "event fired") var eventDidTrigger = false
eventReceiver.eventCaught = {
eventReceiver.eventCaught = { event in eventDidTrigger = true
if event == .valueChanged {
expectation.fulfill()
} else {
XCTFail()
}
} }
// When // When
let _ = subject.continueTracking(fakeTouch, with: nil) let _ = subject.continueTracking(fakeTouch, with: nil)
// Then // Then
wait(for: [expectation], timeout: 0.05) XCTAssertTrue(eventDidTrigger)
} }
func testEndTrackingSendsEditingDidEndAction() { func testEndTrackingSendsTouchUpInsideAction() {
subject.colorWheelView.layoutIfNeeded() subject.colorWheelView.layoutIfNeeded()
// Given // Given
setCurrentHandle(to: makeFakeHandle()) setCurrentHandle(to: makeFakeHandle())
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX, let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX,
y: subject.colorWheelView.bounds.midY)) y: subject.colorWheelView.bounds.midY))
let eventReceiver = FakeEventReceiver() let eventReceiver = FakeEventReceiver(listensFor: .touchUpInside)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .editingDidEnd) subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .touchUpInside)
let expectation = XCTestExpectation(description: "event fired") var eventDidTrigger = false
eventReceiver.eventCaught = {
eventReceiver.eventCaught = { event in eventDidTrigger = true
if event == .valueChanged {
expectation.fulfill()
} else {
XCTFail()
}
} }
// When // When
let _ = subject.endTracking(fakeTouch, with: nil) let _ = subject.endTracking(fakeTouch, with: nil)
// Then // Then
wait(for: [expectation], timeout: 0.05) XCTAssertTrue(eventDidTrigger)
} }
} }
@ -216,25 +206,3 @@ extension ChromaColorPickerTests {
let _ = subject.beginTracking(fakeTouch, with: nil) let _ = subject.beginTracking(fakeTouch, with: nil)
} }
} }
private class FakeUITouch: UITouch {
let locationInParent: CGPoint
init(locationInParent: CGPoint) {
self.locationInParent = locationInParent
}
override func location(in view: UIView?) -> CGPoint {
return locationInParent
}
}
private class FakeEventReceiver: NSObject {
var eventCaught: ((UIControl.Event) -> ())?
@objc func catchEvent() {
eventCaught?(.valueChanged)
}
}

View File

@ -1,9 +0,0 @@
//
// SliderTrackViewTests.swift
// ChromaColorPickerTests
//
// Created by Jon Cardasis on 5/13/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import Foundation

View File

@ -30,7 +30,6 @@ class SliderHandleViewTests: XCTestCase {
let layer = handleLayer(for: subject) let layer = handleLayer(for: subject)
let expectedColor: UIColor = .purple let expectedColor: UIColor = .purple
// When // When
subject.borderColor = expectedColor subject.borderColor = expectedColor

View File

@ -0,0 +1,37 @@
//
// FakeTouch.swift
// ChromaColorPickerTests
//
// Created by Jon Cardasis on 9/8/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import UIKit
class FakeUITouch: UITouch {
let locationInParent: CGPoint
init(locationInParent: CGPoint) {
self.locationInParent = locationInParent
}
override func location(in view: UIView?) -> CGPoint {
return locationInParent
}
}
class FakeEventReceiver: NSObject {
var eventCaught: (() -> ())?
let event: UIControl.Event
init(listensFor event: UIControl.Event) {
self.event = event
}
@objc func catchEvent(_ sender: UIControl) {
if sender.allControlEvents.contains(event) {
eventCaught?()
}
}
}