SwiftUIでビューを作成する際、@Stateを使ってビューの状態を管理することが多いです。しかし、@State変数は通常privateとして扱われるため、プレビュー時に直接アクセスして変更することができません。この記事では、privateな@State変数にアクセスして、プレビュー時に表示内容や表示状態を変更する方法を紹介します。
問題の背景
通常、@State変数はprivateであり、外部から直接変更することができません。そのため、プレビュー時に特定の状態を再現したい場合でも、その状態にアクセスできず、手動で状態を変更しなければなりません。
例えば、以下のようなコードがあるとします。
struct MainView: View {
@State private var isTextVisible: Bool = false
@State private var displayText: String = "Default Text"
var body: some View {
VStack {
if isTextVisible {
Text(displayText)
.font(.headline)
.padding()
}
}
}
}
このコードでは、isTextVisibleがfalseであるため、プレビュー時にはテキストが表示されません。しかし、プレビューでテキストが表示される状態を確認したい場合、この変数にアクセスする方法が必要です。
また、Textの内容もprivateでアクセスできないです。
解決策: extensionを使ったapplyメソッド
この問題を解決するために、extensionを使ってビューにapplyメソッドを追加します。このメソッドを使うことで、プレビュー時にprivateな変数を簡単に設定できるようになります。
extension MainView {
func apply(isVisible: Bool, text: String) -> Self {
var copy = self
copy._isTextVisible = State(initialValue: isVisible)
copy._displayText = State(initialValue: text)
return copy
}
}
本番環境で拡張したコードの情報を含めたくない場合は"#if DEBUG #endif DEBUG"で囲むことも視野。
また、Previewのみ動かす方法として
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
という文を条件として使用することができる。
全体のコードと実行結果
import SwiftUI
struct MainView: View {
@State private var isTextVisible: Bool = false
@State private var displayText: String = "Default Text"
var body: some View {
VStack {
if isTextVisible {
Text(displayText)
.font(.headline)
.padding()
}
}
}
}
extension MainView {
func apply(isVisible: Bool, text: String) -> Self {
var copy = self
copy._isTextVisible = State(initialValue: isVisible)
copy._displayText = State(initialValue: text)
return copy
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView().apply(isVisible: true, text: "This text is visible in Preview!")
}
}
simulatorでの実行結果
これだと分かりにくいので、isTextVisibleをtrueにして実行
そして、Previewでは
これを活用してsnapshot testなどを進めていきたい。