创建客户细分
After a few weeks off from SwiftUI content, I’m back! In this article, we’ll go over how to create a multi-segment picker. This might be useful for selecting a time duration (ex. one day, four weeks, etc.), which was my use case. This is the final product:
在关闭SwiftUI内容几周后,我回来了! 在本文中,我们将介绍如何创建多细分选择器。 这对于选择持续时间(例如一天,四个星期等)很有用,这就是我的用例。 这是最终产品:
Let’s first define a simple, single segment picker in SwiftUI:
我们首先在SwiftUI中定义一个简单的单段选择器:
struct PickerView: View {
// 1 Some trial data
@State var selectionIdx = 0
let data = ["1", "2", "3"]
var body: some View {
// 2 Define a picker
Picker("PickerName", selection: self.$selectionIdx) {
// 3 Populate with data
ForEach(0..<self.data.count) {
// 4 Display text from the data
Text(self.data[$0])
// Tag tells SwiftUI which selection to show - we tag with the index
.tag($0)
}
}
.pickerStyle(WheelPickerStyle()) // 6 We want the traditional wheel picker
}
}
Let’s go over this really quickly:
让我们快速浏览一下:
A SwiftUI picker needs some sort of
data
(i.e. what the picker values are). It also needs astate
or abinding
that informs the currently selected value.SwiftUI选择器需要某种
data
(即选择器的值是什么)。 它还需要通知当前选定值的state
或binding
。SwiftUI makes declaring a picker relatively easy. The first argument is the picker’s label and the second (
selection
) is a binding to the current selection. The last argument is a closure that creates a series of views. Each view is a selectable value.SwiftUI使声明选择器相对容易 。 第一个参数是选择器的标签,第二个参数(
selection
)是当前选择的绑定。 最后一个参数是创建一系列视图的闭包。 每个视图都是一个可选值。Inside the picker, we create a series of views with
ForEach
. Notice that instead of iterating overdata
itself, we iterate over the indices. This is fine as long as the number of items inForEach
doesn't change. If it does, consider conforming the items indata
toIdentifiable
.在选择器内部,我们使用
ForEach
创建了一系列视图。 注意,我们不是遍历data
本身,而是遍历索引。 只要ForEach
中的项目数不变,就可以了。 如果是这样,请考虑使data
的项目符合Identifiable
。We display the value using
Text
and give it atag
value of the index. This means that eachText
instance is associated with the index within the array. This works with theselection
argument for SwiftUI to determine which item to show as the current selection.我们使用
Text
显示该值,并为其指定索引的tag
值。 这意味着每个Text
实例都与数组中的索引关联。 这与SwiftUI的selection
参数一起使用,以确定将哪个项目显示为当前选择。
创建多个细分 (Creating Multiple Segments)
That was pretty standard, but how can we start defining multiple segments for the picker? In my opinion, it’s always good to start with the view model first. This way, we can cleanly describe what our incoming data looks like and create our view accordingly:
那是很标准的,但是我们如何开始为选择器定义多个细分呢? 我认为,首先从视图模型开始总是好的。 这样,我们可以清晰地描述传入数据的外观,并相应地创建视图:
struct MultiSegmentPickerViewModel {
typealias Label = String // The label for each picker
typealias Selection = Binding<Int> // The selection index for each picker
typealias PickerDisplayValues = [String] // The respective values for the picker
// A multi-segment picker is composed of an array of the above types
let segments: [(Label, Selection, PickerDisplayValues)]
}
// Create a test viewmodel
let testVm = MultiSegmentPickerViewModel(segments: [
("FirstPicker", .constant(0), ["1.1", "1.2", "1.3"]), // Values for first segment
("SecondPicker", .constant(1), ["2.1", "2.2", "2.3"]), // Values for second segment
])
Our view model has a single property, segments
, which is an array of tuples. Each tuple represents one segment within the picker. Each tuple is composed of:
我们的视图模型只有一个属性segments
,它是一个元组数组。 每个元组代表选择器中的一个细分。 每个元组包括:
A
String
label.String
标签。- A binding to a selection. In this case, we are using the selection index as an integer, but you can use any binding you see fit by changing the type alias. 对选择的绑定。 在这种情况下,我们将选择索引用作整数,但是您可以通过更改类型别名来使用您认为合适的任何绑定。
An array of values for the particular segment. We have defined these to be of type
String
, but again, the type alias can be used for your use case.特定细分受众群的值数组。 我们已经将它们定义为
String
类型,但是同样,类型别名可以用于您的用例。
Now we can define the view:
现在我们可以定义视图:
struct MultiSegmentPickerView: View {
let viewModel: MultiSegmentPickerViewModel
var body: some View {
// 1 Use geometry reader to get the parent view width
GeometryReader { geometry in
// 2 Use HStack to position views horizontally
HStack {
// 3 Iterate over the individual picker values
ForEach(0..<self.viewModel.segments.count, id: \.self) { pickerIndex in
// Define a picker
Picker(
self.viewModel.segments[pickerIndex].0, // Label
selection: self.viewModel.segments[pickerIndex].1 // Selection
) {
// Populate with data for this specific picker
ForEach(0..<self.viewModel.segments[pickerIndex].2.count) { pickerSelectionIndex in
Text(self.viewModel.segments[pickerIndex].2[pickerSelectionIndex])
// Tag tells SwiftUI which selection to show - we tag with the index
.tag(pickerSelectionIndex)
}
}
.pickerStyle(WheelPickerStyle())
// 4 Size each picker to the allowed fraction of the total width
.frame(width: geometry.size.width / CGFloat(self.viewModel.segments.count))
// 5 Clip to the given width, otherwise the picker lines will overflow & intersect
.clipped()
}
}
}
}
}
Nice! This is exactly what we want. Let’s go over the view. Notice that we are declaring a collection of pickers arranged horizontally using HStack
:
真好! 这正是我们想要的。 让我们来看一下。 注意,我们声明了使用HStack
水平排列的选择器的集合:
For us to be able to define multiple segments, we must size each picker accordingly.
GeometryReader
gives us the sizing of the parent view. We can then use this to frame eachPicker
accordingly.为了能够定义多个细分,我们必须相应地调整每个选择器的大小。
GeometryReader
为我们提供了父视图的大小。 然后,我们可以使用它来相应地构造每个Picker
。As mentioned, for us to lay out the pickers in a horizontal fashion, we use
HStack
.如前所述,对于我们以水平方式布置选择器的方式,我们使用
HStack
。We iterate over the different picker segments and define a picker for each segment using
ForEach
. Notice that the definition for each individual picker is the same as our initial example of the single-segment picker.我们遍历不同的选择器段,并使用
ForEach
为每个段定义一个选择器。 请注意,每个单个选择器的定义与我们的单段选择器的初始示例相同。We use
.frame(...)
to size each picker to the appropriate width, which is the parent width divided by the number of pickers.我们使用
.frame(...)
将每个选取器调整为适当的宽度,该宽度是父宽度除以选取器的数量。Using
.clipped()
is key here. If we don't use the modifier, you'll see that the lines of the picker will overlap.clipped()
will make sure that each picker does not extend beyond the bounds of each view.这里使用
.clipped()
是关键。 如果不使用修饰符,您将看到选择器的行将重叠。clipped()
将确保每个选择器都不会超出每个视图的范围。
And there you go: multi-segment pickers in SwiftUI. Visit the GitHub playground with all the code for the multi-segment picker.
随您去:SwiftUI中的多细分选择器。 访问GitHub游乐场 ,获取多细分选择器的所有代码。
翻译自: https://medium.com/better-programming/how-to-multi-segment-picker-in-swiftui-9c5b909971f5
创建客户细分