简单的快速颜色选择器弹出框(iOS)

Posted

技术标签:

【中文标题】简单的快速颜色选择器弹出框(iOS)【英文标题】:Simple swift color picker popover (iOS) 【发布时间】:2015-01-28 06:39:36 【问题描述】:

有没有一种简单的方法可以快速实现颜色选择器弹出框?是否有任何内置库或 UI 元素可以用于此目的?我看到了一些用 Objective-c 编写的颜色选择器,但它们已经有几年的历史了,我想知道是否有更新的东西。

【问题讨论】:

【参考方案1】:

这是我制作的一个,它非常简单。它只是一个轻量级的 UIView,允许您指定元素大小以防您想要阻塞区域(elementSize > 1)。它在界面生成器中绘制自己,因此您可以设置元素大小并查看结果。只需将界面构建器中的一个视图设置为此类,然后将自己设置为委托。它会告诉您何时有人点击或拖动它以及该位置的 uicolor。它将自己绘制到自己的边界,除了这个类之外不需要任何东西,不需要图像。

元素大小=1(默认)

元素大小=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)
                
    

【讨论】:

我喜欢您将代码放在 Stack Overflow 中,而不是链接到某个 github 项目。 这里缺少的一件事是灰度。 看起来很漂亮,但我无法将它调整到我现有的 Objective C 项目中。事实上,我在包含上述代码的 swift 文件中遇到错误。我已经导入了 UIKit,但在我看来 Swift 改变了它的语法?上面的代码与 Swift 2.3 兼容吗?我使用 Xcode 8.1。如果我对 Swift 不熟悉,我很抱歉。谢谢你的建议。 当我回到这一点时,我也会添加灰度。我们现在使用 swift 3.0,但如果有人想提供他们的翻译代码,我会更新它。 认为颜色返回错误是因为 longPressGesture 被调用两次错误计算,更新了代码【参考方案2】:

我继续用 Swift 编写了一个简单的颜色选择器弹出框。希望它会帮助其他人。

https://github.com/EthanStrider/ColorPickerExample

【讨论】:

我不知道这个答案的答案在哪里。 问。你是如何生成这个特定的调色板的?它看起来像是程序颜色和手工挑选颜色的组合。整体效果比平时使用的 RGB 彩虹光谱要好很多。 说实话,我想我在网上找到了一个调色板,它只是一个艺术家创作的图像。然后,我使用吸管工具手动对所有图像颜色进行采样并获得它们的 RGB 值。您可以在此处找到所有值:github.com/EthanStrider/ColorPickerExample/blob/master/… 谢谢 - 完美运行并转换为 swift 5 没有太多问题。【参考方案3】:

Swift 3.0 版本@joel-teply的回答:

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)
    

【讨论】:

【参考方案4】:

基于 Joel Teply 代码 (Swift 4),顶部带有灰色条:

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)
  

用法:

    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
    ...

【讨论】:

你能解释一下如何在VC中使用代码吗?或发布一个如何完成的示例? 更新了我的答案。为简单起见,去掉了委托。 MichaelRos,我按以下方式尝试了您的代码,但单击按钮时没有任何反应。我做错了什么?类 CustomizationViewController: UIViewController @IBAction func backgroundColorSelector(_ sender: Any) let colorPickerView = ColorPickerView() colorPickerView.onColorDidChange = [weak self] 颜色 in DispatchQueue.main.async self?.view.backgroundColor = color 通过添加 UIView 并分配给 ColorPickerView 类使其工作。谢谢。 @Yan 我还在视图控制器的顶部添加了 UIView,但我什么也没得到【参考方案5】:

Apple 现在在 ios 14 中实现了标准 UIColorPickerViewController 和关联的 UIColorWell,这是一个自动调出 UIColorPicker 以选择颜色的色板。

您可以通过使用 Xcode 12 或更高版本(针对 iOS 14+)创建 Swift App 项目来测试 ColorPicker,然后尝试以下简单代码:

import SwiftUI

struct ContentView: View 
    @State private var bgColor = Color.white

    var body: some View 
        VStack 
            ColorPicker("Set the background color",
                        selection: $bgColor,
                        supportsOpacity: true)
        
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(bgColor)
    

supportsOpacity 更改为false 以去掉不透明度滑块,只允许完全不透明的颜色。

ColorPicker 显示两种不同的选择模式:

没有 alpha 的颜色选择器:

【讨论】:

如何在 UIViewController 类中将它与常规 Swift 一起使用?【参考方案6】:

感谢您的起点。

我从那里得到它,并用自定义 UIView 和一些绘图代码编写了一个完整的 Color PickerViewController。

