mirror of
https://github.com/joncardasis/ChromaColorPicker.git
synced 2024-11-25 22:12:01 +03:00
Refactor color handle layer positioning, pulling up layout logic to parent. Picker now uses touch delegate events instead of a gesture for faster recognition. Additional handle positioning improvements.
This commit is contained in:
parent
c701c6d109
commit
5de59e1092
@ -39,7 +39,14 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public var handleSize: CGSize { /* TODO */ }
|
/// The size handles should be displayed at.
|
||||||
|
public var handleSize: CGSize = defaultHandleSize {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An extension to handles' hitboxes in the +Y direction.
|
||||||
|
/// Allows for handles to be grabbed more easily.
|
||||||
|
public var handleHitboxExtensionY: CGFloat = 10.0
|
||||||
|
|
||||||
/// Handles added to the color picker.
|
/// Handles added to the color picker.
|
||||||
private(set) public var handles: [ChromaColorHandle] = []
|
private(set) public var handles: [ChromaColorHandle] = []
|
||||||
@ -65,9 +72,9 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
updateBorderIfNeeded()
|
updateBorderIfNeeded()
|
||||||
|
|
||||||
handles.forEach { handle in
|
handles.forEach { handle in
|
||||||
//let location = colorWheelView.location(of: handle.color)
|
let location = colorWheelView.location(of: handle.color)
|
||||||
handle.frame.size = defaultHandleSize
|
handle.frame.size = defaultHandleSize
|
||||||
//handle.center = location
|
positionHandle(handle, forColorLocation: location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,16 +82,13 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func addHandle(at color: UIColor? = nil) -> ChromaColorHandle {
|
public func addHandle(at color: UIColor? = nil) -> ChromaColorHandle {
|
||||||
let handleColor = color ?? defaultHandleColorPosition
|
|
||||||
let handle = ChromaColorHandle()
|
let handle = ChromaColorHandle()
|
||||||
handle.color = handleColor
|
handle.color = color ?? defaultHandleColorPosition
|
||||||
|
|
||||||
addHandle(handle)
|
addHandle(handle)
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addHandle(_ handle: ChromaColorHandle) {
|
public func addHandle(_ handle: ChromaColorHandle) {
|
||||||
addPanGesture(to: handle)
|
|
||||||
handles.append(handle)
|
handles.append(handle)
|
||||||
colorWheelView.addSubview(handle)
|
colorWheelView.addSubview(handle)
|
||||||
}
|
}
|
||||||
@ -101,49 +105,66 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
self.backgroundColor = UIColor.clear
|
self.backgroundColor = UIColor.clear
|
||||||
self.layer.masksToBounds = false
|
self.layer.masksToBounds = false
|
||||||
setupColorWheelView()
|
setupColorWheelView()
|
||||||
//setupGestures()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Control
|
// MARK: - Control
|
||||||
|
|
||||||
// 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)
|
||||||
//
|
|
||||||
// for handle in handles {
|
for handle in handles {
|
||||||
// if handle.frame.contains(location) {
|
if extendedHitFrame(for: handle).contains(location) {
|
||||||
// print("tapped on handle")
|
currentHandle = handle
|
||||||
// currentHandle = handle
|
colorWheelView.bringSubviewToFront(handle)
|
||||||
// return true
|
animateHandleScale(handle, shouldGrow: true)
|
||||||
// }
|
return true
|
||||||
// }
|
}
|
||||||
// return false
|
}
|
||||||
// }
|
return false
|
||||||
//
|
}
|
||||||
// public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
|
||||||
// let location = touch.location(in: colorWheelView)
|
public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
// guard let handle = currentHandle else { return false }
|
var location = touch.location(in: colorWheelView)
|
||||||
//
|
guard let handle = currentHandle else { return false }
|
||||||
// handle.center = location
|
|
||||||
// if let selectedColor = colorWheelView.pixelColor(at: location) {
|
if !colorWheelView.pointIsInColorWheel(location) {
|
||||||
// print(location)
|
// Touch is outside color wheel and should map to outermost edge.
|
||||||
// handle.color = selectedColor
|
let center = colorWheelView.center
|
||||||
//
|
let radius = colorWheelView.radius
|
||||||
// delegate?.colorPickerDidChooseColor(self, color: selectedColor)
|
let angleToCenter = atan2(location.x - center.x, location.y - center.y)
|
||||||
// }
|
let positionOnColorWheelEdge = CGPoint(x: center.x + radius * sin(angleToCenter),
|
||||||
//
|
y: center.y + radius * cos(angleToCenter))
|
||||||
// sendActions(for: .valueChanged)
|
print("pos: \(positionOnColorWheelEdge)")
|
||||||
// return true
|
location = positionOnColorWheelEdge
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
if let pixelColor = colorWheelView.pixelColor(at: location) {
|
||||||
// sendActions(for: .editingDidEnd)
|
let previousBrightness = handle.color.brightness
|
||||||
// }
|
handle.color = pixelColor.withBrightness(previousBrightness)
|
||||||
|
positionHandle(handle, forColorLocation: location)
|
||||||
|
|
||||||
|
if let slider = brightnessSlider {
|
||||||
|
slider.trackColor = pixelColor
|
||||||
|
slider.currentValue = slider.value(brightness: previousBrightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
informDelegateOfColorChange(on: handle)
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||||
|
if let handle = currentHandle {
|
||||||
|
animateHandleScale(handle, shouldGrow: false)
|
||||||
|
}
|
||||||
|
sendActions(for: .editingDidEnd)
|
||||||
|
}
|
||||||
|
|
||||||
internal func addPanGesture(to handle: ChromaColorHandle) {
|
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handleWasMoved(_:)))
|
// Self should handle all touch events, forwarding if needed.
|
||||||
panGesture.maximumNumberOfTouches = 1
|
return self
|
||||||
panGesture.minimumNumberOfTouches = 1
|
|
||||||
handle.addGestureRecognizer(panGesture)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Setup & Layout
|
// MARK: Setup & Layout
|
||||||
@ -172,62 +193,8 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
colorWheelView.layer.borderWidth = borderWidth
|
colorWheelView.layer.borderWidth = borderWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func setupGestures() {
|
|
||||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(colorWheelTapped(_:)))
|
|
||||||
colorWheelView.isUserInteractionEnabled = true
|
|
||||||
colorWheelView.addGestureRecognizer(tapGesture)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Actions
|
// MARK: Actions
|
||||||
|
|
||||||
@objc
|
|
||||||
internal func handleWasMoved(_ gesture: UIPanGestureRecognizer) {
|
|
||||||
if let touchedView = gesture.view {
|
|
||||||
colorWheelView.bringSubviewToFront(touchedView)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (gesture.state) {
|
|
||||||
case .began:
|
|
||||||
currentHandle = gesture.view as? ChromaColorHandle
|
|
||||||
case .changed:
|
|
||||||
let location = gesture.location(in: colorWheelView)
|
|
||||||
|
|
||||||
if let handle = currentHandle, let pixelColor = colorWheelView.pixelColor(at: location) {
|
|
||||||
let previousBrightness = handle.color.brightness
|
|
||||||
|
|
||||||
print(pixelColor)
|
|
||||||
|
|
||||||
handle.color = pixelColor.withBrightness(previousBrightness)
|
|
||||||
handle.center = location
|
|
||||||
|
|
||||||
if let slider = brightnessSlider {
|
|
||||||
slider.trackColor = pixelColor
|
|
||||||
slider.currentValue = slider.value(brightness: previousBrightness)
|
|
||||||
}
|
|
||||||
|
|
||||||
informDelegateOfColorChange(on: handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sendActions(for: .touchDragInside)
|
|
||||||
case .ended:
|
|
||||||
/* Shrink Animation */
|
|
||||||
//self.executeHandleShrinkAnimation()
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
|
||||||
internal func colorWheelTapped(_ gesture: UITapGestureRecognizer) {
|
|
||||||
let location = gesture.location(in: colorWheelView)
|
|
||||||
let pixelColor = colorWheelView.pixelColor(at: location)
|
|
||||||
print(pixelColor)
|
|
||||||
|
|
||||||
print(location)
|
|
||||||
delegate?.colorPickerDidChooseColor(self, color: pixelColor!)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
internal func brightnessSliderDidValueChange(_ slider: ChromaBrightnessSlider) {
|
internal func brightnessSliderDidValueChange(_ slider: ChromaBrightnessSlider) {
|
||||||
guard let currentHandle = currentHandle else { return }
|
guard let currentHandle = currentHandle else { return }
|
||||||
@ -239,7 +206,29 @@ public class ChromaColorPicker: UIControl, ChromaControlStylable {
|
|||||||
internal func informDelegateOfColorChange(on handle: ChromaColorHandle) {
|
internal func informDelegateOfColorChange(on handle: ChromaColorHandle) {
|
||||||
delegate?.colorPickerDidChooseColor(self, color: handle.color)
|
delegate?.colorPickerDidChooseColor(self, color: handle.color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
internal func extendedHitFrame(for handle: ChromaColorHandle) -> CGRect {
|
||||||
|
var frame = handle.frame
|
||||||
|
frame.size.height += handleHitboxExtensionY
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func positionHandle(_ handle: ChromaColorHandle, forColorLocation location: CGPoint) {
|
||||||
|
handle.center = location.applying(CGAffineTransform.identity.translatedBy(x: 0, y: -handle.bounds.height / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func animateHandleScale(_ handle: ChromaColorHandle, shouldGrow: Bool) {
|
||||||
|
if shouldGrow && handle.transform.d > 1 { return } // Already grown
|
||||||
|
|
||||||
|
let transform = shouldGrow ? CGAffineTransform(scaleX: 1.25, y: 1.25) : CGAffineTransform(scaleX: 1, y: 1)
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.15, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.6, options: .curveEaseInOut, animations: {
|
||||||
|
handle.transform = transform
|
||||||
|
}, completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal let defaultHandleColorPosition: UIColor = .white
|
internal let defaultHandleColorPosition: UIColor = .white
|
||||||
internal let defaultHandleSize: CGSize = CGSize(width: 42, height: 42)
|
internal let defaultHandleSize: CGSize = CGSize(width: 34, height: 42)
|
||||||
|
@ -37,6 +37,7 @@ public class ColorWheelView: UIView {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the (x,y) location of the color provided within the ColorWheelView.
|
Returns the (x,y) location of the color provided within the ColorWheelView.
|
||||||
|
Disregards color's brightness component.
|
||||||
*/
|
*/
|
||||||
public func location(of color: UIColor) -> CGPoint {
|
public func location(of color: UIColor) -> CGPoint {
|
||||||
var hue: CGFloat = 0
|
var hue: CGFloat = 0
|
||||||
@ -86,6 +87,17 @@ public class ColorWheelView: UIView {
|
|||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns whether or not the point is in the circular area of the color wheel.
|
||||||
|
*/
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
internal let imageView = UIImageView()
|
internal let imageView = UIImageView()
|
||||||
|
|
||||||
@ -123,12 +135,4 @@ 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ public class ChromaColorHandle: UIView, ChromaControlStylable {
|
|||||||
internal let handleShape = CAShapeLayer()
|
internal let handleShape = CAShapeLayer()
|
||||||
|
|
||||||
internal func commonInit() {
|
internal func commonInit() {
|
||||||
backgroundColor = UIColor.red.withAlphaComponent(0.25) //DEBUG
|
|
||||||
layer.addSublayer(handleShape)
|
layer.addSublayer(handleShape)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,14 +86,12 @@ public class ChromaColorHandle: UIView, ChromaControlStylable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal func layoutHandleShape() {
|
internal func layoutHandleShape() {
|
||||||
let size = CGSize(width: bounds.height * handleWidthHeightRatio, height: bounds.height)
|
let size = CGSize(width: bounds.width - borderWidth, height: bounds.height - borderWidth)
|
||||||
handleShape.path = makeHandlePath(frame: CGRect(origin: .zero, size: size))
|
handleShape.path = makeHandlePath(frame: CGRect(origin: .zero, size: size))
|
||||||
handleShape.frame = CGRect(origin: CGPoint(x: bounds.midX - (size.width / 2), y: bounds.midY - size.height), size: size)
|
handleShape.frame = CGRect(origin: CGPoint(x: bounds.midX - (size.width / 2), y: bounds.midY - (size.height / 2)), size: size)
|
||||||
|
|
||||||
handleShape.fillColor = color.cgColor
|
handleShape.fillColor = color.cgColor
|
||||||
handleShape.strokeColor = borderColor.cgColor
|
handleShape.strokeColor = borderColor.cgColor
|
||||||
handleShape.lineWidth = borderWidth
|
handleShape.lineWidth = borderWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal let handleWidthHeightRatio: CGFloat = 52.0 / 65
|
|
||||||
|
Loading…
Reference in New Issue
Block a user