我想轻松地为 SwiftUI 预览 View 提供可变绑定(bind)(以便预览是交互式的,与传递 .constant(...)
绑定(bind)不同)。我关注了BindingProvider一次允许一个值的方法,如下所示:
#Preview {
BindingProvider(true) { binding in
SomeToggleView(toggleBinding: binding)
}
}
最好传入多个要绑定(bind)的值,就像这样
#Preview {
BindingProvider(true, "text") { toggleBinding, textFieldBinding in
SomeToggleAndTextFieldView(toggleBinding: toggleBinding, textFieldBinding: textFieldBinding)
}
}
到目前为止,我已经得到了这段代码,但它没有编译,只是说“Command SwiftCompile failed with a nonzero exit code”
struct BindingProvider<each T, Content: View>: View {
@State private var state: (repeat State<each T>)
private var content: ((repeat Binding<each T>)) -> Content
init(_ initialState: repeat each T,
@ViewBuilder content: @escaping ((repeat Binding< each T>)) -> Content)
{
self.content = content
self._state = State(initialValue: (repeat State(initialValue: each initialState)))
}
var body: Content {
content((repeat (each state).projectedValue))
}
}
也许我做错了什么或者这只是编译器问题?任何帮助将不胜感激!
最佳答案
编译器似乎还无法将某些协议(protocol)
结构与参数包一起管理。指定具体 View 类型有帮助,因此只需替换
var body: some View {
与
var body: Content {
没有类型删除的解决方案不能提供平滑的绑定(bind)展开...并且如果不以某种方式枚举元组的元素,您就无法展开它。我认为在 @dynamicMemberLookup 学会如何处理参数包之前这是不可能的。
struct BindingProvider<each T, Content: View>: View {
@State private var state: (repeat each T)
private var content: (_ binding: Binding<(repeat each T)>) -> Content
init(_ initialState: repeat each T, @ViewBuilder content: @escaping (Binding<(repeat each T)>) -> Content) {
self.content = content
self._state = State(initialValue: (repeat each initialState))
}
var body: Content {
self.content($state)
}
}
#Preview {
BindingProvider(0, true) { bind in
let (count, isOn) = (bind.0, bind.1)
Stepper("", value: count)
Toggle("", isOn: isOn)
}
}
这是类型删除和枚举元组值的解决方案:
struct BindingProvider<each T, Content: View>: View {
@State private var states: [Any]
private var content: ((repeat Binding<each T>)) -> Content
init(_ initialState: repeat each T, @ViewBuilder content: @escaping (repeat Binding<each T>) -> Content) {
self.content = { (args: (repeat Binding<each T>)) in
content(repeat each args)
}
// convert arguments to an array
var states = [Any]()
repeat states.append(each initialState as Any)
_states = State(initialValue: states)
}
// Specify body type as Content. Compile time error otherwise
var body: Content {
content((repeat each makeBindings()))
}
private func makeBindings() -> (repeat Binding<each T>) {
var index = 0
func makeBinding<Result>(_ index: inout Int) -> Binding<Result> {
let currentIndex = index
index += 1
return Binding<Result> {
states[currentIndex] as! Result
} set: { newValue in
states[currentIndex] = newValue
}
}
return (repeat makeBinding(&index) as Binding<each T>)
}
}
#Preview {
BindingProvider(18, "Hello") { $temperature, $greeting in
VStack(alignment: .leading) {
Text("\(greeting), the temperature is \(temperature)º")
TextField("Greeting", text: $greeting)
Stepper("\(temperature)", value: $temperature)
}
.padding()
}
}
即使类型删除发生在幕后,该解决方案也是强类型的。此外,索引也是安全的,因为它受限于内部“A”的数量(重复每个 A),并且在任何实例的生命周期中都是相同的。
关于swift - 如何在 View 中使用 Swift 参数包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77101028/