We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
我在 SwiftUI 方面还是个初学者。我偶尔尝试过,但大部分时间我真的不知道自己在做什么。最近,我有机会再试一次。对于这个特定的实验,我想将一个现有的视图封装起来,以便我可以从 SwiftUI 访问它。这部分是相当标准的东西。然而,我遇到了一个有趣的问题,我敢打赌我不是唯一一个。
当我学习一个新的 API 时,我喜欢从一个新的项目开始。这可以帮助我专注于任务,避免与更大项目集成时的复杂性。我的目标是将 NSTableView 包装起来,并将其嵌入到 SwiftUI 的视图层次结构中。所以,我创建了一个新项目,并根据苹果的 SwiftUI 教程 Interfacing with UIKit 进行操作,按照需要将其适配为 AppKit 和我的问题。
NSTableView
我感觉挺不错的,因为我很快就实现了基本功能。这个实现遵循了 View-NSView-Coordinator 的模式,结构在仔细研究之后很容易理解。
View
NSView
Coordinator
以下是我编写的代码:
struct EventsTableView: NSViewRepresentable { var events: [String] func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> NSScrollView { let column = NSTableColumn(identifier: .exampleColumn) column.width = 100.0 let tableView = NSTableView() tableView.headerView = nil tableView.addTableColumn(column) tableView.delegate = context.coordinator tableView.dataSource = context.coordinator let view = NSScrollView() view.documentView = tableView return view } func updateNSView(_ nsView: NSScrollView, context: Context) { let tableView = nsView.documentView as! NSTableView tableView.reloadData() } } extension EventsTableView { final class Coordinator: NSObject, NSTableViewDataSource, NSTableViewDelegate { var parent: EventsTableView init(_ parent: EventsTableView) { self.parent = parent } func numberOfRows(in tableView: NSTableView) -> Int { return parent.events.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let reusedView = tableView.makeView(withIdentifier: .exampleColumn, owner: self) let view = reusedView as? NSTextField ?? NSTextField(labelWithString: "") view.stringValue = parent.events[row] return view } } }
我之前提到一切都在正常运行。但是,当我进一步实验时,很快发现即使我在更新时重新加载表视图,其内容也始终没有变化。
当然,事后看来,问题显而易见。你可能已经注意到了。如果没有发现,也别感到糟糕,因为我花了不少时间调试和思考,才完全明白问题出在哪里。
SwiftUI 的互操作性围绕着一个包装视图 EventsTableView 及其协调器 Coordinator 展开。这个协调器实例会在视图需要更改时作为上下文传递回去。包装视图和协调器之间的这种关系至关重要,理解它们的生命周期关系也同样重要。
EventsTableView
SwiftUI 的视图在正常执行期间会非常频繁地被重新创建,这很正常。但你的协调器实例只会创建一次,并尽可能地被重用。这其实是非常标准的值类型/引用类型的行为。当协调器将包装视图作为初始化参数时,它会拷贝那个视图的第一次实例化的版本。而当 SwiftUI 包装视图被重新创建时,这个拷贝并不会更新。
我的问题在于,Coordinator 创建时,父级属性及其相关事件只被复制了一次,然后就再也没有更新过。当我意识到这一点时,我觉得自己有点蠢。视图是值类型,当然它不会更新!但我也不想对自己太苛刻,因为这种协调器捕获父级视图的模式非常普遍。许多关于这一主题的博客文章中都存在这种模式。包括苹果的(表面上是权威的)教程,都使用了这种父级模式。我猜这就是问题的根源。
在我的案例中,解决方案其实很简单。关键是要在协调器实例中捕获并更新数据。
struct EventsTableView: NSViewRepresentable { var events: [String] func makeCoordinator() -> Coordinator { Coordinator(events: events) // <- 这里! } // 省略部分代码... func updateNSView(_ nsView: NSScrollView, context: Context) { context.coordinator.events = events // <- 这里也是! let tableView = nsView.documentView as! NSTableView tableView.reloadData() } }
当我完全意识到协调器实例是长期存在的时,捕获父视图就毫无意义了。但是,它确实让我误以为我有一个引用类型的关系,而这种关系是可行的。如果这个属性被称为 firstParent 或者 creatingView,也许我就不会犯这个错误了。
firstParent
creatingView
经历了这一切后,我不确定是否能想到任何一个你需要捕获 Coordinator 创建视图副本的理由。如果你知道这种父视图-协调器模式可能有用的原因,请告诉我。不过即便有一些合适的场景,我仍然认为这种模式太容易让人困惑,就像我一样,因此最好还是避免使用。
https://www.massicotte.org/swiftui-coordinator-parent
The text was updated successfully, but these errors were encountered:
Quick RSS Feed: SwiftUI 的协调器父级的好奇案例 #41
7a0f60f
jaywcjlove
No branches or pull requests
📋 简介
我在 SwiftUI 方面还是个初学者。我偶尔尝试过,但大部分时间我真的不知道自己在做什么。最近,我有机会再试一次。对于这个特定的实验,我想将一个现有的视图封装起来,以便我可以从 SwiftUI 访问它。这部分是相当标准的东西。然而,我遇到了一个有趣的问题,我敢打赌我不是唯一一个。
问题
当我学习一个新的 API 时,我喜欢从一个新的项目开始。这可以帮助我专注于任务,避免与更大项目集成时的复杂性。我的目标是将
NSTableView
包装起来,并将其嵌入到 SwiftUI 的视图层次结构中。所以,我创建了一个新项目,并根据苹果的 SwiftUI 教程 Interfacing with UIKit 进行操作,按照需要将其适配为 AppKit 和我的问题。我感觉挺不错的,因为我很快就实现了基本功能。这个实现遵循了
View
-NSView
-Coordinator
的模式,结构在仔细研究之后很容易理解。以下是我编写的代码:
我之前提到一切都在正常运行。但是,当我进一步实验时,很快发现即使我在更新时重新加载表视图,其内容也始终没有变化。
当然,事后看来,问题显而易见。你可能已经注意到了。如果没有发现,也别感到糟糕,因为我花了不少时间调试和思考,才完全明白问题出在哪里。
父级?
SwiftUI 的互操作性围绕着一个包装视图
EventsTableView
及其协调器Coordinator
展开。这个协调器实例会在视图需要更改时作为上下文传递回去。包装视图和协调器之间的这种关系至关重要,理解它们的生命周期关系也同样重要。SwiftUI 的视图在正常执行期间会非常频繁地被重新创建,这很正常。但你的协调器实例只会创建一次,并尽可能地被重用。这其实是非常标准的值类型/引用类型的行为。当协调器将包装视图作为初始化参数时,它会拷贝那个视图的第一次实例化的版本。而当 SwiftUI 包装视图被重新创建时,这个拷贝并不会更新。
我的问题在于,
Coordinator
创建时,父级属性及其相关事件只被复制了一次,然后就再也没有更新过。当我意识到这一点时,我觉得自己有点蠢。视图是值类型,当然它不会更新!但我也不想对自己太苛刻,因为这种协调器捕获父级视图的模式非常普遍。许多关于这一主题的博客文章中都存在这种模式。包括苹果的(表面上是权威的)教程,都使用了这种父级模式。我猜这就是问题的根源。捕获属性
在我的案例中,解决方案其实很简单。关键是要在协调器实例中捕获并更新数据。
当我完全意识到协调器实例是长期存在的时,捕获父视图就毫无意义了。但是,它确实让我误以为我有一个引用类型的关系,而这种关系是可行的。如果这个属性被称为
firstParent
或者creatingView
,也许我就不会犯这个错误了。永远不要捕获父视图
经历了这一切后,我不确定是否能想到任何一个你需要捕获
Coordinator
创建视图副本的理由。如果你知道这种父视图-协调器模式可能有用的原因,请告诉我。不过即便有一些合适的场景,我仍然认为这种模式太容易让人困惑,就像我一样,因此最好还是避免使用。🔗 链接
https://www.massicotte.org/swiftui-coordinator-parent
The text was updated successfully, but these errors were encountered: