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

Muf*_*497 3 swift swiftui

目标

我的目标是制作一个可重用的视图协议,它继承 SwiftUI View 协议并提供一些默认功能,用于根据视图内容的状态加载显示不同的视图。

这样,我不必为我创建的每个视图重写相同的代码,并使代码更清晰。

问题

我制作了一个“DelayedContentView”协议,它根据视图内容是否已加载来显示两个不同的视图主体。当我尝试实施它时,问题就出现了。“loadedBody”和“unloadedBody”属性不能是“some View”类型,即使它们与 SwiftUI View 的 Body AssociatedType 类型相同。

背景

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

Apple 的 SwiftUI 视图协议

public protocol View {

    associatedtype Body : View

    var body: Self.Body { get }
}
Run Code Online (Sandbox Code Playgroud)

我的观点协议

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
        }
    }
    
}

Run Code Online (Sandbox Code Playgroud)

执行

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...")
    }
        
    
}
Run Code Online (Sandbox Code Playgroud)
Compile Error: Type 'ExampleView' does not conform to protocol 'DelayedContentView'
Run Code Online (Sandbox Code Playgroud)

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

New*_*Dev 5

该协议DelayedContentView必须添加另外两种关联类型来表示两种附加视图类型:一种用于“已加载”视图,另一种用于“已卸载”视图。不可能,Self.Body因为它显然会被违反,因为你需要身体中有条件的视图。

protocol DelayedContentView: View {
    
    associatedtype LoadedBody: View
    associatedtype UnloadedBody: View
    
    func contentLoaded() -> Bool
    
    var loadedBody: LoadedBody { get }
    
    var unloadedBody: UnloadedBody { get }
}
Run Code Online (Sandbox Code Playgroud)

现在您已经有了其中两个泛型类型,这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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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