如何增加SwiftUI选择器中显示的最大行数? [英] How can I increase maximum number of rows that are shown in a SwiftUI picker?

查看:92
本文介绍了如何增加SwiftUI选择器中显示的最大行数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a SwiftUI picker that users can use to select a number from 1000 to 20000 (in increments of 1000. for example 1000,2000,3000 .... ... 20000)

默认情况下,SwiftUI选择器只能容纳10行文本.如何允许SwiftUI选择器包含20行文本?

By default the SwiftUI picker can only hold 10 rows of text. How can I allow the SwiftUI picker to contain 20 rows of text?

推荐答案

我猜你写了这样的东西:

I guess you wrote something like this:

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添加一行并收到此错误:

And then you tried to add a row for 11000 and got this error:

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个子视图.

The problem is that, due to limitations in the Swift language and in how SwiftUI is implemented, you can only have 10 subviews in a @ViewBuilder body.

有两种方法可以解决此问题.

Here are two ways to work around this.

一种适合您的设计的方法是使用ForEach:

One way, which is appropriate for your design, is to use 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将商品分组:

The other way, which would be more appropriate if your items didn't follow a simple pattern, is to group your items using 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.

SwiftUI flattens the Group subviews into the Group's parent (in this case, into the Picker). Each Group can have up to 10 subviews, which can themselves be Groups, so by nesting Groups you can have arbitrarily many explicit elements in your Picker. But I recommend using ForEach.

如果您想了解10次子视图限制的来源,请编辑第二个示例,将Picker存储在这样的变量中:

If you want to understand where the 10 subview limit comes from, edit my second example, storing the Picker in a variable like this:

struct ContentView: View {
    var body: some View {
        let picker = Picker(selection: $value, label: Text("Pick One")) {
            Group {
            ...
            }
        }
        return picker
    }
}

现在,在Xcode中单击picker变量以查看其推断的类型:

Now option-click the picker variable in Xcode to see its inferred type:

我们重新格式化一下:

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大量使用此类通用类型,因为它在运行时效率更高.因为这些都是符合Viewstruct类型,所以Swift将整个Picker及其所有子级存储在单个连续的内存块中.该块可以从堆栈开始,只有在SwiftUI最终需要对其进行类型擦除或长期存储时,才需要将其复制到堆中.与UIKit相比,UIKit总是在创建时在堆上分别分配每个视图.

Wow, that's a big type! SwiftUI uses generic types like this heavily because it is more efficient at runtime. Because these are all struct types conforming to View, Swift stores this whole Picker and all its children in a single contiguous block of memory. That block can start out on the stack and only needs to be copied to the heap when SwiftUI eventually needs to type-erase it or store it long-term. Compare to UIKit, where every view is always separately allocated on the heap on creation.

ViewBuilder 是组装这些复杂视图的SwiftUI实用程序. Swift将每个Group的主体转换为对ViewBuilder.buildBlock的调用,而Group主体内部的每个视图均作为ViewBuilder.buildBlock的单独参数.这些参数中的每一个都可以是单独的类型(例如,Group可以具有一些Text子代和一些Image子代).但是Swift不支持可变参数泛型,因此ViewBuilder必须定义具有单个视图的buildBlock版本,具有两个视图的版本以及具有三个视图的版本,依此类推.它不能定义无数种方法,因为SwiftUI框架将无穷大.因此,它在10个参数处停止:

ViewBuilder is the SwiftUI utility that assembles these complex views. Swift transforms the body of each Group into a call to ViewBuilder.buildBlock, with each view inside the Group body as a separate argument to ViewBuilder.buildBlock. Each of those arguments could be a separate type (e.g. a Group could have some Text children and some Image children). But Swift doesn't support variadic generics, so ViewBuilder has to define a version of buildBlock that takes a single view, and a version that takes two views, and a version that takes three views, and so on. It cannot define an infinite number of methods, because then the SwiftUI framework would be infinitely large. So it stops at 10 arguments:

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定义内容的任何视图(包括VStackHStackZStackPickerListGroup等)只能具有10个直接视图的原因子视图.

That's why any view whose content is defined using ViewBuilder (which includes VStack, HStack, ZStack, Picker, List, Group, and others) can only have 10 direct subviews.

这篇关于如何增加SwiftUI选择器中显示的最大行数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