我正在尝试创建一个基 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 Preview
的 Node
扩展> 默认情况下。然而,当我运行它时,我看到的是“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 不知道 node
是 TestNode
在编译时。
将此与您访问 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/