我制作了自定义 UIView @IBDesignable,以便它可以在 InterfaceBuilder 中呈现。

https://github.com/Christian1313/iOS_Swift_ColorPicker

【讨论】:

【参考方案7】:

ColorPickerViewImage

根据 Christian1313 的回答,我添加了深色

@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")

【讨论】:

【参考方案8】:

使用 Michael Ros 的答案,

如果您想通过objective-c viewcontroller 使用此视图,您可以简单地创建一个名为ColorPickerView 的新swift 文件,然后在情节提要上的viewcontroller 中添加一个uiview,然后选择ColorPickerView 作为它的类名。然后让您的视图控制器成为名称为@“colorIsPicking”的通知观察者

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateColor) name:@"colorIsPicked" object:nil];

ColorPickerView.swift 的代码

class ColorPickerView : UIView 

@objc public lazy var onColorDidChange: ((_ color: UIColor) -> ()) = 
           
    //send a notification for the caller view to update its elements if necessery
    NotificationCenter.default.post(name: Notification.Name("colorIsPicked"), object: nil)




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 = 10.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)

 

【讨论】:

【参考方案9】:

我快速添加了代码以使用 iOS 14 中发布的 Apple 颜色选择器为 ViewController 实现颜色选择器。确保您的部署信息至少为 IOS 14.0 或更高版本。参考https://developer.apple.com/documentation/uikit/uicolorpickerviewcontroller。

将颜色选择器委托添加到类中,并声明一个颜色选择器对象:

class ViewController: UIViewController, UIColorPickerViewControllerDelegate  
    
    let colorPicker = UIColorPickerViewController()

在 viewDidLoad 的最后,我将 colorPicker 委托设置为 self

        colorPicker.delegate = self

     // ends viewDidLoad

我使用一个颜色选择器为几个不同的对象选择颜色。当我向用户展示颜色选择器时,我将一个布尔标志设置为 true 以指示颜色选择器显示的原因。

        resetAllColorChangeFlags() // First make sure all the booleans are false for robust design

        changingScreenBackgroundColor = true

        present(colorPicker, animated: true, completion: nil)

我添加了处理 colorPickerViewControllerDidSelectColor 和 colorPickerViewControllerDidFinish 的 API

colorPickerViewControllerDidSelectColor 检查布尔标志,并为适当的对象设置颜色属性。如果颜色应用于图层中的边框颜色,则使用 cgColor。

    func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) 
        
        if changingScreenBackgroundColor
        
            self.view.backgroundColor = viewController.selectedColor
        
        if addingATintCircle
        
            pointerToThisTintView.backgroundColor = viewController.selectedColor
        
        if changingTextBackgroundColor
        
            pointerToTextObjectSelected.backgroundColor = viewController.selectedColor
        
        if changingTextColor
        
            pointerToTextObjectSelected.textColor = viewController.selectedColor
        
        if changingTextBorderColor
        
            pointerToTextObjectSelected.layer.borderColor = viewController.selectedColor.cgColor
        
        if changingPhotoBorderColor
        
            pointerToPhotoObjectSelected.layer.borderColor = viewController.selectedColor.cgColor
        
        if changingCameraBorderColor 
            cameraView.layer.borderColor = viewController.selectedColor.cgColor
        
     // ends colorPickerViewControllerDidSelectColor

colorPickerViewControllerDidFinish 仅用于重置我的所有布尔标志,这些标志指示颜色选择器为何呈现给用户。


    func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController)
    
        resetAllColorChangeFlags()

     // ends colorPickerViewControllerDidFinish

这是我的重置程序:

    func resetAllColorChangeFlags()
    
        changingFunTextColor = false
        changingFunTextFirstColorForGradient = false
        changingFunTextSecondColorForGradient = false
        changingScreenBackgroundColor = false
        changingTextBackgroundColor = false
        changingTextColor = false
        changingTextBorderColor = false
        changingPhotoBorderColor = false
        changingCameraBorderColor = false
        addingATintToAPhotoObject = false
        addingATintCircle = false
        addingAColorForGradients = false
        
     // ends resetAllColorChangeFlags

【讨论】:

以上是关于简单的快速颜色选择器弹出框(iOS)的主要内容,如果未能解决你的问题,请参考以下文章

无法调整 UIPopupController 显示图像

频谱颜色选择器在 Internet Explorer 中不起作用

带有弹出框的自定义选择,打开时更改颜色

如何使用选择属性更改 TextField 的 MUI 菜单弹出框的背景颜色?

引导日期选择器弹出窗口未显示突出显示的当前日期

当值最初在范围内设置时,日期选择器弹出格式不起作用