通过协议继承创建可重用的SwiftUI视图

3

目标

我的目标是创建一个可重用的视图协议,该协议继承了SwiftUI视图协议并提供了一些默认功能,以在视图内容的状态基础上加载和显示不同的视图。

这样,我就不必为每个视图重新编写相同的代码,使代码更加简洁。

问题

我创建了一个名为“DelayedContentView”的协议,根据视图内容是否已加载,显示两种不同的视图主体。问题在于当我尝试实现它时,“loadedBody”和“unloadedBody”属性不能是“some View”类型,即使它们与SwiftUI视图的Body相关联的类型相同。

背景

我的应用程序中有各种视图,可以从远程获取数据。每个视图都显示两个视图主体:一个用于获取内容时,另一个用于完成获取时。

苹果的SwiftUI视图协议

public protocol View {

    associatedtype Body : View

    var body: Self.Body { get }
}

我认为的协议

protocol DelayedContentView:View {
    
    func contentLoaded() -> Bool
    
    var loadedBody: Self.Body { get }
    
    var unloadedBody: Self.Body { get }
    
}


extension DelayedContentView {
    
    //Default implementation that I won't have to rewrite for each view.
    var body: some View {
        if contentLoaded() {
            return self.loadedBody
        }else{
            return self.unloadedBody
        }
    }
    
}

实现

struct ExampleView:DelayedContentView {
    
    func contentLoaded() -> Bool {
        //Ask viewModel if content is loaded.
        return false
    }

    var loadedBody: some View {
        Text("Content is loaded.")
    }

    var unloadedBody: some View {
        Text("Fetching content...")
    }
        
    
}

Compile Error: Type 'ExampleView' does not conform to protocol 'DelayedContentView'

我认为协议继承是处理这种情况的正确工具,但也许我错了?

1个回答

5
协议DelayedContentView必须添加两个关联类型来表示两种额外的视图类型:一个用于“已加载”视图,另一个用于“未加载”视图。它不能是Self.Body,因为这显然会违反规则,因为需要在body中使用条件视图。
protocol DelayedContentView: View {
    
    associatedtype LoadedBody: View
    associatedtype UnloadedBody: View
    
    func contentLoaded() -> Bool
    
    var loadedBody: LoadedBody { get }
    
    var unloadedBody: UnloadedBody { get }
}

现在您有了两个泛型类型,Self.Body 将是这两个类型的复合类型:

extension DelayedContentView {

    @ViewBuilder // needed to create a _ConditionalContent type from if/else
    var body: some View {
        if contentLoaded() {
            self.loadedBody
        } else {
            self.unloadedBody
        }
    }
}

现在,您的特定视图将声明LoadedViewUnloadedView类型是什么。例如,在您的ExampleView中,它们都将是Text,而Body将是_ConditionalContent<Text,Text>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接