#経緯
お絵かきアプリにカラーピッカーを付けたかったのでいろいろ探したのですが、ライブラリを使ったのしか出てこなかった。
...
...
じゃあ自力で1から作っちゃおう!
#環境
・macOS Catalina
・Xcode 11.7
・Swift 5
#Assetsに画像を追加
・千鳥柄の画像 × 2枚
※なくても良い。(alphaが1より低いときにわかりやすくするため)
1枚目 -> サイズ:220 × 37.5 、名前:chidori_s
2枚目 -> サイズ:290 × 290 、名前:chidori
・Hueスライダー用の画像
サイズ:300×13、名前:colors
画像は私のブログ記事から入手できます。
#全コード
ボタンを押すとColor Pickerが現れます。
import UIKit
class ViewController: UIViewController {
// 現在表示されているSlider
var sliderNow = ""
// Hue一時保存用
var hue: CGFloat!
// Color Picker を表示するボタン
var colorBtnNow: UIButton!
// Color Picker
var picker: UIView!
var sliderPallete: UIView!
// Color Pickerの上の白丸
let thumb = UIView()
// 変更前、現在の色プレビュー
var twoColors = [UIButton(), UIButton()]
// パレットのグラデーションレイヤー
var gradLayer = [CAGradientLayer(), CAGradientLayer()]
// Slider
var colorSlider = [UISlider(), UISlider(), UISlider(), UISlider()]
// Slider の色
let sliderGrad = [CAGradientLayer(), CAGradientLayer(), CAGradientLayer(), CAGradientLayer()]
// 現在のSliderの値
var numText = [UILabel(), UILabel(), UILabel(), UILabel()]
// 現在の色
var colorNow: UIColor!
// 現在のrgba/hsba
var changingColor = [CGFloat(), CGFloat(), CGFloat(), CGFloat()]
override func viewDidLoad() {
super.viewDidLoad()
let b = UIButton()
b.frame = CGRect(x: 10, y: 60, width: 80, height: 50)
b.backgroundColor = .systemOrange
b.setTitle("button", for: .normal)
view.addSubview(b)
b.addTarget(self, action: #selector(showColorPicker(sender:)), for: .touchUpInside)
}
@objc func showColorPicker(sender: UIButton) {
picker = UIView()
picker.backgroundColor = UIColor(white: 0.2, alpha: 1)
picker.layer.cornerRadius = 10
picker.frame = CGRect(x: 60, y: 120, width: 310, height: 615)
view.addSubview(picker)
colorBtnNow = sender
let parameters = ["RGB", "HLS"]
let seg = UISegmentedControl(items: parameters)
seg.frame = CGRect(x: 95, y: 20, width: 120, height: 35)
seg.backgroundColor = .gray
seg.selectedSegmentIndex = 0
seg.addTarget(self, action: #selector(segmentChanged), for: .valueChanged)
picker.addSubview(seg)
// 色プレビュー
let colorView = UIImageView()
colorView.image = UIImage(named: "chidori_s")
colorView.frame = CGRect(x: 45, y: 70, width: 220, height: 37.5)
colorView.clipsToBounds = true
colorView.layer.cornerRadius = 6
colorView.isUserInteractionEnabled = true
picker.addSubview(colorView)
var x: CGFloat = 0
for c in twoColors {
c.frame = CGRect(x: x, y: 0, width: 110, height: colorView.frame.height)
c.backgroundColor = sender.backgroundColor!
colorView.addSubview(c)
c.addTarget(self, action: #selector(setColor), for: .touchDown)
x = 110
}
// グラデーションパレット
let hsb = sender.backgroundColor!.hsb()
let gradView = UIButton(frame: CGRect(x: 10, y: 120, width: 290, height: 290))
gradView.addTarget(self, action: #selector(colorGradLayer_tapped), for: .allTouchEvents)
gradView.setBackgroundImage(UIImage(named: "chidori"), for: .normal)
hue = hsb[0]
func set_gradLayer(_ g: CAGradientLayer, colors: [CGColor], start: CGPoint) {
g.colors = colors
g.startPoint = start
g.endPoint = CGPoint(x: 0.0, y: 0.0)
g.frame = CGRect(x: 0, y: 0, width: 290, height: 290)
g.locations = [0, 1]
gradView.layer.addSublayer(g)
}
set_gradLayer(gradLayer[0],
colors: [hsb_color([hsb[0], 1, 1, hsb[3]]).cgColor,
UIColor(white: 1, alpha: hsb[3]).cgColor],
start: CGPoint(x: 1.0, y: 0.0))
set_gradLayer(gradLayer[1],
colors: [UIColor.black.cgColor, UIColor.clear.cgColor],
start: CGPoint(x: 0.0, y: 1.0))
thumb.center = CGPoint(x: 290*hsb[1], y: 290*(1-hsb[2]))
thumb.frame.size = CGSize(width: 24, height: 24)
thumb.layer.cornerRadius = 12
thumb.backgroundColor = .white
thumb.isUserInteractionEnabled = false
gradView.addSubview(thumb)
picker.addSubview(gradView)
setUpRGBView()
}
@objc func colorGradLayer_tapped(sender: UIButton, event: UIEvent) {
if let touch = event.touches(for: sender)?.first {
// タップした場所を取得
let loc = touch.location(in: sender)
if 0..<290 ~= loc.x { thumb.center.x = loc.x }
if 0..<290 ~= loc.y { thumb.center.y = loc.y }
changingColor[0] = hue
changingColor[1] = thumb.center.x/290
changingColor[2] = 1 - thumb.center.y/290
if sliderNow == "hls" {
for i in 1...2 { updateSlider(tag: i, value: changingColor[i]) }
} else {
changingColor = hsb_color(changingColor).rgb()
for i in 0...2 { updateSlider(tag: i, value: changingColor[i]) }
}
changeColor()
}
}
@objc func segmentChanged(_ segment: UISegmentedControl) {
switch segment.selectedSegmentIndex {
case 0: setUpRGBView()
case 1: setUpHLSView()
default: break
}
}
@objc func setColor(sender: UIButton) {
if sliderNow == "hls" { changingColor = sender.backgroundColor!.hsb()
} else { changingColor = sender.backgroundColor!.rgb() }
changeColor()
}
@objc func setUpHLSView() {
sliderNow = "hls"
setSlider(colors: colorBtnNow.backgroundColor!.hsb(), text: ["colors", "satu", "br", "alpha"])
colorSlider[0].setMinimumTrackImage(UIImage(named: "colors"), for: .normal)
colorSlider[0].setMaximumTrackImage(UIImage(named: "colors"), for: .normal)
for s in colorSlider[0].layer.sublayers! { s.removeFromSuperlayer() }
set_grad_hls(1...3)
}
func set_grad_hls(_ range: ClosedRange<Int>) {
for i in range {
var c = changingColor
if i != 3 { c[3] = 1 }
c[i] = 0
let color1 = UIColor(hue: c[0], saturation: c[1], brightness: c[2], alpha: c[3])
c[i] = 1
let color2 = UIColor(hue: c[0], saturation: c[1], brightness: c[2], alpha: c[3])
sliderGrad[i].colors = [color1.cgColor, color2.cgColor]
}
}
@objc func setUpRGBView() {
sliderNow = "rgb"
let color = colorBtnNow.backgroundColor!
setSlider(colors: color.rgb(), text: ["red", "green", "blue", "alpha"])
set_grad_rbg(0...3)
}
func set_grad_rbg(_ range: ClosedRange<Int>) {
for i in range {
var c = changingColor
if i != 3 { c[3] = 1 }
c[i] = 0
let color1 = UIColor(red: c[0], green: c[1], blue: c[2], alpha: c[3])
c[i] = 1
let color2 = UIColor(red: c[0], green: c[1], blue: c[2], alpha: c[3])
sliderGrad[i].colors = [color1.cgColor, color2.cgColor]
}
}
func setSlider(colors: [CGFloat], text: [String]) {
sliderPallete?.removeFromSuperview()
sliderPallete = UIView(frame: CGRect(x: 0, y: 410, width: 310, height: 250))
picker.addSubview(sliderPallete)
for i in 0..<colors.count {
colorSlider[i] = slider(tag: i, img: text[i], value: colors[i], superV: sliderPallete)
colorSlider[i].addTarget(self, action: #selector(slider_value_changed), for: .valueChanged)
changingColor[i] = colors[i]
}
for i in 0..<sliderGrad.count {
sliderGrad[i].frame = CGRect(x: 0, y: 0, width: 220, height: 20)
sliderGrad[i].startPoint = CGPoint(x: 0.0, y: 0.0)
sliderGrad[i].endPoint = CGPoint(x: 1.0, y: 0.0)
sliderGrad[i].locations = [0, 1]
sliderGrad[i].cornerRadius = 10
colorSlider[i].layer.addSublayer(sliderGrad[i])
}
}
func slider(tag: Int, img: String, value: CGFloat, superV: UIView) -> UISlider {
numText[tag].backgroundColor = UIColor.clear
numText[tag].font = .systemFont(ofSize: 16, weight: UIFont.Weight(6))
numText[tag].textColor = .white
superV.addSubview(numText[tag])
let s = UISlider()
s.tag = tag
s.tintColor = .clear
s.frame = CGRect(x: 65, y: 25+tag*40, width: 220, height: 20)
superV.addSubview(s)
if img == "alpha" {
numText[tag].text = String(format: "%.1f", value)
} else { numText[tag].text = "\(Int(value*255))"}
s.maximumValue = 0
s.maximumValue = 1
s.setValue(Float(value), animated: false)
numText[tag].frame = CGRect(x: 14, y: s.frame.origin.y-9, width: 60, height: 35)
return s
}
@objc func slider_value_changed(slider: UISlider) {
updateSlider(tag: slider.tag, value: CGFloat(slider.value))
changeColor()
changingColor[slider.tag] = CGFloat(slider.value)
let c = changingColor
// update grad layer
if sliderNow == "hls" {
hue = c[0]
switch slider.tag {
case 0, 3:
gradLayer[0].colors = [hsb_color(c).cgColor, UIColor(white: 1, alpha: c[3]).cgColor]
set_grad_hls(1...3)
case 1: thumb.center.x = 290*c[1]; break
case 2: thumb.center.y = 290*(1-c[2]); break
default: break
}
} else {
let hsb = rgb_color(c).hsb()
hue = hsb[0]
thumb.center = CGPoint(x: 290*hsb[1], y: 290*(1-hsb[2]))
gradLayer[0].colors = [hsb_color([hsb[0], 1, 1, hsb[3]]).cgColor,
UIColor(white: 1, alpha: hsb[3]).cgColor]
}
}
func updateSlider(tag: Int, value: CGFloat) {
colorSlider[tag].setValue(Float(value), animated: false)
if tag == 3 { numText[tag].text = String(format: "%.1f", value) }
else { numText[tag].text = "\(Int(value*255))" }
}
func changeColor() {
var color = rgb_color(changingColor)
if sliderNow == "hls" { color = hsb_color(changingColor) }
else { set_grad_rbg(0...3) }
colorBtnNow.backgroundColor = color
colorNow = color
twoColors[1].backgroundColor = color
}
func hsb_color(_ c: [CGFloat]) -> UIColor {
return UIColor(hue: c[0], saturation: c[1], brightness: c[2], alpha: c[3])
}
func rgb_color(_ c: [CGFloat]) -> UIColor {
return UIColor(red: c[0], green: c[1], blue: c[2], alpha: c[3])
}
}
extension UIColor {
func hsb() -> [CGFloat] {
var (h, s, b, a) = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return [h, s, b, a]
}
func rgb() -> [CGFloat] {
var (r, g, b, a) = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
getRed(&r, green: &g, blue: &b, alpha: &a)
return [r, g, b, a]
}
}
#まとめ
スライダーの色を動的に変えることで
直感的に操作できるUIになりましたね!