SwiftUI @Binding 初始化

99

我一直在尝试使用SwiftUI并理解BindableObjects等概念(至少我希望我已经理解了)。

现在我遇到了一个看起来很傻的问题,但是我似乎找不到答案:如何初始化一个@Binding变量?

我有以下代码:

struct LoggedInView : View {

    @Binding var dismissView: Bool

    var body: some View {
        VStack {
            Text("Hello World")
        }
    }
}

在我的预览代码中,我想传递那个类型为Binding<Bool>的参数:

#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: **Binding<Bool>**)
    }
}
#endif

我该怎么初始化它?我试过:

Binding<Bool>.init(false)
Binding<Bool>(false)

甚至可以是:

@Binding var dismissView: Bool = false

但是没有一个起作用……有任何想法吗?

6个回答

213

当您在应用程序中使用 LoggedInView 时,您确实需要提供一些绑定,例如来自先前视图的 @State@EnvironmentObject

对于仅需要固定值的 PreviewProvider 的特殊情况,您可以使用 .constant(false)

例如:

#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: .constant(false))
    }
}
#endif

看起来很优雅。我只是想知道它是否会使属性成为实时预览中的不可变属性。 - Martin R
这通常就是预览所需的全部。 - Paulw11
2
对于静态预览,使用不可变的Binding.constant是可以的,但是在我想要玩弄一个视图并查看值更改的实时预览中,它完全无法使用。 - NeverwinterMoon
@NeverwinterMoon 下面的解决方案更好,因为它可以让您与画布进行交互。 - Noyer282

83

使用Binding.constant(false)可以,但仅适用于静态预览。如果您实际上想启动Live Preview,则constant不会像真实情况下那样行为,因为它永远不会被您的操作更新。我个人经常使用Live Preview,因为我可以玩弄一个隔离的视图。

这里是我在需要Binding的预览中所做的:

import SwiftUI

struct SomeView: View {
   @Binding var code: String

   var body: some View {
     // some views modifying code binding
   }
}

struct SomeView_Previews: PreviewProvider {
  static var previews: some View {
    PreviewWrapper()
  }

  struct PreviewWrapper: View {
    @State(initialValue: "") var code: String

    var body: some View {
      SomeView(code: $code)
    }
  }
}

这个可以运行。但我在想,为什么添加一个属性像 @State static var code: String = "" 就不够了呢? - bcause
2
这还不够,因为它无法正常工作,与提议的解决方案不同。使用静态变量时,属性值根本不会更新,它的工作方式与使用Binding.constant相同——这对于静态预览来说是可以的。 - NeverwinterMoon
NeverwinterMoon 我在我的代码中使用过它,它的效果非常好(因为我需要一个 @State,因为我有一个 Binding<> 调用)。给出了 +1 的答案。 - Ethan Halprin

12
  • 如果你需要一个简单的属性,属于单个视图,你应该使用 @State
  • 如果你需要一个复杂的属性,可能属于多个视图(如2-3个视图),你应该使用@ObjectBinding
  • 最后,如果你需要在所有视图中使用的属性,你应该使用@EnvironmentObject。 详细信息请参考此处

对于你的情况,如果你仍然想初始化你的绑定变量,你可以使用:

var binding: Binding = .constant(false)

请注意,自Xcode 11.3 Beta 5以来,(AT)ObjectBinding现在被称为(AT)ObservedObject(其中(AT) = @)。 - gepree

2
在预览中,您需要使用.constant(Bool(false))
#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: .constant(Bool(false))
    }
}
#endif

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

1

如果你有一个类似于viewModel的对象,你也可以使用.constant()方法。

struct View_Previews: PreviewProvider {
  static var previews: some View {
     View(vm:.constant(ViewModel(text: "Sample Text")))
  }
}

0

我正在一个预览中使用视图的不同配置(我正在开发一个自定义控件,想要看到它的不同配置)。我已经扩展了@NeverwinterMoon提供的实现,以创建多个独立的视图实例。

struct SomeView: View {
   @Binding var value: Int

   var body: some View {
     // some views modifying code binding
   }
}

struct SomeView_Previews: PreviewProvider {
  static var previews: some View {
    VStack {
      // The same view but with different configurations

      // Configuration #1
      PreviewWrapper() { value in
        SomeView(value: value)
          .background(Color.blue)
      }

      // Configuration #2      
      PreviewWrapper(initialValue: 2) { value in
        SomeView(value: value)
          .padding()
      }
    }
  }

  struct PreviewWrapper<Content: View>: View {
    @State var value: Int
    private let content: (Binding<Int>) -> Content
    
    init(
      initialValue: Int = 0,
      @ViewBuilder content: @escaping (Binding<Int>) -> Content
    ) {
      self.value = initialValue
      self.content = content
    }
    
    var body: some View {
      content($value)
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接