Simple swift color picker popover (iOS)

前端 未结 8 1170
被撕碎了的回忆
被撕碎了的回忆 2020-12-12 13:30

Is there is a simple way to implement a color picker popover in swift? Are there any built-in libraries or UI elements that I could leverage for this purpose? I saw some c

相关标签:
8条回答
  • 2020-12-12 13:52

    ColorPickerViewImage

    Based on Christian1313 answer, I added darker colors:

    @IBDesignable final public class SwiftColorView: UIView {
    
    weak var colorSelectedDelegate: ColorDelegate?
    
    @IBInspectable public var numColorsX:Int =  10 {
        didSet {
            setNeedsDisplay()
        }
    }
    
    @IBInspectable public var numColorsY:Int = 18 {
        didSet {
            setNeedsDisplay()
        }
    }
    
    @IBInspectable public var coloredBorderWidth:Int = 10 {
        didSet {
            setNeedsDisplay()
        }
    }
    
    @IBInspectable public var showGridLines:Bool = false {
        didSet {
            setNeedsDisplay()
        }
    }
    
    weak var delegate: SwiftColorPickerDataSource?
    
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
    
        colorSelectedDelegate?.setStroke(color: colorAtPoint(point: location))
    }
    
    public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
    
        colorSelectedDelegate?.setStroke(color: colorAtPoint(point: location))
    }
    
    public override func draw(_ rect: CGRect) {
        super.draw(rect)
        let lineColor = UIColor.gray
        let pS = patternSize()
        let w = pS.w
        let h = pS.h
    
        for y in 0..<numColorsY
        {
            for x in 0..<numColorsX
            {
                let path = UIBezierPath()
                let start = CGPoint(x: CGFloat(x)*w+CGFloat(coloredBorderWidth), y: CGFloat(y)*h+CGFloat(coloredBorderWidth))
                path.move(to: start);
                path.addLine(to: CGPoint(x: start.x+w, y: start.y))
                path.addLine(to: CGPoint(x: start.x+w, y: start.y+h))
                path.addLine(to: CGPoint(x: start.x, y: start.y+h))
                path.addLine(to: start)
                path.lineWidth = 0.25
                colorForRectAt(x: x,y:y).setFill();
    
                if (showGridLines)
                {
                    lineColor.setStroke()
                }
                else
                {
                    colorForRectAt(x: x, y: y).setStroke();
                }
                path.fill();
                path.stroke();
            }
        }
    }
    
    private func colorForRectAt(x: Int, y: Int) -> UIColor
    {
    
        if let ds = delegate {
            return ds.colorForPalletIndex(x: x, y: y, numXStripes: numColorsX, numYStripes: numColorsY)
        } else {
    
            var hue:CGFloat = CGFloat(x) / CGFloat(numColorsX)
            var fillColor = UIColor.white
            if (y==0)
            {
                if (x==(numColorsX-1))
                {
                    hue = 1.0;
                }
                fillColor = UIColor(white: hue, alpha: 1.0);
            }
            else
            {
                if y < numColorsY / 2 {
                    //dark
                    let length = numColorsY / 2
                    let brightness: CGFloat = CGFloat(y) / CGFloat(length)
                    fillColor = UIColor(hue: hue, saturation: 1.0, brightness: brightness, alpha: 1.0)
                } else if y == numColorsY / 2 {
                    // normal
                    fillColor = UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
                } else {
                    // light
                    let length = numColorsY / 2 - 1
                    let offset = y - length - 1
                    let sat:CGFloat = CGFloat(1.0) - CGFloat(offset) / CGFloat(length + 1)
                    print("sat", sat)
                    fillColor = UIColor(hue: hue, saturation: sat, brightness: 1.0, alpha: 1.0)
                }
            }
            return fillColor
        }
    }
    
    func colorAtPoint(point: CGPoint) -> UIColor
    {
        let pS = patternSize()
        let w = pS.w
        let h = pS.h
        let x = (point.x-CGFloat(coloredBorderWidth))/w
        let y = (point.y-CGFloat(coloredBorderWidth))/h
        return colorForRectAt(x: Int(x), y:Int(y))
    }
    
    private func patternSize() -> (w: CGFloat, h:CGFloat)
    {
        let width = self.bounds.width-CGFloat(2*coloredBorderWidth)
        let height = self.bounds.height-CGFloat(2*coloredBorderWidth)
    
        let w = width/CGFloat(numColorsX)
        let h = height/CGFloat(numColorsY)
        return (w,h)
    }
    
    public override func prepareForInterfaceBuilder()
    {
        print("Compiled and run for IB")
    }
    

    }

    0 讨论(0)
  • 2020-12-12 13:53

    Swift 3.0 version of @joel-teply's answer:

    internal protocol HSBColorPickerDelegate : NSObjectProtocol {
        func HSBColorColorPickerTouched(sender:HSBColorPicker, color:UIColor, point:CGPoint, state:UIGestureRecognizerState)
    }
    
    @IBDesignable
    class HSBColorPicker : UIView {
    
        weak internal var delegate: HSBColorPickerDelegate?
        let saturationExponentTop:Float = 2.0
        let saturationExponentBottom:Float = 1.3
    
        @IBInspectable var elementSize: CGFloat = 1.0 {
            didSet {
                setNeedsDisplay()
            }
        }
    
    
        private func initialize() {
    
            self.clipsToBounds = true
            let touchGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.touchedColor(gestureRecognizer:)))
            touchGesture.minimumPressDuration = 0
            touchGesture.allowableMovement = CGFloat.greatestFiniteMagnitude
            self.addGestureRecognizer(touchGesture)
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            initialize()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            initialize()
        }
    
        override func draw(_ rect: CGRect) {
            let context = UIGraphicsGetCurrentContext()
    
            for y in stride(from: (0 as CGFloat), to: rect.height, by: elementSize) {
    
                var saturation = y < rect.height / 2.0 ? CGFloat(2 * y) / rect.height : 2.0 * CGFloat(rect.height - y) / rect.height
                saturation = CGFloat(powf(Float(saturation), y < rect.height / 2.0 ? saturationExponentTop : saturationExponentBottom))
                let brightness = y < rect.height / 2.0 ? CGFloat(1.0) : 2.0 * CGFloat(rect.height - y) / rect.height
    
                for x in stride(from: (0 as CGFloat), to: rect.width, by: elementSize) {
                    let hue = x / rect.width
                    let color = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
                    context!.setFillColor(color.cgColor)
                    context!.fill(CGRect(x:x, y:y, width:elementSize,height:elementSize))
                }
            }
        }
    
        func getColorAtPoint(point:CGPoint) -> UIColor {
            let roundedPoint = CGPoint(x:elementSize * CGFloat(Int(point.x / elementSize)),
                                       y:elementSize * CGFloat(Int(point.y / elementSize)))
            var saturation = roundedPoint.y < self.bounds.height / 2.0 ? CGFloat(2 * roundedPoint.y) / self.bounds.height
                : 2.0 * CGFloat(self.bounds.height - roundedPoint.y) / self.bounds.height
            saturation = CGFloat(powf(Float(saturation), roundedPoint.y < self.bounds.height / 2.0 ? saturationExponentTop : saturationExponentBottom))
            let brightness = roundedPoint.y < self.bounds.height / 2.0 ? CGFloat(1.0) : 2.0 * CGFloat(self.bounds.height - roundedPoint.y) / self.bounds.height
            let hue = roundedPoint.x / self.bounds.width
            return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
        }
    
        func getPointForColor(color:UIColor) -> CGPoint {
            var hue:CGFloat=0;
            var saturation:CGFloat=0;
            var brightness:CGFloat=0;
            color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil);
    
            var yPos:CGFloat = 0
            let halfHeight = (self.bounds.height / 2)
    
            if (brightness >= 0.99) {
                let percentageY = powf(Float(saturation), 1.0 / saturationExponentTop)
                yPos = CGFloat(percentageY) * halfHeight
            } else {
                //use brightness to get Y
                yPos = halfHeight + halfHeight * (1.0 - brightness)
            }
    
            let xPos = hue * self.bounds.width
    
            return CGPoint(x: xPos, y: yPos)
        }
    
        func touchedColor(gestureRecognizer: UILongPressGestureRecognizer){
            let point = gestureRecognizer.location(in: self)
            let color = getColorAtPoint(point: point)
    
            self.delegate?.HSBColorColorPickerTouched(sender: self, color: color, point: point, state:gestureRecognizer.state)
        }
    }
    
    0 讨论(0)
提交回复
热议问题