swift - 如何在 Swift 中使用子定义覆盖父类扩展

标签 swift inheritance swiftui swift-protocols

我正在尝试创建一个基 Node 类,该类可以通过一组可组合的协议(protocol)进行继承扩展,以便有条件地向基 Node 类添加附加功能。首先,我添加一个 Previewable 协议(protocol),该协议(protocol)可以扩展 Node 以为某些实现提供可选的预览 View 。

独立示例:

import SwiftUI

// MARK: Base node protocol

protocol Node: AnyObject {
    var name: String { get }
}

extension Node {
    var name: String {
        get { "Unnamed Node" }
    }
}

// MARK: Previewable node

protocol Previewable {
    associatedtype PreviewBody: SwiftUI.View
    
    @ViewBuilder var previewBody: Self.PreviewBody { get }
}

extension Node {
    var previewBody: some View {
        Text("No Preview")
    }
}

// MARK: Test node impl

class TestNode: Node, Previewable {
    typealias PreviewView = Text
    
    var name: String = "Test Node"
    
    var previewBody: some View {
        Text("Test Preview")
    }
}

// MARK: Test content view

struct NodeView<NodeType: Node>: View {
    var node: NodeType
    
    init (_ node: NodeType) {
        self.node = node
    }
    
    var body: some View {
        VStack {
            node.previewBody
        }
    }
}

struct ContentView: View {
    var node = TestNode()
    
    var body: some View {
        VStack {
            NodeView(node)
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我的假设是 TestNode 类型上的 previewBody 实现将覆盖返回No PreviewNode 扩展> 默认情况下。然而,当我运行它时,我看到的是“No Preview”,而不是类实现提供的“Test Preview”文本。

我感觉我处理这个问题的方式是错误的,但我不完全确定如何纠正它。理想情况下,我希望以某种方式实现 Previewable 类型,以便在显示时检查 Node 继承类型的可选合规性。我无法找到一种方法来做到这一点,尽管没有泛型(即在 NodeView 结构中)允许 NodeView 仅将泛型节点作为参数(稍后我需要一种将 [any Node] 数组传递给 View 的方法,以根据它们实现的协议(protocol)来显示它们,因此在这种情况下泛型并不理想)。

如何更新它以更好地符合 Swift 语义,并使其正确显示实现的 “测试预览” View ,而不是扩展程序的默认 “无预览” View ?

最佳答案

正如亚历山大在评论中所说,

Methods on protocols aren't dynamically dispatched unless they're requirements on the protocol themselves.

当你执行node.previewBody时,Swift只知道node是某个Node。在 Node 上声明的唯一名为 previewBody 的内容是带有 Text("No Preview") 的内容。 Text("Test Preview") 是在 TestNode 中声明的,Swift 不知道 nodeTestNode 在编译时。

将此与您访问 node.name 时的情况进行比较,后者是动态调度的。在 Node 上声明了两个名为 name 的东西 - 属性要求和返回“Unnamed Node” 的默认实现。 Swift 选择前者,以便在运行时动态地将其分派(dispatch)给协议(protocol)要求的见证人。

我认为如果 Previewable 提供了默认实现,您的设计会更有意义:

extension Previewable {
    var previewBody: Text {
        Text("No Preview")
    }
}

由于 NodeView 使用 previewBody,它应该要求 NodeType 都是 Node 可预览

struct NodeView<NodeType: Node & Previewable>: View {

关于swift - 如何在 Swift 中使用子定义覆盖父类扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73822025/

相关文章:

SwiftUI EaseIn Transition 通过多种颜色循环

SwiftUI - 如何访问列表中的数据

macos - 如何防止 Picker 在 SwiftUI macOS 中折叠到子菜单中

swift - NSError.setUserInfoValueProvider 无限循环

ios - 编译错误 : Header 'ChattoAdditions-Swift.h' not found

javascript - 浏览器和服务器之间的代码共享(继承)

Python:从子类调用父类(super class)方法时超出最大递归深度

java - Toplink JPA 继承 - 摘要/详细关系

ios - 在自定义类下拉 Xcode 8.3.2 中找不到自定义 Controller 类

ios - Swift - Parse.com - 为什么这个闭包是错误的?