SwiftUI Tutorialsに登場したProperty Wrappersまとめ
はじめに
自分がApple公式のSwiftUI Tutorialsを通して学んだ時に登場したProperty Wrappersがそれぞれどういう役割かとかを忘れちゃいそうなので備忘のために書き残すことにしました。
watchOSやmacOSのAppを作る予定は現状なかったので、Chapter3までに登場したProperty Wrappersの紹介になります。
これから同じくSwiftUIを始めて勉強する方々の参考になればと思います。
Property Wrappersとは
Swift5.1で実装された機能です。
プロパティのget/setに関わる制御を共通化するような仕組みです。
この後紹介する@State
やEnvironmentObject
はProperty Wrappersですし、自分で新たなものを定義することが可能です。
SwiftUI Tutorialsに登場したProperty Wrappers
@State
State | Apple Developer Documentation
通常SwiftUIのViewはstructで定義していくためプロパティの更新ができないが、@State
をつけて宣言することでそのプロパティの値の読み書きができる。
@State
で宣言したプロパティはViewのbodyもしくはViewから呼び出されるメソッドからのみアクセスする必要があるとAppleから推奨されているので、基本的にはprivate
をつけて宣言するのが一般的のようだ。
Viewにプロパティの値を渡したい時は$
を変数名につけることで実現できる。
struct ToggleView: View {
/// トグルのスイッチの状態(初期値: false)
@State private var isOn = false
var body: some View {
// isOnの値を監視($をつける!)
Toggle(isOn: $isOn) {
Text("スイッチを切り替える")
}
}
}
これで下記画像のようにスイッチの状態を切り替えることができるようになる。
@Publishedと@EnvironmentObject
Published | Apple Developer Documentation
EnvironmentObject | Apple Developer Documentation
チュートリアルでは、ObservableObject
を準拠しているクラスのプロパティに@Published
を付けることで監視側がデータの変更を取得できるようにしている。
@EnvironmentObject
をつけてプロパティを宣言することで、複数のViewに共通のインスタンスを渡して値を監視させられます。
下記コードでは親のContentView
内で.environmentObject(User())
でインスタンスを渡して子ビューであるTextView
とButtonsView
で値を参照しています。
final class User: ObservableObject {
@Published var name = "Taro"
@Published var age = 18
}
struct ContentView: View {
var body: some View {
VStack {
TextView()
ButtonsView()
}
.environmentObject(User())
}
}
struct TextView: View {
@EnvironmentObject var user: User
var body: some View {
Text("I'm \(user.name).")
Text("I'm \(user.age) years old.")
}
}
struct ButtonsView: View {
@EnvironmentObject var user: User
var body: some View {
HStack {
Button(action: {
self.user.age += 1
}) {
Text("歳をとる")
}
Button(action: {
self.user.age -= 1
}) {
Text("若返る")
}
}
}
}
実行したアプリでは、ボタンを押したときのユーザの年齢が動的に変更される。
これで一応動くのですが、environmentObjectはアプリ全体で共通使用するデータのやりとりをするので、正しくはContentViewから.environmentObject(User())
を削除し、代わりにXXXApp.swift
内に下記のようにして記述するのが適切なようです。
そうでないと、print文で確認すると更新はされているがアプリの見た目は変わらないというような、正常に動作しないことがありました。
struct SampleSwiftUIApp: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(User()) //ここで共通する
}
}
}
@StateObject
StateObject | Apple Developer Documentation
一度初期化すると、フレームワーク側で@stateObject
がついたプロパティの値が保持され続けるのでビューの再描画が発生しても値が初期化されなくなります。
@ObservedObject
を使用していると初期化されてしまうので、再描画されても変わってほしくない場合は@stateObject
を使うと良さそうです。
チュートリアルを見る感じだと、こちらも初期化してそのままプロパティを保持して欲しいことを考えると起動直後に宣言して.environmentObject
で渡すのが適しているという感じでしょうか。
struct SampleSwiftUIApp: App {
@StateObject var hogeData = HogeData() // 任意の保持され続けて欲しいデータ
var body: some Scene {
WindowGroup {
ContentView().environmentObject(hogeData) // そのデータを渡す
}
}
}
@Binding
Binding | Apple Developer Documentation
データを格納するプロパティと、そのデータを変更/更新するビューを双方向に接続します。
ということは、@Binding
で設定したプロパティが更新されれば、それと接続している他のビューも更新されるということになるようです。
final class ToggleState: ObservableObject {
@Published var isOn = false
}
struct ContentView: View {
@EnvironmentObject var toggle: ToggleState
var body: some View {
VStack {
ToggleAView(isOn: $user.isOn)
ToggleBView(isOn: $user.isOn)
}
}
}
struct ToggleAView:View {
@Binding var isOn: Bool
var body: some View {
Toggle("スイッチA", isOn: $isOn)
}
}
struct ToggleBView:View {
@Binding var isOn: Bool
var body: some View {
Toggle("スイッチB", isOn: $isOn)
}
}
上記のコードは同じプロパティをBindingしているので、片方のトグルを切り替えると、その状態がもう片方の状態にも同期されて切り替わるようになります。
@Environment
Environment | Apple Developer Documentation
EnvironmentValues
に定義されているビューの環境の設定値を取得/更新したいときに使います。
下記コードは取得の例です。
struct ContentView: View {
@Environment(\.timeZone) var timeZone
@Environment(\.calendar) var calendar
@Environment(\.locale) var locale
var body: some View {
VStack {
Button(action: {
print(timeZone) // Asia/Tokyo (current)
print(calendar) // gregorian (current)
print(locale) //en (current)
}) {
Text("button")
}
}
}
}
EnvironmentValues
は上記のようにtimeZoneやcalendar等の値意外にも豊富に定義されています。
また、自分でEnvironmentValues
の値を新たに定義することもできるそうです。
おわりに
ひとまず、SwiftUI Tutorialsに登場するProperty Wrappersをそれぞれ調べてみて、それぞれがどういうものなのかはある程度把握できました。
他の記事を見る感じだと、今回紹介したものがやはりよく紹介されているのを見かけるので頻繁に活用していくのだと思われます。
これから実際にSwiftUIを使ったコーディングをしていくと思うので、これらを駆使しながら実装していきます。
Property Wrappersを複数種類組み合わせたときの挙動とかのケーススタディができていないのと、@ObservedObject
や@AppStorage
等の他のProperty Wrappersのまとめができていなかったりするので、こういうときどれを使うのが良いかがまだ不明な状態ですが、また色々調べながら進めて行けたらと思います。
参照
※チュートリアルと各Property Wrappersのリンクは既に上記内で共有しているので省略させていただいています。
Author And Source
この問題について(SwiftUI Tutorialsに登場したProperty Wrappersまとめ), 我々は、より多くの情報をここで見つけました https://qiita.com/ta9yamakawa/items/e9cc7a66cf1458695fdc著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .