Property Wrappersを理解する
Published by @SoNiceInfo at 6/24/2020
SwiftUIで変数の状態を監視するにはProperty Wrappers(プロパティラッパー)を使います。
Property Wrappersを使うことで、複数のViewで変数を共有したり、変数が更新されるとViewを再描画してくれます。
SwiftUIでは以下の種類のProperty Wrappersが用意されています。
Property Wrappers | 役割 | 使い所(例) |
---|---|---|
@State | Viewの状態と変数の値を監視する | Sheet(モーダル)の状態監視 |
@Binding | 他のViewの@State 変数を参照・変更する | Sheet(モーダル)へ変数を渡す |
@ObservedObject | 構造体・クラスの変更を監視する。 | ユーザ情報など複数のプロパティを持つクラスを扱う。親Viewからインスタンスを受け取る。 |
@Environment | 予め定義されたViewの環境情報を取得する | 画面のサイズを扱う |
@EnvironmentObject | 構造体・クラスの変更を監視する | 複数のプロパティを持つクラスを扱う |
@State
@State
ではひとつの値を保持することができます。
また@State
はひとつのView内でのみ使用することが推奨されています。
ここではView内でSheet(モーダル)遷移状態を保持するために使っています。
sheetにisPresentedのBindingを渡すために$(ドルマーク)をつけています。
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
@State private var isPresented: Bool = false
@State private var catName: String = "Tora"
var body: some View {
VStack {
Text(catName)
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
VStack {
TextField("My cat's name is...", text: self.$catName)
.multilineTextAlignment(.center)
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@Binding
@Binding
を利用すると他のViewの@State
変数を受け継ぐことができます。@Binding
した変数は変更すると他のViewにも変更が通知されます。@Binding
する変数を持つViewには$(ドルマーク)をつけてBinding型にします。
このコードはSheet内のViewを分けていますが上のコードと同じ動きをします。
//
// ContentView.swift
//
import SwiftUI
struct SheetView: View {
@Binding var isPresented: Bool
@Binding var catName: String
var body: some View {
VStack {
TextField("My cat's name is...", text: $catName)
.multilineTextAlignment(.center)
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView: View {
@State private var isPresented: Bool = false
@State private var catName: String = "Tora"
var body: some View {
VStack {
Text(catName)
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
SheetView(isPresented: self.$isPresented, catName: self.$catName)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@ObservedObject
@ObservedObject
は構造体やクラスの値を監視するときに有効です。ObservableObject
プロトコルに準拠したクラスといっしょに使うことで、複数のインスタンスを生成できますObservableObject
内では@Published
を使うことで変更が複数のViewに通知されます。
//
// ContentView.swift
//
import SwiftUI
class CatModel: ObservableObject {
@Published var name : String
@Published var age : String
init (name: String, age: String) {
self.name = name
self.age = age
}
}
struct ResultView: View {
@Binding var isPresented: Bool
@ObservedObject var cat: CatModel
var body: some View {
VStack {
Text("My cat is: \(cat.name), \(cat.age)")
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView: View {
@State private var isPresented: Bool = false
@ObservedObject var cat = CatModel(name: "Mike", age: "3")
var body: some View {
VStack {
HStack {
Text("Name: ")
TextField("Input name", text: $cat.name)
}
HStack {
Text("Age: ")
TextField("Input age", text: $cat.age)
}
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
ResultView(isPresented: self.$isPresented, cat: self.cat)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@Environment
@Environment
はフォントやダークモード・ライトモード等の環境情報を取得することができます。
取得できる値はEnvironmentValues - SwiftUI | Apple Developer Documentation
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
@Environment(\.font) var font
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
Text("Text")
.font(font)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
HStack {
ContentView()
.environment(\.font, .body)
ContentView()
.environment(\.font, .largeTitle)
}
}
}
@EnvironmentObject
@EnvironmentObject
は複数の値を保持することができます。
すべてのViewで使いたいときには@EnvironmentObject
が有効です。@ObservedObject
が複数のインスタンスを作成できるのに対し、@EnvironmentObject
で作成できるインスタンスはひとつです。
さらにインスタンスは外部から渡されてすべてのViewで共有されるという特徴があります。@EnvironmentObject
のインスタンスはenvironmentObject
でインスタンスを渡します。
iOS 13対応
//
// SceneDelegate.swift
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Create the SwiftUI view that provides the window contents.
// 大事!!
let contentView = ContentView().environmentObject(CatModel())
...
}
}
iOS 14対応
import SwiftUI
@main
struct iOS14App: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(CatModel())
}
}
}
共通
//
// ContentView.swift
//
import SwiftUI
class CatModel: ObservableObject {
@Published var name = ""
@Published var age = ""
}
struct ContentView: View {
@State var isPresented: Bool = false
@EnvironmentObject var cat : CatModel
var body: some View {
VStack {
HStack {
Text("Name: ")
TextField("Input name", text: $cat.name)
}
HStack {
Text("Age: ")
TextField("Input age", text: $cat.age)
}
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
ResultView(isPresented: self.$isPresented)
.environmentObject(self.cat)
}
}
}
struct ResultView: View {
@Binding var isPresented: Bool
@EnvironmentObject var cat : CatModel
var body: some View {
VStack {
Text("My cat is: \(cat.name), \(cat.age)")
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}