mirror of
https://github.com/joncardasis/ChromaColorPicker.git
synced 2024-10-26 09:00:39 +03:00
Added brightness slider and slider handle views. Created gradient backed slider track view. Refactored shadow stylings.
This commit is contained in:
parent
927a4d8834
commit
1a1179fbe6
@ -18,6 +18,10 @@
|
|||||||
FC1BD8C02207D7B700817AF3 /* ChromaColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */; };
|
FC1BD8C02207D7B700817AF3 /* ChromaColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */; };
|
||||||
FC1BD8C22207D7B700817AF3 /* ChromaColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3503B82F1F2689BC00750356 /* ChromaColorPicker.framework */; };
|
FC1BD8C22207D7B700817AF3 /* ChromaColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3503B82F1F2689BC00750356 /* ChromaColorPicker.framework */; };
|
||||||
FC4387C422603AE900F739F1 /* ColorWheelViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42AB226038A400BE2FF9 /* ColorWheelViewTests.swift */; };
|
FC4387C422603AE900F739F1 /* ColorWheelViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42AB226038A400BE2FF9 /* ColorWheelViewTests.swift */; };
|
||||||
|
FC4387C62262556600F739F1 /* BrightnessSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387C52262556600F739F1 /* BrightnessSlider.swift */; };
|
||||||
|
FC4387C922625C7000F739F1 /* SliderTrackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387C822625C7000F739F1 /* SliderTrackView.swift */; };
|
||||||
|
FC4387CB22625DA800F739F1 /* SliderHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CA22625DA800F739F1 /* SliderHandleView.swift */; };
|
||||||
|
FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4387CC2262B82600F739F1 /* ChromaControlStylable.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 /* UIKit+DropShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A92260329900BE2FF9 /* UIKit+DropShadow.swift */; };
|
FCCA42AA2260329900BE2FF9 /* UIKit+DropShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCCA42A92260329900BE2FF9 /* UIKit+DropShadow.swift */; };
|
||||||
@ -75,6 +79,10 @@
|
|||||||
FC1BD8CB2207FCE100817AF3 /* UIColor+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Utilities.swift"; sourceTree = "<group>"; };
|
FC1BD8CB2207FCE100817AF3 /* UIColor+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Utilities.swift"; sourceTree = "<group>"; };
|
||||||
FC1BD8CC2207FCE100817AF3 /* ChromaAddButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaAddButton.swift; sourceTree = "<group>"; };
|
FC1BD8CC2207FCE100817AF3 /* ChromaAddButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaAddButton.swift; sourceTree = "<group>"; };
|
||||||
FC1BD8CD2207FCE100817AF3 /* ColorModeToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorModeToggleButton.swift; sourceTree = "<group>"; };
|
FC1BD8CD2207FCE100817AF3 /* ColorModeToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorModeToggleButton.swift; sourceTree = "<group>"; };
|
||||||
|
FC4387C52262556600F739F1 /* BrightnessSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessSlider.swift; sourceTree = "<group>"; };
|
||||||
|
FC4387C822625C7000F739F1 /* SliderTrackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderTrackView.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>"; };
|
||||||
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 /* UIKit+DropShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+DropShadow.swift"; sourceTree = "<group>"; };
|
FCCA42A92260329900BE2FF9 /* UIKit+DropShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+DropShadow.swift"; sourceTree = "<group>"; };
|
||||||
@ -112,6 +120,7 @@
|
|||||||
3503B8301F2689BC00750356 /* Source */ = {
|
3503B8301F2689BC00750356 /* Source */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
FC4387C722625C5F00F739F1 /* Helpers */,
|
||||||
FCCA42A82260325F00BE2FF9 /* Extensions */,
|
FCCA42A82260325F00BE2FF9 /* Extensions */,
|
||||||
FCCA427822601F8800BE2FF9 /* Legacy */,
|
FCCA427822601F8800BE2FF9 /* Legacy */,
|
||||||
3503B8311F2689BC00750356 /* ChromaColorPicker.h */,
|
3503B8311F2689BC00750356 /* ChromaColorPicker.h */,
|
||||||
@ -119,6 +128,8 @@
|
|||||||
FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */,
|
FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */,
|
||||||
FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */,
|
FCCA42A6226023F000BE2FF9 /* ChromaColorHandle.swift */,
|
||||||
FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */,
|
FCCA42A4226022A800BE2FF9 /* ColorWheelView.swift */,
|
||||||
|
FC4387C52262556600F739F1 /* BrightnessSlider.swift */,
|
||||||
|
FC4387CC2262B82600F739F1 /* ChromaControlStylable.swift */,
|
||||||
);
|
);
|
||||||
path = Source;
|
path = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -167,6 +178,15 @@
|
|||||||
path = Tests;
|
path = Tests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
FC4387C722625C5F00F739F1 /* Helpers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FC4387C822625C7000F739F1 /* SliderTrackView.swift */,
|
||||||
|
FC4387CA22625DA800F739F1 /* SliderHandleView.swift */,
|
||||||
|
);
|
||||||
|
path = Helpers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
FCCA427822601F8800BE2FF9 /* Legacy */ = {
|
FCCA427822601F8800BE2FF9 /* Legacy */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -344,9 +364,13 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */,
|
FCEA4E272235AAA200C0A1B6 /* ChromaColorPicker.swift in Sources */,
|
||||||
|
FC4387CD2262B82600F739F1 /* ChromaControlStylable.swift in Sources */,
|
||||||
FCCA42AA2260329900BE2FF9 /* UIKit+DropShadow.swift in Sources */,
|
FCCA42AA2260329900BE2FF9 /* UIKit+DropShadow.swift in Sources */,
|
||||||
|
FC4387C62262556600F739F1 /* BrightnessSlider.swift in Sources */,
|
||||||
FCCA42A7226023F000BE2FF9 /* ChromaColorHandle.swift in Sources */,
|
FCCA42A7226023F000BE2FF9 /* ChromaColorHandle.swift in Sources */,
|
||||||
FCCA42A5226022A800BE2FF9 /* ColorWheelView.swift in Sources */,
|
FCCA42A5226022A800BE2FF9 /* ColorWheelView.swift in Sources */,
|
||||||
|
FC4387C922625C7000F739F1 /* SliderTrackView.swift in Sources */,
|
||||||
|
FC4387CB22625DA800F739F1 /* SliderHandleView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -14,10 +14,15 @@ class ViewController: UIViewController {
|
|||||||
|
|
||||||
let colorPicker = ChromaColorPicker()
|
let colorPicker = ChromaColorPicker()
|
||||||
|
|
||||||
|
let temp = BrightnessSlider()
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
temp.frame = CGRect(x: 30, y: 70, width: 320, height: 32)
|
||||||
|
view.addSubview(temp)
|
||||||
|
|
||||||
|
|
||||||
colorPicker.translatesAutoresizingMaskIntoConstraints = false
|
colorPicker.translatesAutoresizingMaskIntoConstraints = false
|
||||||
view.addSubview(colorPicker)
|
view.addSubview(colorPicker)
|
||||||
|
166
Source/BrightnessSlider.swift
Normal file
166
Source/BrightnessSlider.swift
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
//
|
||||||
|
// BrightnessSlider.swift
|
||||||
|
// ChromaColorPicker
|
||||||
|
//
|
||||||
|
// Created by Jon Cardasis on 4/13/19.
|
||||||
|
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public class BrightnessSlider: UIControl, ChromaControlStylable {
|
||||||
|
|
||||||
|
/// The value of the slider between [0.0, 1.0].
|
||||||
|
public var currentValue: CGFloat = 0.0 {
|
||||||
|
didSet { updateControl(to: currentValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value of the color the handle is currently displaying.
|
||||||
|
public var currentColor: UIColor {
|
||||||
|
return sliderHandleView.handleColor
|
||||||
|
}
|
||||||
|
|
||||||
|
public var borderWidth: CGFloat = 4.0 {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
public var borderColor: UIColor = .white {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
public var showsShadow: Bool = true {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Initialization
|
||||||
|
|
||||||
|
public override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
sliderTrackView.layer.cornerRadius = sliderTrackView.bounds.height / 2.0
|
||||||
|
sliderTrackView.layer.borderColor = borderColor.cgColor
|
||||||
|
sliderTrackView.layer.borderWidth = borderWidth
|
||||||
|
|
||||||
|
moveHandle(to: currentValue)
|
||||||
|
updateShadowIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the tracking color of the slider and updates the gradient view.
|
||||||
|
public func updateTrackingColor(to color: UIColor, value: CGFloat) {
|
||||||
|
updateTrackViewGradient(for: color)
|
||||||
|
currentValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Control
|
||||||
|
|
||||||
|
public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
|
let location = touch.location(in: self)
|
||||||
|
return confiningTrackFrame.contains(location) || sliderHandleView.frame.contains(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
|
let location = touch.location(in: self)
|
||||||
|
let clampedPositionX: CGFloat = max(0, min(location.x, confiningTrackFrame.width))
|
||||||
|
let value = clampedPositionX / confiningTrackFrame.width
|
||||||
|
|
||||||
|
updateControl(to: value)
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||||
|
sendActions(for: .editingDidEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
internal let sliderTrackView = SliderTrackView()
|
||||||
|
internal let sliderHandleView = SliderHandleView()
|
||||||
|
|
||||||
|
/// 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 func commonInit() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
setupSliderTrackView()
|
||||||
|
setupSliderHandleView()
|
||||||
|
updateControl(to: currentValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
sliderHandleView.isUserInteractionEnabled = false
|
||||||
|
addSubview(sliderHandleView)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func updateShadowIfNeeded() {
|
||||||
|
let views = [sliderHandleView, sliderTrackView]
|
||||||
|
|
||||||
|
if showsShadow {
|
||||||
|
let shadowProps = shadowProperties(forHeight: bounds.height)
|
||||||
|
views.forEach { $0.applyDropShadow(shadowProps) }
|
||||||
|
} else {
|
||||||
|
views.forEach { $0.removeDropShadow() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal func updateControl(to color: UIColor) {
|
||||||
|
// updateTrackViewGradient(for: color)
|
||||||
|
// }
|
||||||
|
|
||||||
|
internal func updateControl(to value: CGFloat) {
|
||||||
|
let brightness = 1 - max(0, min(1, value))
|
||||||
|
var hue: CGFloat = 0
|
||||||
|
var saturation: CGFloat = 0
|
||||||
|
currentColor.getHue(&hue, saturation: &saturation, brightness: nil, alpha: nil)
|
||||||
|
|
||||||
|
let newColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
|
||||||
|
|
||||||
|
CATransaction.begin()
|
||||||
|
CATransaction.setDisableActions(true)
|
||||||
|
sliderHandleView.handleColor = newColor
|
||||||
|
CATransaction.commit()
|
||||||
|
|
||||||
|
moveHandle(to: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func updateTrackViewGradient(for color: UIColor) {
|
||||||
|
sliderTrackView.gradientValues = (color, .black)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal 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)
|
||||||
|
|
||||||
|
sliderHandleView.frame = CGRect(origin: CGPoint(x: xPos - (size.width / 2), y: 0), size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -17,7 +17,7 @@ public protocol ChromaColorPickerDelegate {
|
|||||||
|
|
||||||
|
|
||||||
@IBDesignable
|
@IBDesignable
|
||||||
public class ChromaColorPicker: UIControl {
|
public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
||||||
|
|
||||||
@IBInspectable public var borderWidth: CGFloat = 8.0 {
|
@IBInspectable public var borderWidth: CGFloat = 8.0 {
|
||||||
didSet { setNeedsLayout() }
|
didSet { setNeedsLayout() }
|
||||||
@ -27,6 +27,10 @@ public class ChromaColorPicker: UIControl {
|
|||||||
didSet { setNeedsLayout() }
|
didSet { setNeedsLayout() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBInspectable public var showsBrightnessSlider: Bool = false {
|
||||||
|
didSet { /* todo */ }
|
||||||
|
}
|
||||||
|
|
||||||
@IBInspectable public var showsShadow: Bool = true {
|
@IBInspectable public var showsShadow: Bool = true {
|
||||||
didSet { setNeedsLayout() }
|
didSet { setNeedsLayout() }
|
||||||
}
|
}
|
||||||
@ -76,10 +80,9 @@ public class ChromaColorPicker: UIControl {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func updateShadowIfNeeded() {
|
func updateShadowIfNeeded() {
|
||||||
if showsShadow {
|
if showsShadow {
|
||||||
let dropShadowHeight = bounds.height * 0.01
|
applyDropShadow(shadowProperties(forHeight: bounds.height))
|
||||||
applyDropShadow(color: UIColor.black, opacity: 0.2, offset: CGSize(width: 0, height: dropShadowHeight), radius: 2)
|
|
||||||
} else {
|
} else {
|
||||||
removeDropShadow()
|
removeDropShadow()
|
||||||
}
|
}
|
||||||
|
25
Source/ChromaControlStylable.swift
Normal file
25
Source/ChromaControlStylable.swift
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// ChromaControlStylable.swift
|
||||||
|
// ChromaColorPicker
|
||||||
|
//
|
||||||
|
// Created by Jon Cardasis on 4/13/19.
|
||||||
|
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
internal protocol ChromaControlStylable {
|
||||||
|
var borderWidth: CGFloat { get set }
|
||||||
|
var borderColor: UIColor { get set }
|
||||||
|
var showsShadow: Bool { get set }
|
||||||
|
|
||||||
|
func updateShadowIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal extension ChromaControlStylable where Self: UIView {
|
||||||
|
|
||||||
|
func shadowProperties(forHeight height: CGFloat) -> ShadowProperties {
|
||||||
|
let dropShadowHeight = height * 0.01
|
||||||
|
return ShadowProperties(color: UIColor.black.cgColor, opacity: 0.35, offset: CGSize(width: 0, height: dropShadowHeight), radius: 4)
|
||||||
|
}
|
||||||
|
}
|
@ -56,11 +56,7 @@ public class ColorWheelView: UIView {
|
|||||||
the point does not exist within the bounds of the color wheel.
|
the point does not exist within the bounds of the color wheel.
|
||||||
*/
|
*/
|
||||||
public func pixelColor(at point: CGPoint) -> UIColor? {
|
public func pixelColor(at point: CGPoint) -> UIColor? {
|
||||||
guard bounds.offsetBy(dx: 1, dy: 1).contains(point) else { return nil }
|
guard pointIsInColorWheel(point) else { return nil }
|
||||||
|
|
||||||
let distanceFromCenter: CGFloat = hypot(center.x - point.x, center.y - point.y)
|
|
||||||
let pointExistsInRadius: Bool = distanceFromCenter <= radius
|
|
||||||
guard pointExistsInRadius else { return nil }
|
|
||||||
|
|
||||||
let pixel = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
|
let pixel = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
|
||||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
@ -127,4 +123,12 @@ public class ColorWheelView: UIView {
|
|||||||
])
|
])
|
||||||
return filter?.outputImage
|
return filter?.outputImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal func pointIsInColorWheel(_ point: CGPoint) -> Bool {
|
||||||
|
guard bounds.offsetBy(dx: 1, dy: 1).contains(point) else { return false }
|
||||||
|
|
||||||
|
let distanceFromCenter: CGFloat = hypot(center.x - point.x, center.y - point.y)
|
||||||
|
let pointExistsInRadius: Bool = distanceFromCenter <= (radius - layer.borderWidth)
|
||||||
|
return pointExistsInRadius
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
75
Source/Helpers/SliderHandleView.swift
Normal file
75
Source/Helpers/SliderHandleView.swift
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//
|
||||||
|
// SliderHandleView.swift
|
||||||
|
// ChromaColorPicker
|
||||||
|
//
|
||||||
|
// Created by Jon Cardasis on 4/13/19.
|
||||||
|
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
internal class SliderHandleView: UIView {
|
||||||
|
|
||||||
|
var handleColor: UIColor = .black {
|
||||||
|
didSet { updateHandleColor(to: handleColor) }
|
||||||
|
}
|
||||||
|
|
||||||
|
var borderWidth: CGFloat = 3.0 {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
var borderColor: UIColor = .white {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
let radius: CGFloat = bounds.height / 10
|
||||||
|
handleLayer.path = makeRoundedTrianglePath(width: bounds.width, height: bounds.height, radius: radius)
|
||||||
|
handleLayer.strokeColor = borderColor.cgColor
|
||||||
|
handleLayer.lineWidth = borderWidth
|
||||||
|
handleLayer.position = CGPoint(x: bounds.width / 2, y: (bounds.height / 2.0) - (radius / 4.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
let handleLayer = CAShapeLayer()
|
||||||
|
|
||||||
|
private struct CornerPoint {
|
||||||
|
let center: CGPoint
|
||||||
|
let startAngle: CGFloat
|
||||||
|
let endAngle: CGFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
private func commonInit() {
|
||||||
|
layer.addSublayer(handleLayer)
|
||||||
|
updateHandleColor(to: handleColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateHandleColor(to color: UIColor) {
|
||||||
|
handleLayer.fillColor = color.cgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
let path = CGMutablePath()
|
||||||
|
path.move(to: CGPoint(x: 0, y: height / 2))
|
||||||
|
path.addArc(tangent1End: point1, tangent2End: point2, radius: radius)
|
||||||
|
path.addArc(tangent1End: point2, tangent2End: point3, radius: radius)
|
||||||
|
path.addArc(tangent1End: point3, tangent2End: point1, radius: radius)
|
||||||
|
path.closeSubpath()
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
49
Source/Helpers/SliderTrackView.swift
Normal file
49
Source/Helpers/SliderTrackView.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// SliderTrackView.swift
|
||||||
|
// ChromaColorPicker
|
||||||
|
//
|
||||||
|
// Created by Jon Cardasis on 4/13/19.
|
||||||
|
// Copyright © 2019 Jonathan Cardasis. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
internal class SliderTrackView: UIView {
|
||||||
|
typealias GradientValues = (start: UIColor, end: UIColor)
|
||||||
|
|
||||||
|
var gradientValues: GradientValues = (.white, .black) {
|
||||||
|
didSet { updateGradient(for: gradientValues) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
gradient.frame = layer.bounds
|
||||||
|
gradient.cornerRadius = layer.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateGradient(for values: GradientValues) {
|
||||||
|
gradient.colors = [values.start.cgColor, values.end.cgColor]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
private let gradient = CAGradientLayer()
|
||||||
|
|
||||||
|
private func commonInit() {
|
||||||
|
gradient.masksToBounds = true
|
||||||
|
gradient.actions = ["position" : NSNull(), "bounds" : NSNull(), "path" : NSNull()]
|
||||||
|
gradient.startPoint = CGPoint(x: 0, y: 0.5)
|
||||||
|
gradient.endPoint = CGPoint(x: 1, y: 0.5)
|
||||||
|
updateGradient(for: gradientValues)
|
||||||
|
layer.addSublayer(gradient)
|
||||||
|
}
|
||||||
|
}
|
@ -84,6 +84,18 @@ class ColorWheelViewTests: XCTestCase {
|
|||||||
XCTAssertNil(subject.pixelColor(at: testPoint))
|
XCTAssertNil(subject.pixelColor(at: testPoint))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testPixelColorShouldReturnNilForPointOnBorder() {
|
||||||
|
// Given
|
||||||
|
subject.layer.borderWidth = 20
|
||||||
|
subject.layer.borderColor = UIColor.white.cgColor
|
||||||
|
|
||||||
|
// When
|
||||||
|
let testPoint = CGPoint(x: subject.bounds.width - 10, y: subject.frame.midY)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
XCTAssertNil(subject.pixelColor(at: testPoint))
|
||||||
|
}
|
||||||
|
|
||||||
func testPixelColorShouldBeRedAtMaxXMidY() {
|
func testPixelColorShouldBeRedAtMaxXMidY() {
|
||||||
// Given, When
|
// Given, When
|
||||||
let size = subject.frame.size
|
let size = subject.frame.size
|
||||||
|
Loading…
Reference in New Issue
Block a user