我正在尝试创建一个SwiftUI选择器,用户可以使用它来从1000到20000(以1000为增量)中选择一个数字。例如1000,2000,3000 .... ... 20000)
默认情况下,SwiftUI选择器只能容纳10行文本。如何允许SwiftUI选择器包含20行文本?
答案 0 :(得分:1)
我猜你写了这样的东西:
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
}
然后您尝试为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")) {
^~~~~~
问题在于,由于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
}
如果您的商品不遵循简单的模式,另一种方法更合适,那就是使用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
}
SwiftUI将Group
子视图平铺到Group
的父视图中(在这种情况下,平移到Picker
中)。每个Group
最多可以有10个子视图,这些子视图本身可以是Group
,因此,通过嵌套Group
,您可以在Picker
中任意包含许多显式元素。但我建议使用ForEach
。
如果您想了解10次子视图限制的来源,请编辑第二个示例,将Picker
存储在这样的变量中:
struct ContentView: View {
var body: some View {
let picker = Picker(selection: $value, label: Text("Pick One")) {
Group {
...
}
}
return picker
}
}
现在,在Xcode中单击picker
变量以查看其推断的类型:
我们重新格式化一下:
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)>>)>>
哇,真是个大人物! SwiftUI大量使用此类通用类型,因为它在运行时效率更高。由于这些都是符合struct
的{{1}}类型,因此Swift将整个View
及其所有子级都存储在单个连续的内存块中。该块可以从堆栈开始,只有在SwiftUI最终需要对其进行类型擦除或长期存储时,才需要将其复制到堆中。与UIKit相比,UIKit总是在创建时在堆上分别分配每个视图。
ViewBuilder
是可汇编这些复杂视图的SwiftUI实用程序。 Swift将每个Picker
的主体转换为对Group
的调用,而ViewBuilder.buildBlock
主体中的每个视图都作为Group
的单独参数。这些参数中的每一个都可以是单独的类型(例如,ViewBuilder.buildBlock
可以有一些Group
子级和某些Text
子级)。但是Swift不支持可变参数泛型,因此Image
必须定义一个具有单个视图的ViewBuilder
版本,一个具有两个视图的版本以及一个具有三个视图的版本,依此类推上。它不能定义无限数量的方法,因为SwiftUI框架将无限大。因此,它在10个参数处停止:
buildBlock
这就是为什么使用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
,和其他人,只能有10个直接子视图。