トップ «前の日記(2024-07-30) 最新 編集

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|

2024-08-09 [SwiftUI] UI部品

ヒレガス本『Cocoa Programming for OS X (5th Edition)』のサンプルRGBWellをSwiftUIで実装してみた。

RGBWell

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で同じことを行うことは諦めた。

_ 【ソースコード】

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/multiplatform/RGBWell - GitHub

トップ «前の日記(2024-07-30) 最新 編集