将 @State 属性的 @Binding 值从容器视图传递到其子视图
Posted
技术标签:
【中文标题】将 @State 属性的 @Binding 值从容器视图传递到其子视图【英文标题】:Passing down @Binding values of @State properties from a container view to its children views 【发布时间】:2021-03-11 10:41:28 【问题描述】:我创建了一个充当容器视图的视图,它具有多个我希望将其值传递给其兄弟视图的属性。
下面的示例容器视图仅存储填充值,不是一个实际的用例,但这个想法可以转移到那个(我将在最后解释)。
public struct Container<Content: View>: View
let content: Content
// MARK: Padding
@State var bottom: CGFloat = .zero
@State var leading: CGFloat = .zero
@State var trailing: CGFloat = .zero
@State var top: CGFloat = .zero
// MARK: -
public init(padding: Padding...,
@ViewBuilder content: () -> Content)
for pad in padding
switch pad
case .bottom(let padding): bottom = padding
case .leading(let padding): leading = padding
case .trailing(let padding): trailing = padding
case .top(let padding): top = padding
case .horizontal(let padding): leading = padding
trailing = padding
case .vertical(let padding): bottom = padding
top = padding
self.content = content()
// MARK: -
public var body: some View
content
.padding(.bottom, bottom)
.padding(.leading, leading)
.padding(.top, top)
.padding(.trailing, trailing)
extension Container
public enum Padding
case vertical(_:CGFloat)
case horizontal(_:CGFloat)
case leading(_:CGFloat)
case trailing(_:CGFloat)
case top(_:CGFloat)
case bottom(_:CGFloat)
实际上,这看起来像:
Container(padding:.vertical(50))
/// children have no idea about Container’s set property values
而且效果很好。但我想要实现的是:
Container(padding:.vertical(50)) padding in
/// children have access to padding.bottom,top,leading,trailing
我在方法中的参考是 GeometryReader,它提供对 GeometryProxy 的访问以供其兄弟视图依赖。
如顶部所述,虽然我不太可能从传递填充值中找到好处,但我关注的直接用例是创建一个可扩展容器视图,该视图存储诸如 expanded: Bool
之类的属性以及诸如toggleExpanded()
之类的方法附加到其轻击手势处理程序,以更改expanded
的状态。
更新:
pawello2222 的建议对于我分享的示例代码在技术上是令人满意的。然而,由于值的静态性质,事实证明它不能立即转移到我的实际用例中,因为它涉及可以根据用户交互更改的值。学习到教训了!这是展开式视图:
public struct Expandable<Content: View>: View
let content: Content
@State public var expanded: Bool = false
public init(@ViewBuilder content: ((Binding<Bool>)) -> Content)
self.content = content(($expanded))
// MARK: -
public var body: some View
content
.onTapGesture(perform: toggle)
// MARK: - Methods
func toggle()
expanded.toggle()
换句话说,我需要子视图能够访问@Binding expanded
属性,因此它不仅能够读取当前的expanded
值,而且还可以将其状态切换回其父级。
上面代码的问题是它告诉我:
Variable 'self.content' used before being initialized
【问题讨论】:
【参考方案1】:我不确定这是否是您想要的,但您可以尝试以下方法:
public init(padding: Padding...,
@ViewBuilder content: ([Padding]) -> Content)
self.content = content(padding)
for pad in padding
// ...
并像这样使用它:
Container(padding:.vertical(50)) padding in
// ...
编辑
您希望在更新后的问题中实现的目标可以通过创建自定义EnvironmentKey
来完成:
extension EnvironmentValues
private struct PaddingKey: EnvironmentKey
static let defaultValue: Binding<Padding> = .constant(.vertical(0)) // or any other default value
var padding: Binding<Padding>
get self[PaddingKey]
set self[PaddingKey] = newValue
struct ContentView: View
@State var padding: Padding = .vertical(50)
var body: some View
Container(padding: padding)
ChildView()
// ...
.environment(\.padding, $padding) // inject the binding into the environment
struct ChildView: View
@Environment(\.padding) var padding // inject the binding into the environment
var body: some View
if case let .bottom(value) = padding.wrappedValue
Text("Bottom")
.padding(.bottom, value)
// ...
Button("Change")
padding.wrappedValue = .leading(50)
注意:您可以按照相同的方法在容器中使用.environment(\.padding
(而不是Container(padding: padding)
)。
【讨论】:
非常感谢!这就是我所寻找的几乎(我提供的代码在技术上很好,但不是实际的最终用例)。我已经在我的原始帖子中阐明了新代码的发展。希望大家多多指导。很抱歉第一次没有分享实际的预期用途——我认为它是可以转移的,但不幸的是显然不是。 @Barrrdi 您更新后的问题与原来的问题不同 - 它不再是关于传递@State
值。但我相应地更新了我的答案。
非常感谢您的跟进,非常感谢您花费的时间和耐心。这种方法对我来说是新的,我相信我会从中受益匪浅。但是,尝试您的确切示例代码,我在 ChildView() 上收到此错误:“Missing argument for parameter 'padding' in call”建议“插入'填充:#>'”作为修复,但我不完全确定需要插入哪些值而不是占位符建议(以及是否有另一种更好/正确的方法)。
是的,我意识到这一点,实际上我正准备将更新的问题分叉为一个单独的问题,但后来我看到你仍然回答了它。像您这样慷慨的人有幸与之互动。
FWIW 如果我将 Binding以上是关于将 @State 属性的 @Binding 值从容器视图传递到其子视图的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:属性装饰器的理解@State,@Binding,@ObservedObject,@Published,@Environment,@EnvironmentObject
SwiftUI:属性装饰器的理解@State,@Binding,@ObservedObject,@Published,@Environment,@EnvironmentObject