Skip to content

Instantly share code, notes, and snippets.

@VictorZhang2014
Created December 15, 2020 14:20
Show Gist options
  • Select an option

  • Save VictorZhang2014/b0020aa165082d5dd20978f08c3b7a3e to your computer and use it in GitHub Desktop.

Select an option

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.

@LouWisoky
Copy link

LouWisoky commented Jul 10, 2025

I like Block Blast Unblocked your quick update on this article the most. Keep it up for long time followers like me.

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