我正在尝试创建一个 SwiftUI 选择器,用户可以使用它来选择 1000 到 20000 之间的数字(以 1000 为增量。例如 1000,2000,3000 .... ... 20000)
默认情况下,SwiftUI 选择器只能保存 10 行文本。如何让 SwiftUI 选择器包含 20 行文本?
我猜你写的是这样的:
struct ContentView: View {
var body: some View {
Picker(selection: $value, label: Text("Pick One")) {
Text("1000").tag(1000)
Text("2000").tag(2000)
Text("3000").tag(3000)
Text("4000").tag(4000)
Text("5000").tag(5000)
Text("6000").tag(6000)
Text("7000").tag(7000)
Text("8000").tag(8000)
Text("9000").tag(9000)
Text("10000").tag(10000)
}
}
@State var value: Int = 1000
}
Run Code Online (Sandbox Code Playgroud)
然后您尝试为 11000 添加一行并收到此错误:
error: picker.xcplaygroundpage:5:31: error: cannot convert value of type 'Binding<Int>' to expected argument type 'Binding<_>'
Picker(selection: $value, label: Text("Pick One")) {
^~~~~~
Run Code Online (Sandbox Code Playgroud)
问题是,由于 Swift 语言的限制以及 SwiftUI 的实现方式,一个@ViewBuilder主体中只能有 10 个子视图。
这里有两种方法可以解决这个问题。
一种适合您的设计的方法是使用ForEach:
struct ContentView: View {
var body: some View {
Picker(selection: $value, label: Text("Pick One")) {
ForEach(Array(stride(from: 1000, through: 20000, by: 1000))) { number in
Text("\(number)").tag(number)
}
}
}
@State var value: Int = 1000
}
Run Code Online (Sandbox Code Playgroud)
如果您的项目不遵循简单模式,另一种更合适的方法是使用Group以下方法对您的项目进行分组:
struct ContentView: View {
var body: some View {
Picker(selection: $value, label: Text("Pick One")) {
Group {
Text("1000").tag(1000)
Text("2000").tag(2000)
Text("3000").tag(3000)
Text("4000").tag(4000)
Text("5000").tag(5000)
Text("6000").tag(6000)
Text("7000").tag(7000)
Text("8000").tag(8000)
Text("9000").tag(9000)
Text("10000").tag(10000)
}
Group {
Text("11000").tag(11000)
Text("12000").tag(12000)
Text("13000").tag(13000)
Text("14000").tag(14000)
Text("15000").tag(15000)
Text("16000").tag(16000)
Text("17000").tag(17000)
Text("18000").tag(18000)
Text("19000").tag(19000)
Text("20000").tag(20000)
}
}
}
@State var value: Int = 1000
}
Run Code Online (Sandbox Code Playgroud)
SwiftUI 将Group子视图展平为Group的父视图(在本例中,为Picker)。每个Group子视图最多可以有 10 个子视图,它们本身可以是Groups,因此通过嵌套Groups,您可以在Picker. 但我建议使用ForEach.
如果您想了解 10 个子视图限制的来源,请编辑我的第二个示例,将 存储Picker在如下变量中:
struct ContentView: View {
var body: some View {
let picker = Picker(selection: $value, label: Text("Pick One")) {
Group {
...
}
}
return picker
}
}
Run Code Online (Sandbox Code Playgroud)
现在picker在 Xcode 中单击选项以查看其推断类型:
让我们重新格式化它,使其更具可读性:
let picker: Picker<
Text,
Int,
TupleView<(
Group<TupleView<(
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View)>>,
Group<TupleView<(
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View,
some View)>>)>>
Run Code Online (Sandbox Code Playgroud)
哇,好大的类型!SwiftUI 大量使用这样的泛型类型,因为它在运行时效率更高。因为这些都是struct符合 的类型View,Swift 将整个类型Picker及其所有子项存储在一个连续的内存块中。该块可以从堆栈开始,只有在 SwiftUI 最终需要对其进行类型擦除或长期存储时才需要复制到堆中。与 UIKit 相比,每个视图总是在创建时在堆上单独分配。
ViewBuilder是组合这些复杂视图的 SwiftUI 实用程序。Swift 将每个的主体转换Group为对 的调用ViewBuilder.buildBlock,Group主体内的每个视图都作为对 的单独参数ViewBuilder.buildBlock。这些参数中的每一个都可以是一个单独的类型(例如,aGroup可以有一些Text孩子和一些Image孩子)。但是 Swift 不支持可变参数泛型,因此ViewBuilder必须定义一个buildBlock接受单个视图的版本,一个接受两个视图的版本,一个接受三个视图的版本,依此类推。它不能定义无限多个方法,因为那样 SwiftUI 框架将是无限大的。所以它在 10 个参数处停止:
Run Code Online (Sandbox Code Playgroud)static func buildBlock() -> EmptyView Builds an empty view from a block containing no statements. static func buildBlock<Content>(Content) -> Content Passes a single view written as a child view through unmodified. static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)> static func buildBlock<C0, C1, C2>(C0, C1, C2) -> TupleView<(C0, C1, C2)> static func buildBlock<C0, C1, C2, C3>(C0, C1, C2, C3) -> TupleView<(C0, C1, C2, C3)> static func buildBlock<C0, C1, C2, C3, C4>(C0, C1, C2, C3, C4) -> TupleView<(C0, C1, C2, C3, C4)> static func buildBlock<C0, C1, C2, C3, C4, C5>(C0, C1, C2, C3, C4, C5) -> TupleView<(C0, C1, C2, C3, C4, C5)> static func buildBlock<C0, C1, C2, C3, C4, C5, C6>(C0, C1, C2, C3, C4, C5, C6) -> TupleView<(C0, C1, C2, C3, C4, C5, C6)> static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7>(C0, C1, C2, C3, C4, C5, C6, C7) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7)> static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8>(C0, C1, C2, C3, C4, C5, C6, C7, C8) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8)> static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>
这就是为什么任何观点,其内容使用的定义ViewBuilder(其中包括VStack,HStack,ZStack,Picker,List,Group,和其他人)最多只能有10个直接子视图。
| 归档时间: |
|
| 查看次数: |
978 次 |
| 最近记录: |