正如标题所暗示的,我试图通过父视图 (P) 将数据从一个子视图 (A) 传递到另一个子视图 (B)。
父视图如下所示:
@State var rectFrame: CGRect = .zero
var body: some View {
childViewA(rectFrame: $rectFrame)
childViewB()
}
Run Code Online (Sandbox Code Playgroud)
其中childViewA获得CGRect该childViewB需求。
childViewA 看起来像这样:
@Binding var rectFrame: CGRect
var body: some View {
// Very long code where rectFrame is obtained and printed to console correctly
}
Run Code Online (Sandbox Code Playgroud)
我如何传递rectFrame给childViewB?我用尽了一切至今返回CGRect.zero的childViewB,尽管在这两个印刷正确的价值观parentView和childViewA。
为了尝试将值传递给childViewB,我重写了parentView这样的内容:
@State var rectFrame: CGRect = .zero
var body: some View {
childViewA(rectFrame: $rectFrame)
childViewB(rectFrame: $rectFrame.value)
}
Run Code Online (Sandbox Code Playgroud)
用childViewB具有以下结构:
var rectFrame: CGRect = CGRect.zero
var body: some View {
}
Run Code Online (Sandbox Code Playgroud)
但这只是CGRect.zero每次都打印。
我最近尝试过,@ObjectBinding但我一直在努力,所以如果有人能帮助我解决这个具体的例子,我将非常感激。
class SourceRectBindings: BindableObject {
let willChange = PassthroughSubject<Void, Never>()
var sourceRect: CGRect = .zero {
willSet {
willChange.send()
}
}
}
Run Code Online (Sandbox Code Playgroud)
您没有提到 ChildViewB 如何获取其矩形。也就是说,它是否使用 GeometryReader 或任何其他来源。无论使用什么方法,都可以通过两种方式向上传递信息:通过绑定或通过首选项。如果涉及几何形状,甚至锚定偏好。
对于第一种情况,在没有看到您的实现的情况下,您可能会DispatchQueue.main.async {}在设置 rect 时丢失 a 。这是因为更改必须在视图完成自身更新之后发生。然而,我认为这是一个非常非常肮脏的技巧,并且只在测试时使用它,但绝不会在生产代码中使用它。
第二种选择(使用首选项)是一种更强大的方法(并且是首选方法;-)。我将包含这两种方法的代码,但您绝对应该了解有关首选项的更多信息。我最近写了一篇文章,您可以在这里找到:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
第一种方法:我不建议将几何值向上传递到层次结构,这会影响其他视图的绘制方式。但如果你坚持的话,这就是让它发挥作用的方法:
struct ContentView : View {
@State var rectFrame: CGRect = .zero
var body: some View {
VStack {
ChildViewA(rectFrame: $rectFrame)
ChildViewB(rectFrame: rectFrame)
}
}
}
struct ChildViewA: View {
@Binding var rectFrame: CGRect
var body: some View {
DispatchQueue.main.async {
self.rectFrame = CGRect(x: 10, y: 20, width: 30, height: 40)
}
return Text("CHILD VIEW A")
}
}
struct ChildViewB: View {
var rectFrame: CGRect = CGRect.zero
var body: some View {
Text("CHILD VIEW B: RECT WIDTH = \(rectFrame.size.width)")
}
}
Run Code Online (Sandbox Code Playgroud)
第二种方法:使用首选项
import SwiftUI
struct MyPrefKey: PreferenceKey {
typealias Value = CGRect
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
struct ContentView : View {
@State var rectFrame: CGRect = .zero
var body: some View {
VStack {
ChildViewA()
ChildViewB(rectFrame: rectFrame)
}
.onPreferenceChange(MyPrefKey.self) {
self.rectFrame = $0
}
}
}
struct ChildViewA: View {
var body: some View {
return Text("CHILD VIEW A")
.preference(key: MyPrefKey.self, value: CGRect(x: 10, y: 20, width: 30, height: 40))
}
}
struct ChildViewB: View {
var rectFrame: CGRect = CGRect.zero
var body: some View {
Text("CHILD VIEW B: RECT WIDTH = \(rectFrame.size.width)")
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将EnvironmentObject用于这样的事情......
约的好处EnvironmentObject的是,无论何时何地,你改变它的变量之一,它总是会确保每一个在使用该变量,它的更新。
注意:要更新每个变量,您必须确保通过 BindableObject 类/EnvironmentObject 在您希望更新的每个视图上...
源矩形绑定:
class SourceRectBindings: BindableObject {
/* PROTOCOL PROPERTIES */
var willChange = PassthroughSubject<Application, Never>()
/* Seems Like you missed the 'self' (send(self)) */
var sourceRect: CGRect = .zero { willSet { willChange.send(self) }
}
Run Code Online (Sandbox Code Playgroud)
家长:
struct ParentView: view {
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View {
childViewA()
.environmentObject(sourceRectBindings)
childViewB()
.environmentObject(sourceRectBindings)
}
}
Run Code Online (Sandbox Code Playgroud)
子视图A:
struct ChildViewA: view {
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View {
// Update your frame and the environment...
// ...will update the variable everywhere it was used
self.sourceRectBindings.sourceRect = CGRect(x: 0, y: 0, width: 300, height: 400)
return Text("From ChildViewA : \(self.sourceRectBindings.sourceRect)")
}
}
Run Code Online (Sandbox Code Playgroud)
子视图B:
struct ChildViewB: view {
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View {
// This variable will always be up to date
return Text("From ChildViewB : \(self.sourceRectBindings.sourceRect)")
}
}
Run Code Online (Sandbox Code Playgroud)
使其发挥作用的最后步骤
• 进入您希望变量更新的最高视图,这是您的父视图,但我通常更喜欢我的SceneDelegate ......所以我通常这样做:
let sourceRectBindings = SourceRectBindings()
• 然后在同一个类中,'SceneDelegate' 将转到我指定的根视图并通过我的 EnviromentObject
window.rootViewController = UIHostingController(
rootView: ContentView()
.environmentObject(sourceRectBindings)
)
Run Code Online (Sandbox Code Playgroud)
• 然后在最后一步,我将它传递给 May Parent 视图
struct ContentView : View {
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View {
ParentView()
.environmentObject(sourceRectBindings)
}
}
Run Code Online (Sandbox Code Playgroud)
如果你像这样运行代码,你会看到你的预览会抛出错误。那是因为它没有通过环境变量。为此,只需启动一个虚拟的环境实例:
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(SourceRectBindings())
}
}
#endif
Run Code Online (Sandbox Code Playgroud)
现在您可以随心所欲地使用该变量,如果您想传递除 rect 之外的任何其他内容……您只需将该变量添加到 EnvironmentObject 类中,您就可以访问和更新它
| 归档时间: |
|
| 查看次数: |
8339 次 |
| 最近记录: |