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

View File

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

View File

@ -29,15 +29,15 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
public let handle = SliderHandleView()
public var borderWidth: CGFloat = 4.0 {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var showsShadow: Bool = true {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
//MARK: - Initialization
@ -79,7 +79,11 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
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 {
@ -87,13 +91,13 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
let clampedPositionX: CGFloat = max(0, min(location.x, confiningTrackFrame.width))
let value = clampedPositionX / confiningTrackFrame.width
updateControl(to: value)
currentValue = value
sendActions(for: .valueChanged)
return true
}
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 {
@ -103,47 +107,6 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
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() {
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))
var hue: CGFloat = 0
var saturation: CGFloat = 0
@ -171,7 +175,7 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
moveHandle(to: value)
}
internal func updateTrackColor(to color: UIColor) {
private func updateTrackColor(to color: UIColor) {
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
@ -183,14 +187,14 @@ public class ChromaBrightnessSlider: UIControl, ChromaControlStylable {
currentValue = 1 - brightness
}
internal func updateTrackViewGradient(for color: UIColor) {
private func updateTrackViewGradient(for color: UIColor) {
CATransaction.begin()
CATransaction.setDisableActions(true)
sliderTrackView.gradientValues = (color, .black)
CATransaction.commit()
}
internal func moveHandle(to value: CGFloat) {
private func moveHandle(to value: CGFloat) {
let clampedValue = max(0, min(1, value))
let xPos = (clampedValue * confiningTrackFrame.width) + horizontalPadding
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?
@IBInspectable public var borderWidth: CGFloat = 6.0 {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
@IBInspectable public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
@IBInspectable public var showsShadow: Bool = true {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
/// A brightness slider attached via the `connect(_:)` method.
@ -41,7 +41,7 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
/// The size handles should be displayed at.
public var handleSize: CGSize = defaultHandleSize {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
/// An extension to handles' hitboxes in the +Y direction.
@ -156,7 +156,7 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
if let handle = currentHandle {
animateHandleScale(handle, shouldGrow: false)
}
sendActions(for: .editingDidEnd)
sendActions(for: .touchUpInside)
}
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
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? {
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.
public var color: UIColor = .black {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
/// 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.
public var accessoryViewEdgeInsets: UIEdgeInsets = .zero {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var borderWidth: CGFloat = 3.0 {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var showsShadow: Bool = true {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
// MARK: - Initialization

View File

@ -15,11 +15,11 @@ public class SliderHandleView: UIView {
}
public var borderWidth: CGFloat = 3.0 {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
public var borderColor: UIColor = .white {
didSet { layoutIfNeeded() }
didSet { layoutNow() }
}
override public init(frame: CGRect) {
@ -41,24 +41,18 @@ public class SliderHandleView: UIView {
}
// MARK: - Private
internal let handleLayer = CAShapeLayer()
private let handleLayer = CAShapeLayer()
internal struct CornerPoint {
let center: CGPoint
let startAngle: CGFloat
let endAngle: CGFloat
}
internal func commonInit() {
private func commonInit() {
layer.addSublayer(handleLayer)
updateHandleColor(to: handleColor)
}
internal func updateHandleColor(to color: UIColor) {
private func updateHandleColor(to color: UIColor) {
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 point2 = CGPoint(x: 0, 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())
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX,
y: subject.colorWheelView.bounds.midY))
let eventReceiver = FakeEventReceiver()
let eventReceiver = FakeEventReceiver(listensFor: .valueChanged)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .valueChanged)
let expectation = XCTestExpectation(description: "event fired")
eventReceiver.eventCaught = { event in
if event == .valueChanged {
expectation.fulfill()
} else {
XCTFail()
}
var eventDidTrigger = false
eventReceiver.eventCaught = {
eventDidTrigger = true
}
// When
let _ = subject.continueTracking(fakeTouch, with: nil)
// Then
wait(for: [expectation], timeout: 0.05)
XCTAssertTrue(eventDidTrigger)
}
func testEndTrackingSendsEditingDidEndAction() {
func testEndTrackingSendsTouchUpInsideAction() {
subject.colorWheelView.layoutIfNeeded()
// Given
setCurrentHandle(to: makeFakeHandle())
let fakeTouch = FakeUITouch(locationInParent: CGPoint(x: subject.colorWheelView.bounds.midX,
y: subject.colorWheelView.bounds.midY))
let eventReceiver = FakeEventReceiver()
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .editingDidEnd)
let eventReceiver = FakeEventReceiver(listensFor: .touchUpInside)
subject.addTarget(eventReceiver, action: #selector(FakeEventReceiver.catchEvent), for: .touchUpInside)
let expectation = XCTestExpectation(description: "event fired")
eventReceiver.eventCaught = { event in
if event == .valueChanged {
expectation.fulfill()
} else {
XCTFail()
}
var eventDidTrigger = false
eventReceiver.eventCaught = {
eventDidTrigger = true
}
// When
let _ = subject.endTracking(fakeTouch, with: nil)
// Then
wait(for: [expectation], timeout: 0.05)
XCTAssertTrue(eventDidTrigger)
}
}
@ -216,25 +206,3 @@ extension ChromaColorPickerTests {
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 expectedColor: UIColor = .purple
// When
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?()
}
}
}