Created
December 15, 2020 14:20
-
-
Save VictorZhang2014/b0020aa165082d5dd20978f08c3b7a3e to your computer and use it in GitHub Desktop.
It's a color picker from any UIView, Instagram-like color picker. - Swift 5.2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
private var INSColorPickerWindow: UIWindow? | |
class INSColorPickerView: UIViewController { | |
private var screenshotForCollectionViewAsImage: UIImage? | |
private var completion: ((UIColor) -> Void)? | |
private let penView = UIView() | |
private let pen = UIImageView() | |
private let penBg = UIView() | |
private let penPoint = UIView() | |
public static func show(screenshot: UIImage?, completion: @escaping (UIColor) -> Void) { | |
let vc = INSColorPickerView() | |
vc.screenshotForCollectionViewAsImage = screenshot | |
vc.completion = completion | |
if INSColorPickerWindow == nil { | |
INSColorPickerWindow = UIWindow(frame: UIScreen.main.bounds) | |
} | |
INSColorPickerWindow?.rootViewController = vc | |
INSColorPickerWindow?.windowLevel = UIWindow.Level.alert | |
INSColorPickerWindow?.makeKeyAndVisible() | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
setupViews() | |
} | |
private func setupViews() { | |
view.backgroundColor = UIColor.black | |
let imageView = UIImageView() | |
imageView.image = screenshotForCollectionViewAsImage | |
imageView.contentMode = .scaleAspectFill | |
view.addSubview(imageView) | |
imageView.snp.makeConstraints { | |
$0.edges.equalToSuperview() | |
} | |
let penW: CGFloat = 120 | |
let penH: CGFloat = 120 | |
let penX: CGFloat = (view.bounds.size.width - penW) / 2 | |
let penY: CGFloat = (view.bounds.size.width - penW) / 2 | |
penView.frame = CGRect(x: penX, y: penY, width: penW, height: penH) | |
view.addSubview(penView) | |
penView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(willDragPenView(_:)))) | |
penBg.backgroundColor = UIColor.clear | |
penView.addSubview(penBg) | |
penBg.snp.makeConstraints { | |
$0.top.equalToSuperview() | |
$0.centerX.equalToSuperview() | |
$0.width.equalTo(88) | |
$0.height.equalTo(95) | |
} | |
pen.image = UIImage(named: "colorpicker-pen") | |
penBg.addSubview(pen) | |
pen.snp.makeConstraints { | |
$0.top.equalTo(12) | |
$0.left.equalTo(1.0) | |
$0.right.equalTo(-1.0) | |
$0.height.equalTo(84) | |
} | |
penPoint.backgroundColor = UIColor.white | |
penPoint.layer.borderWidth = 0.5 | |
penPoint.layer.borderColor = UIColor.white.cgColor | |
penPoint.layer.cornerRadius = 6 | |
penView.addSubview(penPoint) | |
penPoint.snp.makeConstraints { | |
$0.centerX.equalToSuperview() | |
$0.bottom.equalToSuperview() | |
$0.width.height.equalTo(12) | |
} | |
} | |
@objc private func willDragPenView(_ gesture: UIPanGestureRecognizer) { | |
// 1.drag pentView to anywhere by your finger moving | |
guard let theView = gesture.view else { return } | |
let translation = gesture.translation(in: self.view) | |
gesture.view?.center = CGPoint(x: theView.center.x + translation.x, y: theView.center.y + translation.y) | |
gesture.setTranslation(CGPoint(), in: self.view) | |
// 2.Extract the point of center in the bottom of penView variable | |
var point = penView.frame.origin | |
point.x = point.x + penView.frame.size.width / 2 | |
point.y = penView.frame.origin.y + penView.frame.size.height | |
// 3.Get the color of CGPoint {x, y} | |
let color = self.view.getColourFromPoint(point: point) | |
penPoint.backgroundColor = color | |
// 4.As we use BezierPath to create a custom shape, and fill the color to the area that BezierPath goes through it | |
penBg.InsColorPickerShape(with: color) | |
if gesture.state == .ended { | |
completion?(color) | |
INSColorPickerWindow?.removeFromSuperview() | |
INSColorPickerWindow = nil | |
} | |
} | |
deinit { | |
print("\(self) deinit") | |
} | |
} | |
extension UIView { | |
// Reference Doc: https://www.appcoda.com/bezier-paths-introduction/ | |
// This function returns a shape just as follow | |
func InsColorPickerShape(with color: UIColor) { | |
/* | |
The function draw the shape as follows: | |
***** | |
* * | |
* * | |
* * | |
* * | |
* * | |
* * | |
* | |
The degress of a Circle | |
270 | |
180 0 | |
90 | |
*/ | |
let width: CGFloat = self.frame.size.width | |
let height: CGFloat = self.frame.size.height | |
let path = UIBezierPath() | |
path.addArc(withCenter: CGPoint(x: width / 2, y: height / 2), | |
radius: 35, | |
startAngle: CGFloat(270.0).toRadians(), | |
endAngle: CGFloat(150.0).toRadians(), | |
clockwise: false) | |
path.addLine(to: CGPoint(x: width / 2, y: height)) | |
path.addLine(to: CGPoint(x: width - 9, y: height / 2 + 13)) | |
path.addArc(withCenter: CGPoint(x: width / 2, y: height / 2), | |
radius: 35, | |
startAngle: CGFloat(30.0).toRadians(), | |
endAngle: CGFloat(270.0).toRadians(), | |
clockwise: false) | |
path.close() | |
let shapeLayer = CAShapeLayer() | |
shapeLayer.path = path.cgPath | |
self.backgroundColor = color | |
self.layer.mask = shapeLayer | |
} | |
} | |
extension CGFloat { | |
/** | |
* Converts an angle in degrees to radians. | |
*/ | |
func toRadians() -> CGFloat { | |
return self * .pi / 180 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Call the function
The function
self.view.getColourFromPoint(point: point)
is here.The variable
screenshotForCollectionViewAsImage
comes from here.