Resolved issue with handleSize. Added additional tests. Example homeHandle now changes tint based on lightness of selected color

This commit is contained in:
Jon Cardasis 2019-11-08 16:17:06 -05:00
parent 7191d098d0
commit 8bf9b34f9b
6 changed files with 171 additions and 5 deletions

View File

@ -15,6 +15,8 @@
35C376D61D5CF5300069D7A1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 35C376D41D5CF5300069D7A1 /* Main.storyboard */; };
35C376D81D5CF5300069D7A1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 35C376D71D5CF5300069D7A1 /* Assets.xcassets */; };
35C376DB1D5CF5300069D7A1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 35C376D91D5CF5300069D7A1 /* LaunchScreen.storyboard */; };
C83A935D23760C1600564E01 /* UIColor+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83A935C23760C1600564E01 /* UIColor+Utils.swift */; };
C83A936023760C5900564E01 /* UIColor+UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83A935E23760C3800564E01 /* UIColor+UtilsTests.swift */; };
FC1BD8C02207D7B700817AF3 /* ChromaColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */; };
FC1BD8C22207D7B700817AF3 /* ChromaColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3503B82F1F2689BC00750356 /* ChromaColorPicker.framework */; };
FC4387C422603AE900F739F1 /* ColorWheelViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42AB226038A400BE2FF9 /* ColorWheelViewTests.swift */; };
@ -87,6 +89,8 @@
35C376D71D5CF5300069D7A1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
35C376DA1D5CF5300069D7A1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
35C376DC1D5CF5300069D7A1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C83A935C23760C1600564E01 /* UIColor+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Utils.swift"; sourceTree = "<group>"; };
C83A935E23760C3800564E01 /* UIColor+UtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+UtilsTests.swift"; sourceTree = "<group>"; };
FC1BD8BD2207D7B700817AF3 /* ChromaColorPickerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChromaColorPickerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorPickerTests.swift; sourceTree = "<group>"; };
FC1BD8C12207D7B700817AF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -215,6 +219,7 @@
isa = PBXGroup;
children = (
FC4387DC226972EB00F739F1 /* UIColor+Brightness.swift */,
C83A935C23760C1600564E01 /* UIColor+Utils.swift */,
FCCA42A92260329900BE2FF9 /* UIView+DropShadow.swift */,
FC89B7E32325C24F00D007AB /* UIView+Utils.swift */,
);
@ -225,6 +230,7 @@
isa = PBXGroup;
children = (
FCE07BFD227B4A8100920217 /* UIColor+BrightnessTests.swift */,
C83A935E23760C3800564E01 /* UIColor+UtilsTests.swift */,
FCE07C03227B4DB900920217 /* UIView+DropShadowTests.swift */,
);
path = Extensions;
@ -405,6 +411,7 @@
FCCA42AA2260329900BE2FF9 /* UIView+DropShadow.swift in Sources */,
FC4387DE226974FD00F739F1 /* UIColor+Brightness.swift in Sources */,
FC4387C62262556600F739F1 /* ChromaBrightnessSlider.swift in Sources */,
C83A935D23760C1600564E01 /* UIColor+Utils.swift in Sources */,
FCCA42A7226023F000BE2FF9 /* ChromaColorHandle.swift in Sources */,
FCCA42A5226022A800BE2FF9 /* ColorWheelView.swift in Sources */,
FC4387C922625C7000F739F1 /* SliderTrackView.swift in Sources */,
@ -426,6 +433,7 @@
buildActionMask = 2147483647;
files = (
FCE07C06227B525000920217 /* ChromaControlStylableTests.swift in Sources */,
C83A936023760C5900564E01 /* UIColor+UtilsTests.swift in Sources */,
FC89B7E02325B07F00D007AB /* ChromaBrightnessSliderTests.swift in Sources */,
FC89B7E22325B8B900D007AB /* FakeTouch.swift in Sources */,
FCE07C0C228A0F7500920217 /* ChromaColorHandleTests.swift in Sources */,

View File

@ -22,6 +22,13 @@ class ViewController: UIViewController {
setupColorPickerHandles()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - Private
private var homeHandle: ChromaColorHandle! // reference to home handle
private func setupColorPicker() {
colorPicker.delegate = self
colorPicker.translatesAutoresizingMaskIntoConstraints = false
@ -57,14 +64,24 @@ class ViewController: UIViewController {
}
private func setupColorPickerHandles() {
// (Optional) Assign a custom handle size - all handles appear as the same size
// colorPicker.handleSize = CGSize(width: 48, height: 60)
// 1. Add handle and then customize
addHomeHandle()
// 2. Add a handle via a color
let peachColor = UIColor(red: 1, green: 203 / 255, blue: 164 / 255, alpha: 1)
colorPicker.addHandle(at: peachColor)
// 3. Create a custom handle and add to picker
let customHandle = ChromaColorHandle()
customHandle.color = UIColor.purple
colorPicker.addHandle(customHandle)
}
private func addHomeHandle() {
let homeHandle = colorPicker.addHandle(at: .blue)
homeHandle = colorPicker.addHandle(at: .blue)
// Setup custom handle view with insets
let customImageView = UIImageView(image: #imageLiteral(resourceName: "home").withRenderingMode(.alwaysTemplate))
@ -78,8 +95,18 @@ class ViewController: UIViewController {
extension ViewController: ChromaColorPickerDelegate {
func colorPickerHandleDidChange(_ colorPicker: ChromaColorPicker, handle: ChromaColorHandle, to color: UIColor) {
colorDisplayView.backgroundColor = color
// Here I can detect when the color is too bright to show a white icon
// on the handle and change its tintColor.
if handle === homeHandle, let imageView = homeHandle.accessoryView as? UIImageView {
let colorIsBright = color.isLight
print(color.lightness)
UIView.animate(withDuration: 0.2, animations: {
imageView.tintColor = colorIsBright ? .black : .white
}, completion: nil)
}
}
}

View File

@ -1,5 +1,5 @@
//
// ChromaColorPicker2.swift
// ChromaColorPicker.swift
// ChromaColorPicker
//
// Created by Jon Cardasis on 3/10/19.
@ -7,7 +7,6 @@
//
import UIKit
import Accelerate
public protocol ChromaColorPickerDelegate: class {
/// When a handle's value has changed.
@ -41,7 +40,7 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
/// The size handles should be displayed at.
public var handleSize: CGSize = defaultHandleSize {
didSet { layoutNow() }
didSet { setNeedsLayout() }
}
/// An extension to handles' hitboxes in the +Y direction.

View File

@ -0,0 +1,28 @@
//
// UIColor+Utils.swift
// ChromaColorPicker
//
// Created by Jonathan Cardasis on 11/8/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import UIKit
public extension UIColor {
/// The value of lightness a color has. Value between [0.0, 1.0]
/// Based on YIQ color space for constrast (https://www.w3.org/WAI/ER/WD-AERT/#color-contrast)
var lightness: CGFloat {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: nil)
return ((red * 299) + (green * 587) + (blue * 114)) / 1000
}
/// Whether or not the color is considered 'light' in terms of contrast.
var isLight: Bool {
return lightness >= 0.5
}
}

View File

@ -190,6 +190,49 @@ class ChromaColorPickerTests: XCTestCase {
// Then
XCTAssertTrue(eventDidTrigger)
}
func testHitTestShouldReturnSelfWhenTouchIsWithinSelf() {
subject.colorWheelView.layoutIfNeeded()
// Given
let touchLocation = subject.center
// When
let hitView = subject.hitTest(touchLocation, with: UIEvent())
// Then
XCTAssertEqual(hitView, subject)
}
func testHitTestShouldReturnSelfWhenTouchIsWithinSelfPlusHandleRadius() {
subject.colorWheelView.layoutIfNeeded()
// Given
let touchLocation = CGPoint(x: -subject.handleSize.width / 2.0, y: subject.center.y)
// When
let hitView = subject.hitTest(touchLocation, with: UIEvent())
// Then
XCTAssertEqual(hitView, subject)
}
func testHitTestShouldReturnNilWhenTouchIsOutsideHitbox() {
subject.colorWheelView.layoutIfNeeded()
// Given
let extendedSize: CGFloat = 12
subject.handleSize = CGSize(width: extendedSize, height: extendedSize)
let fakeHandle = makeFakeHandle()
let touchLocation = CGPoint(x: -extendedSize - 1, y: subject.center.y)
setCurrentHandle(to: fakeHandle)
// When
let hitView = subject.hitTest(touchLocation, with: UIEvent())
// Then
XCTAssertEqual(hitView, nil)
}
}

View File

@ -0,0 +1,61 @@
//
// UIColor+UtilsTests.swift
// ChromaColorPicker
//
// Created by Jonathan Cardasis on 11/8/19.
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
//
import XCTest
@testable import ChromaColorPicker
class UIColor_UtilsTests: XCTestCase {
func testLightnessReturns1ForWhite() {
XCTAssertEqual(UIColor.white.lightness, 1.0, accuracy: 0.001)
}
func testLightnessReturns0ForBlack() {
XCTAssertEqual(UIColor.black.lightness, 0.0, accuracy: 0.001)
}
func testLightnessReturnsCorrectLightnessForYellow() {
XCTAssertEqual(UIColor.yellow.lightness, 0.886, accuracy: 0.001)
}
func testLightnessReturnsCorrectLightnessForBlue() {
XCTAssertEqual(UIColor.blue.lightness, 0.114, accuracy: 0.001)
}
func testBlueIsNotLight() {
XCTAssertFalse(UIColor.blue.isLight)
}
func testRedIsNotLight() {
XCTAssertFalse(UIColor.red.isLight)
}
func testPurpleIsNotLight() {
XCTAssertFalse(UIColor.purple.isLight)
}
func tesBlackIsNotLight() {
XCTAssertFalse(UIColor.black.isLight)
}
func testWhiteIsLight() {
XCTAssertTrue(UIColor.white.isLight)
}
func testGreenIsLight() {
XCTAssertTrue(UIColor.green.isLight)
}
func testOrangeIsLight() {
XCTAssertTrue(UIColor.orange.isLight)
}
func testYellowIsLight() {
XCTAssertTrue(UIColor.yellow.isLight)
}
}