如何增加 SwiftUI 选择器中显示的最大行数?

Rya*_*son 1 picker swiftui

我正在尝试创建一个 SwiftUI 选择器,用户可以使用它来选择 1000 到 20000 之间的数字(以 1000 为增量。例如 1000,2000,3000 .... ... 20000)

默认情况下,SwiftUI 选择器只能保存 10 行文本。如何让 SwiftUI 选择器包含 20 行文本?

rob*_*off 6

我猜你写的是这样的:

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.buildBlockGroup主体内的每个视图都作为对 的单独参数ViewBuilder.buildBlock。这些参数中的每一个都可以是一个单独的类型(例如,aGroup可以有一些Text孩子和一些Image孩子)。但是 Swift 不支持可变参数泛型,因此ViewBuilder必须定义一个buildBlock接受单个视图的版本,一个接受两个视图的版本,一个接受三个视图的版本,依此类推。它不能定义无限多个方法,因为那样 SwiftUI 框架将是无限大的。所以它在 10 个参数处停止:

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)>
Run Code Online (Sandbox Code Playgroud)

这就是为什么任何观点,其内容使用的定义ViewBuilder(其中包括VStackHStackZStackPickerListGroup,和其他人)最多只能有10个直接子视图。