web-dev-qa-db-fra.com

Simple Swift sélecteur de couleurs Swift (iOS)

Existe-t-il un moyen simple d'implémenter un popover de sélecteur de couleurs dans Swift? Y a-t-il des bibliothèques intégrées ou des éléments d'interface utilisateur que je pourrais utiliser à cet effet? J'ai vu des sélecteurs de couleurs écrits en objectif-c, mais ils avaient plusieurs années et je me demandais s'il y avait quelque chose de plus récent.

33
Ethan Strider

En voici une que j'ai faite, qui est aussi simple que possible. C'est juste une UIView légère qui vous permet de spécifier la taille de l'élément au cas où vous voudriez des régions bloquées (elementSize> 1). Il se dessine dans le générateur d'interface afin que vous puissiez définir la taille de l'élément et voir les conséquences. Définissez simplement l'une de vos vues dans le générateur d'interface sur cette classe, puis définissez-vous en tant que délégué. Il vous dira quand quelqu'un tapote ou traîne dessus et le uicolor à cet endroit. Il se dessinera à ses propres limites et il n'y a pas besoin d'autre chose que cette classe, aucune image requise.

Taille de l'élément = 1 (par défaut) element size=1

Taille de l'élément = 10
element size=10

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 : CGFloat in stride(from: 0.0 ,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 : CGFloat in stride(from: 0.0 ,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.0
        var saturation: CGFloat = 0.0
        var brightness: CGFloat = 0.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)
    }

    @objc func touchedColor(gestureRecognizer: UILongPressGestureRecognizer) {
        if (gestureRecognizer.state == UIGestureRecognizerState.began) {
            let point = gestureRecognizer.location(in: self)
            let color = getColorAtPoint(point: point)
            self.delegate?.HSBColorColorPickerTouched(sender: self, color: color, point: point, state:gestureRecognizer.state)
        }        
    }
}
76
Joel Teply

Je suis allé de l'avant et j'ai écrit un simple popover de sélecteur de couleurs dans Swift. J'espère que cela aidera quelqu'un d'autre.

https://github.com/EthanStrider/ColorPickerExample

Image Picker Screenshot

28
Ethan Strider

Version Swift 3.0 de @ joel-teply réponse:

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)
    }
}
12
Christen Ward

Merci pour le point de départ.

Je l'ai pris à partir de là et j'ai écrit un PickerViewController couleur complet avec une UIView personnalisée et du code de dessin.

J'ai créé UIView personnalisé @IBDesignable afin qu'il puisse être rendu dans InterfaceBuilder.

https://github.com/Christian1313/iOS_Swift_ColorPicker

7
Christian1313

Basé sur le code Joel Teply (Swift 4), avec une barre grise sur le dessus:

import UIKit


class ColorPickerView : UIView {

var onColorDidChange: ((_ color: UIColor) -> ())?

let saturationExponentTop:Float = 2.0
let saturationExponentBottom:Float = 1.3

let grayPaletteHeightFactor: CGFloat = 0.1
var rect_grayPalette = CGRect.zero
var rect_mainPalette = CGRect.zero

// adjustable
var elementSize: CGFloat = 1.0 {
    didSet {
        setNeedsDisplay()
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
}

private func setup() {

    self.clipsToBounds = true
    let touchGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.touchedColor(gestureRecognizer:)))
    touchGesture.minimumPressDuration = 0
    touchGesture.allowableMovement = CGFloat.greatestFiniteMagnitude
    self.addGestureRecognizer(touchGesture)
}



override func draw(_ rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()

    rect_grayPalette = CGRect(x: 0, y: 0, width: rect.width, height: rect.height * grayPaletteHeightFactor)
    rect_mainPalette = CGRect(x: 0, y: rect_grayPalette.maxY,
                              width: rect.width, height: rect.height - rect_grayPalette.height)

    // gray palette
    for y in stride(from: CGFloat(0), to: rect_grayPalette.height, by: elementSize) {

        for x in stride(from: (0 as CGFloat), to: rect_grayPalette.width, by: elementSize) {
            let hue = x / rect_grayPalette.width

            let color = UIColor(white: hue, alpha: 1.0)

            context!.setFillColor(color.cgColor)
            context!.fill(CGRect(x:x, y:y, width:elementSize, height:elementSize))
        }
    }

    // main palette
    for y in stride(from: CGFloat(0), to: rect_mainPalette.height, by: elementSize) {

        var saturation = y < rect_mainPalette.height / 2.0 ? CGFloat(2 * y) / rect_mainPalette.height : 2.0 * CGFloat(rect_mainPalette.height - y) / rect_mainPalette.height
        saturation = CGFloat(powf(Float(saturation), y < rect_mainPalette.height / 2.0 ? saturationExponentTop : saturationExponentBottom))
        let brightness = y < rect_mainPalette.height / 2.0 ? CGFloat(1.0) : 2.0 * CGFloat(rect_mainPalette.height - y) / rect_mainPalette.height

        for x in stride(from: (0 as CGFloat), to: rect_mainPalette.width, by: elementSize) {
            let hue = x / rect_mainPalette.width

            let color = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)

            context!.setFillColor(color.cgColor)
            context!.fill(CGRect(x:x, y: y + rect_mainPalette.Origin.y,
                                 width: elementSize, height: elementSize))
        }
    }
}



func getColorAtPoint(point: CGPoint) -> UIColor
{
    var roundedPoint = CGPoint(x:elementSize * CGFloat(Int(point.x / elementSize)),
                               y:elementSize * CGFloat(Int(point.y / elementSize)))

    let hue = roundedPoint.x / self.bounds.width


    // main palette
    if rect_mainPalette.contains(point)
    {
        // offset point, because rect_mainPalette.Origin.y is not 0
        roundedPoint.y -= rect_mainPalette.Origin.y

        var saturation = roundedPoint.y < rect_mainPalette.height / 2.0 ? CGFloat(2 * roundedPoint.y) / rect_mainPalette.height
            : 2.0 * CGFloat(rect_mainPalette.height - roundedPoint.y) / rect_mainPalette.height

        saturation = CGFloat(powf(Float(saturation), roundedPoint.y < rect_mainPalette.height / 2.0 ? saturationExponentTop : saturationExponentBottom))
        let brightness = roundedPoint.y < rect_mainPalette.height / 2.0 ? CGFloat(1.0) : 2.0 * CGFloat(rect_mainPalette.height - roundedPoint.y) / rect_mainPalette.height

        return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
    }
    // gray palette
    else{

        return UIColor(white: hue, alpha: 1.0)
    }
}


@objc func touchedColor(gestureRecognizer: UILongPressGestureRecognizer){
    let point = gestureRecognizer.location(in: self)
    let color = getColorAtPoint(point: point)

    self.onColorDidChange?(color)
  }
}

Usage:

    let colorPickerView = ColorPickerView()
    colorPickerView.onColorDidChange = { [weak self] color in
        DispatchQueue.main.async {

            // use picked color for your needs here...
            self?.view.backgroundColor = color
        }

    }

    // add it to some view and set constraints
    ...
7
Michael Ros