Простое быстрое всплывающее окно выбора цвета (iOS)

Есть ли простой способ реализовать всплывающее окно выбора цвета в Swift? Существуют ли какие-либо встроенные библиотеки или элементы пользовательского интерфейса, которые я мог бы использовать для этой цели? Я видел некоторые палитры цветов, написанные на Objective-C, но им было несколько лет, и мне было интересно, есть ли что-то более новое.


person Ethan Strider    schedule 29.11.2014    source источник


Ответы (9)


В iOS 14 Apple реализовала стандартный UIColorPickerViewController и связанный с ним UIColorWell — это образец цвета, который автоматически вызывает UIColorPicker для выбора цвета.

Вы можете протестировать ColorPicker, создав проект приложения Swift с Xcode 12 или более поздней версии, предназначенный для iOS 14+, а затем попробуйте этот простой код:

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 показывает два разных режима выделения:

Цветоподборщик iOS 14

ColorPicker без альфы:

iOS ColorPicker без альфа-версии

person snibbe    schedule 23.08.2020
comment
Как я могу использовать его с обычным Swift в классе UIViewController? - person Ahmadreza; 13.11.2020

Вот один из них, который я сделал настолько простым, насколько это возможно. Это просто облегченный UIView, который позволяет указать размер элемента, если вам нужны заблокированные области (elementSize > 1). Он рисуется в построителе интерфейса, поэтому вы можете установить размер элемента и увидеть последствия. Просто установите один из ваших представлений в построителе интерфейса для этого класса, а затем установите себя в качестве делегата. Он сообщит вам, когда кто-то коснется или перетащит его, и uicolor в этом месте. Он будет тянуться к своим границам, и кроме этого класса ничего не нужно, изображение не требуется.

Размер элемента = 1 (по умолчанию) размер элемента=1

Размер элемента = 10
размер элемента=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)
        }        
    }
}
person Joel Teply    schedule 07.12.2015
comment
Мне нравится, что вы разместили код прямо здесь, в Stack Overflow, а не ссылаетесь на какой-то проект github. - person Suragch; 10.06.2016
comment
Единственное, чего здесь не хватает, так это шкалы серого. - person Suragch; 10.06.2016
comment
Выглядит красиво, но я не смог адаптировать его к моему существующему проекту Objective C. Фактически, я получаю ошибки в файле swift, который имеет приведенный выше код. Я импортировал UIKit, но мне кажется, что Swift изменил свою грамматику? Совместим ли приведенный выше код со Swift 2.3? Я использую Xcode 8.1. Мои извинения, если я не понимаю, поскольку я новичок в Swift. Спасибо за совет. - person tsuyoski; 29.11.2016
comment
Когда я вернусь к этому, я также добавлю оттенки серого. Сейчас мы используем swift 3.0, но если кто-то захочет предоставить свой переведенный код, я обновлю его. - person Joel Teply; 21.05.2017
comment
Выяснил, что цвет возвращается неправильно, потому что longPressGesture вызывается дважды с неправильным вычислением, немного обновил код - person Tj3n; 16.06.2017
comment
Спасибо за этот хороший кусок кода. У меня были некоторые проблемы с getPointForColor при вызове из viewDidLoad: возвращаемая точка не располагалась в нужном месте в цветовом представлении. Бывает, что нам лучше вызвать его из viewDidLayoutSubviews, как только все операции автоматического изменения размера макета будут выполнены. - person Laurent Maquet; 07.07.2017
comment
По какой-то причине я получаю nil для context = UIGraphicsGetCurrentContext(). Не уверен, почему я не получаю контекст. Когда самое подходящее время для вызова метода рисования? Я использую версию Swift 3 ниже, НО со Swift 4. - person john; 05.11.2017
comment
Это здорово и отлично работает в Swift 4.2. Хотя как именно вы используете значение цвета? Он появляется как UIExtendedSRGBColorSpace. Есть ли способ преобразовать это в UIColor? - person Adam S.; 15.02.2019

