Sponsor Link
環境&対象
- macOS Monterey 12.1 beta3
- Xcode 13.2 beta2
- iOS 15
同じ View Modifier を適用したい
SwiftUI でアプリの UI を作成していると、条件によって切り替わるビューに 同じ View Modifier を適用したいことがあります。
例えば、以下では モードによって、Text と TextField が切り替わるのですが、どちらの場合でもサイズを一定にするために、.frame を指定しています。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2021/12/06
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
enum ViewMode {
case view
case edit
}
@State private var viewMode = ViewMode.view
@State private var text: String = "Hello, world!"
var body: some View {
VStack {
if viewMode == .view {
Text(text)
.frame(width: 150, height: 80) // (1)
} else {
TextField("text", text: $text)
.textFieldStyle(.roundedBorder)
.frame(width: 150, height: 80) // (2)
}
Button(action: {
self.viewMode = viewMode == .view ? .edit : .view
}, label: {
VStack {
Text("Toggle ViewMode")
}
})
.padding()
Text("Current: \(viewMode == .view ? "View" : "Edit") mode")
}
}
}
- Text の表示サイズを 150×80 にするために指定
- TextField の表示サイズを 150×80 にするために指定
同じサイズにするために指定しているので、(1), (2) で同じ値を指定していなければいけません。
レイアウト調整等で数値を変更することも考えられるので、できれば 1ヶ所で指定できる方が嬉しいです。
どうやって1ヶ所にまとめるか考えてみます。
案1:if に対して View Modifier を適用する
if に対して、.frame が指定できると、以下のように記述できるようになり、1ヶ所で記述できることになります。
if viewMode == .view {
Text(text)
} else {
TextField("text", text: $text)
.textFieldStyle(.roundedBorder)
}
.frame(width: 150, height: 80)
ですが、残念ながら、以下のようなエラーとなります。
cannot infer contextual base in reference to member 'frame'
if の評価結果が View になるわけではないので、エラーとなるのは理解できます。
案2:変数で同じ値にする
数値を直接記述してしまっているために、一方だけ変更して 他方を変更し忘れるということが発生し得るので、変数にしてしまうのも1つです。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2021/12/06
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
enum ViewMode {
case view
case edit
}
@State private var viewMode = ViewMode.view
@State private var text: String = "Hello, world!"
// (1)
let width: CGFloat = 150
let height: CGFloat = 80
var body: some View {
VStack {
if viewMode == .view {
Text(text)
// (2)
.frame(width: width, height: height)
} else {
TextField("text", text: $text)
.textFieldStyle(.roundedBorder)
// (3)
.frame(width: width, height: height)
}
Button(action: {
self.viewMode = viewMode == .view ? .edit : .view
}, label: {
VStack {
Text("Toggle ViewMode")
}
})
.padding()
Text("Current: \(viewMode == .view ? "View" : "Edit") mode")
}
}
}
- .frame で使用する値を変数として定義しておきます。(実行中に変更する必要がなければ、@State の必要はありません)
- 事前に定義された変数を使用して、Text に .frame を指定します。
- 事前に定義された変数を使用して、TextField に .frame を指定します。
この方法の問題点は、動的に変更するものでもないのに、変数を定義していることで、よりコードが複雑に見えてしまうことです。
案3:ViewModifier を適用するビューを 内部関数から取得する
SwiftUI では、複雑になってきた ビューの一部を 外部ビューに移動させて、管理しやすくしたり 再利用しやすくしたりします。
同様の方法で 同じ View Modifier を適用することができます。ただし、不必要に 外部ビューにしてしまうと 逆に管理しづらくなってしまうこともあるため、struct 内のメソッドが ビューを返すようにします。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2021/12/06
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
enum ViewMode {
case view
case edit
}
@State private var viewMode = ViewMode.view
@State private var text: String = "Hello, world!"
var body: some View {
VStack {
// (1)
textView()
.frame(width: 150, height: 80)
Button(action: {
self.viewMode = viewMode == .view ? .edit : .view
}, label: {
VStack {
Text("Toggle ViewMode")
}
})
.padding()
Text("Current: \(viewMode == .view ? "View" : "Edit") mode")
}
}
// (2)
@ViewBuilder
func textView() -> some View {
if viewMode == .view {
Text(text)
} else {
TextField("text", text: $text)
.textFieldStyle(.roundedBorder)
}
}
}
- 内部関数 textView が View を返してくるので、.frame を指定します
- textView は、条件に応じた View を返します(このとき @ViewBuilder 指定するのがポイントです)
こうすることで、サイズ指定の .frame を1ヶ所に記述することができるようになります。
まとめ:条件で切り替えるビューに同じ ViewModifier を適用する方法
ここまでに見てきた方法で、条件文で切り替えられる View に対して、1ヶ所で View Modifier を指定することができるようになります。
なお、特定の View にのみ適用できる View Modifier を扱う場合は、その View Modifier を適用できる View に制限することが必要となります。
- @ViewBuilder 指定で、View を返すメソッドを作り、その中で条件により適切な View を返す
- メソッドから返る View にたいして、View Modifier を適用することで、1ヶ所で ViewModifier を記述することができるようになる
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI 学習におすすめの本
SwiftUI 徹底入門
SwiftUI は、グラフィカルなライブラリということもあり、文字だけのテキストよりは、画像が多く入れられた書籍を読むと理解が進みやすいです。
自分で購入した中でおすすめできるものとしては、以下のものです。
2019 年発表の SwiftUI 1.0 相当を対象にしているので、2020/2021 に追加された一部の機能は、説明されていません。
ですが、SwiftUI 入門書としては、非常によくできていますし、わかりやすいです。 この本で学習した後に、追加分を学習しても良いと思います。
SwiftUIViewsMastery
英語での説明になってしまいますが、以下の本もおすすめです。
1ページに、コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
Sponsor Link