Add preview view to the color palette control.
This commit is contained in:
parent
283741364c
commit
441c784146
|
@ -264,13 +264,3 @@ public class GalleryRailView: UIView, GalleryRailCellViewDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGSize {
|
||||
var aspectRatio: CGFloat {
|
||||
guard self.height > 0 else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return self.width / self.height
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,118 @@ public class ImageEditorColor: NSObject {
|
|||
|
||||
// MARK: -
|
||||
|
||||
private class PalettePreviewView: OWSLayerView {
|
||||
|
||||
private static let innerRadius: CGFloat = 32
|
||||
private static let shadowMargin: CGFloat = 0
|
||||
// The distance from the "inner circle" to the "teardrop".
|
||||
private static let circleMargin: CGFloat = 3
|
||||
private static let teardropTipRadius: CGFloat = 4
|
||||
private static let teardropPointiness: CGFloat = 12
|
||||
|
||||
private let teardropColor = UIColor.white
|
||||
public var selectedColor = UIColor.white {
|
||||
didSet {
|
||||
circleLayer.fillColor = selectedColor.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
private let circleLayer: CAShapeLayer
|
||||
private let teardropLayer: CAShapeLayer
|
||||
|
||||
override init() {
|
||||
let circleLayer = CAShapeLayer()
|
||||
let teardropLayer = CAShapeLayer()
|
||||
self.circleLayer = circleLayer
|
||||
self.teardropLayer = teardropLayer
|
||||
|
||||
super.init()
|
||||
|
||||
circleLayer.strokeColor = nil
|
||||
teardropLayer.strokeColor = nil
|
||||
// Layer order matters.
|
||||
layer.addSublayer(teardropLayer)
|
||||
layer.addSublayer(circleLayer)
|
||||
|
||||
teardropLayer.fillColor = teardropColor.cgColor
|
||||
teardropLayer.shadowColor = UIColor.black.cgColor
|
||||
teardropLayer.shadowRadius = 2.0
|
||||
teardropLayer.shadowOpacity = 0.33
|
||||
teardropLayer.shadowOffset = .zero
|
||||
|
||||
layoutCallback = { (view) in
|
||||
PalettePreviewView.updateLayers(view: view,
|
||||
circleLayer: circleLayer,
|
||||
teardropLayer: teardropLayer)
|
||||
}
|
||||
|
||||
// The bounding rect of the teardrop + shadow is non-trivial, so
|
||||
// we use a generous size that reserves plenty of space.
|
||||
//
|
||||
// The size doesn't matter since this view is
|
||||
// mostly transparent and isn't hot.
|
||||
autoSetDimensions(to: CGSize(width: PalettePreviewView.innerRadius * 4,
|
||||
height: PalettePreviewView.innerRadius * 4))
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "use other init() instead.")
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
static func updateLayers(view: UIView,
|
||||
circleLayer: CAShapeLayer,
|
||||
teardropLayer: CAShapeLayer) {
|
||||
let bounds = view.bounds
|
||||
let outerRadius = innerRadius + circleMargin
|
||||
let rightEdge = CGPoint(x: bounds.width,
|
||||
y: bounds.height * 0.5)
|
||||
let teardropTipCenter = rightEdge.minus(CGPoint(x: teardropTipRadius + shadowMargin, y: 0))
|
||||
let circleCenter = teardropTipCenter.minus(CGPoint(x: teardropPointiness + innerRadius, y: 0))
|
||||
|
||||
// The "teardrop" shape is bounded by 2 circles, joined by their tangents.
|
||||
//
|
||||
// UIBezierPath can be used to draw this using 2 arcs, if we
|
||||
// have the angle of the tangents.
|
||||
//
|
||||
// Finding the tangent between two circles of known distance + radius
|
||||
// is pretty straightforward. We solve for the right triangle that
|
||||
// defines the tangents and atan() that triangle to get the angle.
|
||||
//
|
||||
// 1. Find the length of the hypotenuse.
|
||||
let circleCenterDistance = teardropTipCenter.minus(circleCenter).length
|
||||
// 2. Fine the length of the first side.
|
||||
let radiusDiff = outerRadius - teardropTipRadius
|
||||
// 2. Fine the length of the second side.
|
||||
let tangentLength = (circleCenterDistance.square - radiusDiff.square).squareRoot()
|
||||
let angle = atan2(tangentLength, radiusDiff)
|
||||
|
||||
let teardropPath = UIBezierPath()
|
||||
teardropPath.addArc(withCenter: circleCenter,
|
||||
radius: outerRadius,
|
||||
startAngle: +angle,
|
||||
endAngle: -angle,
|
||||
clockwise: true)
|
||||
teardropPath.addArc(withCenter: teardropTipCenter,
|
||||
radius: teardropTipRadius,
|
||||
startAngle: -angle,
|
||||
endAngle: +angle,
|
||||
clockwise: true)
|
||||
|
||||
teardropLayer.path = teardropPath.cgPath
|
||||
teardropLayer.frame = bounds
|
||||
|
||||
let innerCircleSize = CGSize(width: innerRadius * 2,
|
||||
height: innerRadius * 2)
|
||||
let circleFrame = CGRect(origin: circleCenter.minus(innerCircleSize.asPoint.times(0.5)),
|
||||
size: innerCircleSize)
|
||||
circleLayer.path = UIBezierPath(ovalIn: circleFrame).cgPath
|
||||
circleLayer.frame = bounds
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class ImageEditorPaletteView: UIView {
|
||||
|
||||
public weak var delegate: ImageEditorPaletteViewDelegate?
|
||||
|
@ -89,6 +201,8 @@ public class ImageEditorPaletteView: UIView {
|
|||
private let imageWrapper = OWSLayerView()
|
||||
private let shadowView = UIView()
|
||||
private var selectionConstraint: NSLayoutConstraint?
|
||||
private let previewView = PalettePreviewView()
|
||||
private var previewConstraint: NSLayoutConstraint?
|
||||
|
||||
private func createContents() {
|
||||
self.backgroundColor = .clear
|
||||
|
@ -140,6 +254,14 @@ public class ImageEditorPaletteView: UIView {
|
|||
selectionConstraint.autoInstall()
|
||||
self.selectionConstraint = selectionConstraint
|
||||
|
||||
previewView.isHidden = true
|
||||
addSubview(previewView)
|
||||
previewView.autoPinEdge(.trailing, to: .leading, of: imageView, withOffset: -24)
|
||||
let previewConstraint = NSLayoutConstraint(item: previewView,
|
||||
attribute: .centerY, relatedBy: .equal, toItem: imageWrapper, attribute: .top, multiplier: 1, constant: 0)
|
||||
previewConstraint.autoInstall()
|
||||
self.previewConstraint = previewConstraint
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
addGestureRecognizer(PaletteGestureRecognizer(target: self, action: #selector(didTouch)))
|
||||
|
||||
|
@ -208,6 +330,7 @@ public class ImageEditorPaletteView: UIView {
|
|||
|
||||
private func updateState() {
|
||||
selectionView.backgroundColor = selectedValue.color
|
||||
previewView.selectedColor = selectedValue.color
|
||||
|
||||
guard let selectionConstraint = selectionConstraint else {
|
||||
owsFailDebug("Missing selectionConstraint.")
|
||||
|
@ -215,6 +338,12 @@ public class ImageEditorPaletteView: UIView {
|
|||
}
|
||||
let selectionY = imageWrapper.height() * selectedValue.palettePhase
|
||||
selectionConstraint.constant = selectionY
|
||||
|
||||
guard let previewConstraint = previewConstraint else {
|
||||
owsFailDebug("Missing previewConstraint.")
|
||||
return
|
||||
}
|
||||
previewConstraint.constant = selectionY
|
||||
}
|
||||
|
||||
// MARK: Events
|
||||
|
@ -222,9 +351,12 @@ public class ImageEditorPaletteView: UIView {
|
|||
@objc
|
||||
func didTouch(gesture: UIGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .began, .changed, .ended:
|
||||
break
|
||||
case .began, .changed:
|
||||
previewView.isHidden = false
|
||||
case .ended:
|
||||
previewView.isHidden = true
|
||||
default:
|
||||
previewView.isHidden = true
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,10 @@ public extension CGFloat {
|
|||
public func fuzzyEquals(_ other: CGFloat, tolerance: CGFloat = 0.001) -> Bool {
|
||||
return abs(self - other) < tolerance
|
||||
}
|
||||
|
||||
public var square: CGFloat {
|
||||
return self * self
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
|
@ -222,6 +226,25 @@ public extension CGPoint {
|
|||
return (x.fuzzyEquals(other.x, tolerance: tolerance) &&
|
||||
y.fuzzyEquals(other.y, tolerance: tolerance))
|
||||
}
|
||||
|
||||
public static func tan(angle: CGFloat) -> CGPoint {
|
||||
return CGPoint(x: sin(angle),
|
||||
y: cos(angle))
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGSize {
|
||||
var aspectRatio: CGFloat {
|
||||
guard self.height > 0 else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return self.width / self.height
|
||||
}
|
||||
|
||||
var asPoint: CGPoint {
|
||||
return CGPoint(x: width, y: height)
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGRect {
|
||||
|
|
Loading…
Reference in New Issue