如何将SwiftUI View作为属性传入类中?(AlertPresenter实现)
解决SwiftUI AlertPresenter中传递可选View的问题
核心问题说明
SwiftUI的View是带关联类型的协议,无法直接作为ObservableObject的属性类型,因此需要用AnyView擦除类型来存储;同时利用@ViewBuilder修饰参数,让调用语法更贴合SwiftUI的使用习惯,还能实现可选View的需求。
修正后的AlertPresenter代码
import SwiftUI class AlertPresenter: ObservableObject { @Published var title: String = "" @Published var isPresented: Bool = false @Published var actions: AnyView? = nil @Published var messageView: AnyView? = nil func present( title: String, @ViewBuilder actions: () -> some View, @ViewBuilder message: () -> some View? = { nil } ) { self.title = title self.isPresented = true self.actions = AnyView(actions()) self.messageView = message().map(AnyView.init) } func dismiss() { self.isPresented = false self.title = "" self.actions = nil self.messageView = nil } }
- 用
AnyView?存储可选的操作视图和消息视图,默认值设为nil present函数用@ViewBuilder修饰闭包参数,允许直接传入SwiftUI原生View语法- 消息视图参数设置默认值为
nil,实现可选效果
修正ParentView的环境对象配置
struct ParentView: View { // 用@StateObject管理Presenter生命周期,再注入环境 @StateObject private var alertPresenter = AlertPresenter() var body: some View { ChildView() .environmentObject(alertPresenter) .alert(title: alertPresenter.title, isPresented: $alertPresenter.isPresented) { // 解包可选视图,空值时用EmptyView占位 alertPresenter.actions ?? EmptyView() } message: { alertPresenter.messageView ?? EmptyView() } } }
- 用
@StateObject初始化AlertPresenter,确保其生命周期与视图绑定 - 在
alert闭包中处理可选视图,符合SwiftUI的参数要求
调用示例(子视图中使用)
struct ChildView: View { @EnvironmentObject private var alertPresenter: AlertPresenter var body: some View { Button("Present Alert") { alertPresenter.present(title: "This is the alert title") { Button("OK") { alertPresenter.dismiss() } Button("Cancel", role: .cancel) {} } message: { Text("This is the alert message") } } // 展示无消息的弹窗 Button("Present Alert Without Message") { alertPresenter.present(title: "No Message Alert") { Button("Got it") { alertPresenter.dismiss() } } // 省略message参数即可 } } }
- 调用
present时直接用SwiftUI原生View语法传入操作和消息 - 支持省略message参数,实现可选View的需求
内容的提问来源于stack exchange,提问作者Harry CHOI
相关产品推荐
相关产品推荐

