【问题标题】:SwiftUI: Generic parameter 'Subject' could not be inferredSwiftUI:无法推断通用参数“主题”
【发布时间】:2019-12-16 20:20:09
【问题描述】:

我使用 SwiftUI 构建了一个 LoadingView,用于在我从 API 获取远程数据时在我的应用程序中显示一些加载内容。我使用的是 Xcode 11.0 beta 5。

这是LoadingView

struct LoadingView<Content>: View where Content: View {

    @Binding var isShowing: Bool
    var content: () -> Content

    var body: some View {

        GeometryReader { geometry in

            ZStack(alignment: .center) {

                self.content()
                    .disabled(self.isShowing)
                    .blur(radius: self.isShowing ? 3 : 0)

                VStack {
                    Text("Loading...")
                    ActivityIndicator(isAnimating: .constant(true), style: .large)
                }
                .frame(width: geometry.size.width / 2,
                       height: geometry.size.height / 5)
                    .background(Color.white)
                    .foregroundColor(Color.primary)
                    .cornerRadius(5)
                    .opacity(self.isShowing ? 1 : 0)
            }
        }
    }
}

这是我的数据存储。它被声明为ObservableObject 并且有多个@Published 属性。它还可以从 API 进行一些远程获取:

class CharacterStore: ObservableObject {

    @Published private(set) var isLoading = false


    // Fetches some stuff from a remote api
    func fetch() {

        self.isLoading = true

        myService.getCharacters { (result) in
            DispatchQueue.main.async {
                self.isLoading = false
            }
        }
    }
}

最后,这是我要显示我的LoadingView 的视图,其中包含ContentView 的内容。当然我在显示这个视图之前设置了@EnvironmentObject

struct ContentView: View {

    @EnvironmentObject var charStore: CharacterStore

    var body: some View {

        LoadingView(isShowing: self.$charStore.isLoading) { // Here I get the error

            // Show some Content here
            Text("")
        }
    }
}

问题是我想将self.$charStore.isLoading 绑定到LoadingView。在这一行中,我收到以下错误:

无法推断通用参数“主题”

我尝试了几种方法,但这些方法都不起作用。顺便说一句:如果我在ContentView 中使用@State 属性,它就可以像这样正常工作:

struct ContentView: View {

    @EnvironmentObject var charStore: CharacterStore

    @State var loads: Bool = false

    var body: some View {

        LoadingView(isShowing: self.$loads) { // Here I get no error

            // Show some Content here
            Text("")
        }
    }
}

我错过了什么吗?如果您需要更多信息,请告诉我,如果需要,我可以提供更多内容。

感谢您的帮助!

【问题讨论】:

    标签: swift swiftui swift5 combine


    【解决方案1】:

    由于您的 LoadingView 不会修改 .isLoading,因此您无需将其作为绑定传递:

    LoadingView(isShowing: self.$charStore.isLoading)
    

    相反,删除LoadingView 中的@Binding

    struct LoadingView<Content>: View where Content: View {
    
        var isShowing: Bool
        ...
    

    并像这样创建它(删除美元符号):

    LoadingView(isShowing: self.charStore.isLoading) { ... }
    

    相反,如果你坚持传递一个绑定,那么你需要将private(set)从:

    @Published private(set) var isLoading = false
    

    【讨论】:

    • 两种方式都有效。我决定采用解决方案 1:从 LoadingView 中的 isShowing 中删除 @Binding 很好,因为就我而言,LoadingView 不需要修改 loadingState。感谢您的帮助!
    • 对于未来遇到此问题的 Google 员工,Binding 需要同时访问投影属性的 setter 和 getter。我遇到了这个问题,我试图将计算属性(仅限获取)映射到 Binding 参数,如下所示。这可以解决,但事实证明我的计算属性无论如何都需要一个 setter。添加一个为我解决了这个问题。
    【解决方案2】:

    你不能这样做吗:

    将 @Binding 替换为与 ContentView 使用的相同的 @EnvironmentObject。

    struct LoadingView<Content>: View where Content: View {
        @EnvirontmentObject var charStore: CharacterStore // added
        //@Binding var isShowing: Bool // removed
        var content: () -> Content
    
        var body: some View {
    
            GeometryReader { geometry in
    
                ZStack(alignment: .center) {
    
                    self.content()
                        .disabled(self.$charStore.isLoading) // Changed
                        .blur(radius: self.$charStore.isLoading ? 3 : 0) // Changed
    
                    VStack {
                        Text("Loading...")
                        ActivityIndicator(isAnimating: .constant(true), style: .large)
                    }
                    .frame(width: geometry.size.width / 2,
                           height: geometry.size.height / 5)
                        .background(Color.white)
                        .foregroundColor(Color.primary)
                        .cornerRadius(5)
                        .opacity(self.$charStore.isLoading ? 1 : 0) // Changed
                }
            }
        }
    }
    

    当然,您还必须从 ContentView 的 LoadingView() 初始化程序中删除 isShowing 参数。

    如有错误请指正!

    【讨论】:

    • 我稍后会尝试并让您知道:您的解决方案的问题是,当然,我想添加 LoadingView 不仅与我的 charStore 结合...它应该是通用的 LoadingView,我可以在任何我想使用的地方使用它。
    猜你喜欢
    • 2019-11-26
    • 2022-08-17
    • 2020-04-26
    • 1970-01-01
    • 2020-05-26
    • 2020-04-19
    • 2023-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多