iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
ヒレガス本『Cocoa Programming for OS X (5th Edition)』のサンプルRGBWellをSwiftUIで実装してみた。
NSColorWellの操作で得られた色のRGBをNSSliderの値に反映し、NSSliderの操作で変更したRGBをNSColorWellに変異するというサンプルだ。
AppKitとUIKitでUI部品のクラスに差異があるが、SwiftUIではColorPickerやSlider、Colorと共通のクラスとなっている。ただし、Colorの内部の色情報はmacOSではNSColor、iOSではUIColorと差異があるのでRGBを値として取り出す場合は、macOSとiOSで異なる型になるようにする必要があった。
#if os(iOS)
typealias MyColor = UIColor
#elseif os(macOS)
typealias MyColor = NSColor
#else
#error("your os is not supported")
#endif
extension MyColor {
var rgba: (red: Double, green: Double, blue: Double, alpha: Double) {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return (Double(red), Double(green), Double(blue), Double(alpha))
}
}
extension Color {
var rgbValues:(red: Double, green: Double, blue: Double){
let rgba = MyColor(self).rgba
return (rgba.red, rgba.green, rgba.blue)
}
}
ColorPickerで得られるのはColor構造体だ。ColorPickerによって変更が発生したイベントを受け取るために@Observableのクラスを用意した。
@Observable
final class MyData {
var color = Color(.sRGB, red: 0.98, green: 0.9, blue: 0.2)
var red = 0.98
var green = 0.9
var blue = 0.2
}
ただし、Color構造体は更新不可なので、RGBスライダーで値が変更されても、ColorPickerが利用しているColorの値は更新できないので、スライダーによる変更は諦めた。
struct ContentView: View {
@State private var myData = MyData()
var body: some View {
HStack {
ColorPicker("", selection: $myData.color)
VStack {
HStack {
Text("R")
Slider(
value: $myData.red,
in: 0.0...1.0
).disabled(true)
}
HStack {
Text("G")
Slider(
value: $myData.green,
in: 0.0...1.0
).disabled(true)
}
HStack {
Text("B")
Slider(
value: $myData.blue,
in: 0.0...1.0
).disabled(true)
}
}
.padding()
}.padding().onAppear {
trackingColor(myData)
}
}
nonisolated private func trackingColor(_ myData: MyData) {
withObservationTracking {
_ = myData.color
} onChange: {
let rgb = myData.color.rgbValues
myData.red = rgb.red
myData.green = rgb.green
myData.blue = rgb.blue
trackingColor(myData)
}
}
}
ヒレガス本では、
let newColor = NSColor(calibratedRed: CGFloat(r),
green: CGFloat(g),
blue: CGFloat(b),
alpha: CGFloat(a))
colorWell.color = newColor
とNSColorWellのcolorを更新することで値が変更していたが、これは少々トリッキーなコードだがサンプルだったので、と考えて、SwiftUIで同じことを行うことは諦めた。