Я пошел дальше и написал простое всплывающее окно выбора цвета в Swift. Надеюсь, это поможет кому-то еще.

https://github.com/EthanStrider/ColorPickerExample

Снимок экрана средства выбора изображений

person Ethan Strider    schedule 30.11.2014
comment
Я не знаю, где ответ в этом ответе. - person khunshan; 23.05.2016
comment
В. Как вы создали эту конкретную палитру? Это выглядит как сочетание процедурных и подобранных вручную цветов. Общий эффект намного лучше, чем обычно используемый спектр радуги RGB. - person Womble; 26.06.2018
comment
Честно говоря, я думаю, что нашел цветовую палитру в Интернете, которая была просто изображением, созданным художником. Затем я вручную выбрал все цвета изображения с помощью инструмента «Пипетка» и получил их значения RGB. Вы можете найти все значения здесь: github.com/EthanStrider/ColorPickerExample /блоб/мастер/ - person Ethan Strider; 17.07.2018
comment
Спасибо - отлично работает и конвертируется в Swift 5 без особых проблем. - person Jeremy Andrews; 30.06.2019

Версия 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)
    }
}
person Christen Ward    schedule 05.01.2017

На основе кода 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
    ...
person Michael Ros    schedule 17.11.2017
comment
не могли бы вы объяснить, как использовать код в ВК? или опубликовать пример того, как это делается? - person Fullpower; 06.07.2019
comment
обновил мой ответ. Избавился от делегата для простоты. - person Michael Ros; 07.07.2019
comment
MichaelRos, я попробовал ваш код следующим образом, но ничего не произошло, когда я нажал кнопку. Что я сделал не так? class CustomizationViewController: UIViewController { @IBAction func backgroundColorSelector(_ sender: Any) { let colorPickerView = ColorPickerView() colorPickerView.onColorDidChange = {[weak self] color in DispatchQueue.main.async { self?.view.backgroundColor = color } } } } - person Yan; 14.03.2020
comment
Получил работу, добавив UIView и присвоив классу ColorPickerView. Спасибо. - person Yan; 16.03.2020
comment
@Yan Я также добавил UIView в контроллер Top of View, но ничего не получил - person Madhu_Nani; 01.10.2020
comment
Вы можете обернуть его в UIViewController следующим образом: class ColorPickerController: UIViewController { var setColor: ((UIColor) -> Void)?; init() { super.init(nibName: nil, bundle: nil) }; required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }; override func loadView() { let colorPickerView = ColorPickerView(); colorPickerView.onColorDidChange = setColor; view = colorPickerView } } (замените ; новыми строками) - person aheze; 01.11.2020

Спасибо за отправную точку.

Я взял это оттуда и написал полный Color PickerViewController с пользовательским UIView и некоторым кодом рисования.

Я сделал собственный UIView @IBDesignable, чтобы его можно было отобразить в InterfaceBuilder.

https://github.com/Christian1313/iOS_Swift_ColorPicker

person Christian1313    schedule 04.03.2015

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

}

person extrum    schedule 22.10.2019

используя ответ Майкла Роса,

Если вы хотите использовать это представление с помощью контроллера представления target-c, вы можете просто создать новый быстрый файл с именем ColorPickerView и добавить uiview в свой контроллер представления на раскадровке и выбрать ColorPickerView в качестве имени класса. затем сделайте свой контроллер представления наблюдателем уведомлений с именем @colorIsPicked

[[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)
}
 }
person Mona    schedule 21.11.2020

Я быстро добавил код для реализации палитры цветов для ViewController с помощью палитры цветов Apple, выпущенной в IOS 14. Убедитесь, что информация о развертывании соответствует версии не ниже 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)

Я добавил API для обработки colorPickerViewControllerDidSelectColor и colorPickerViewControllerDidFinish.

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
person user3408691    schedule 25.12.2020