Skip to content

Instantly share code, notes, and snippets.

@VictorZhang2014
Created December 15, 2020 14:20
Show Gist options
  • Save VictorZhang2014/b0020aa165082d5dd20978f08c3b7a3e to your computer and use it in GitHub Desktop.
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
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
}
}
@VictorZhang2014
Copy link
Author

Call the function

INSColorPickerView.show(screenshot: self.screenshotForCollectionViewAsImage) { (suckedColor) in
               
}

The function self.view.getColourFromPoint(point: point) is here.

The variable screenshotForCollectionViewAsImage comes from here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment