soranoba
soranoba Author of soranoba.net
programming

SwiftUIでPreviewの内容が実体と異なる場合がある話

SwiftUIにおいて、そのView(A)のPreviewでは正しいのに、Aを使ったView(B)のPreviewでは正しくないという現象に遭遇した。
まずは結論から。

結論

同名のViewがあった場合に、Previewの場所によって異なるViewになるので、同名の構造体・クラスは明確に指定するべき。

発生条件と再現方法

再現コードを公開しているので、コード全体を見たい場合はこちらを参照してください

public extension Form {
    struct Group<Content: View>: View {
        // 省略
    }
}

public extension Form {
    struct Announce<Content: View>: View {
        private let content: Content

        public init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }

        public var body: some View {
            Group { // このGroupが`Form.Group`になるか`SwiftUI.Group`になるかが変わる
                content
            }
        }
    }
}

#Preview {
    Form.Announce {
        Text("Does it use SwiftUI.Group?")
    }
}

ここでForm.Announce.body内で使用されているGroupは近いところに定義されている物が選ばれる為、Form.Groupとなるはずだった。
しかし、このPreviewを確認してみると、SwiftUI.Groupが使用されている。

SwiftUI.Groupが使われている様子

class ViewController: UIHostingController<AnyView> {
    init() {
        super.init(rootView: AnyView(Form.Announce {
            Text("Does it use SwiftUI.Group?")
        }))
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

次に、AnyViewでラップしてはいるが、同様の内容を実際に表示してみる。
すると、以下のような画面になり、先ほどとは見た目が異なる結果となる。これは、Form.Announce.body内で使用されているGroupForm.Groupとして解決された結果だ。

Form.Groupが使われている様子

色々と試した結果、Previewは常にSwiftUIのViewを優先するということもないようで、何か他にも条件がありそうではある。
という訳で、同名の構造体・クラスには気をつけましょうという話でした。

(Updated: )

comments powered by Disqus