无法在 SwiftUI 中使用 @ViewBuilder 返回类型 - 使用通用视图而不是 AnyView

问题描述 投票:0回答:1

首先,此代码的错误之处在于它会产生编译错误:

"Cannot convert the return expression of type '_ConditionalContent<MyView, MyView>' to the return type 'MyView'."
每个分支都隐式返回
MyView
类型。 其次,有没有一种方法可以绕过使用
AnyView
并利用泛型编写代码?

import SwiftUI

private struct ControllerView: View {
    let rootView: () -> MyView
    @Binding var isGreen: Bool
    
    init(isGreen: Binding<Bool>,@ViewBuilder rootView: @escaping () -> MyView) {
        self.rootView = rootView
        self._isGreen = isGreen
    }
    
    var body: some View {
        ZStack {
            rootView()
            
            Button {
                isGreen.toggle()
            } label: {
                Text("Change")
            }
        }
    }
}

private struct GreenView: View  {
    var body: some View {Color.green.ignoresSafeArea()}
}
private struct OrangeView: View  {
    var body: some View {Color.orange.ignoresSafeArea()}
}


private struct MyView: UIViewControllerRepresentable {
    let rootView: AnyView
    
    init(rootView: AnyView) {
        self.rootView = rootView
        print("init MyView")
    }
    
    func makeUIViewController(context: Context) -> UIViewController {
        print("makeUI")
        /// create my custom VC
        return UIHostingController(rootView: rootView)
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print("updateUI")
    }
}

struct SwiftUIView19: View {
    @State var isGreen: Bool = true
    
    var body: some View {
        ControllerView(isGreen: $isGreen) {
            if isGreen {
                MyView(rootView: AnyView(GreenView()))
            } else {
                MyView(rootView: AnyView(OrangeView()))
            }
        }
    }
}

#Preview {
    SwiftUIView19()
}
swift swiftui view ios17 viewbuilder
1个回答
0
投票

每个分支都隐式返回

MyView
类型

不,它隐式返回

ViewBuilder.buildEither(first: ...)
ViewBuilder.buildEither(second: ...)
。这两个都返回
_ConditionalContent

ControllerView
不应强制视图类型为
MyView
。使其通用:

private struct ControllerView<Content: View>: View {
    let rootView: () -> Content
    @Binding var isGreen: Bool
    
    init(isGreen: Binding<Bool>, @ViewBuilder rootView: @escaping () -> Content) {
        self.rootView = rootView
        self._isGreen = isGreen
    }

    ...
}

如果出于某种原因您只需要

View
中的某些类型的
ControllerView
,我建议您编写自己的
ViewBuilder
版本,将所有内容委托给内置
ViewBuilder
,就像我在 here 所建议的那样.

并且

MyView
确实可以通用以避免
AnyView
。这与
ControllerView

的更改非常相似
private struct MyView<Content: View>: UIViewControllerRepresentable {
    let rootView: Content
    
    init(rootView: Content) {
        self.rootView = rootView
        print("init MyView")
    }

    ...
}
© www.soinside.com 2019 - 2024. All rights reserved.