Skip to main content

SwiftUI专辑005-各种pickers

· 预计阅读8分钟

headerimg pickers是用来提供给用户选择某个值的。 本文将介绍以下的pickers

  • Picker
  • Toggle
  • Slider
  • Stepper
  • DatePicker
  • ColorPicker

Getting ready

首先,新建一个SwiftUI工程,叫做PickersApp

How to do it…

  1. ContentView.swift创建好State,以绑定各个pickers的value。
@State var choice = 0
@State var showText = false
@State var transitModes = ["Bike", "Car", "Bus"]
@State var sliderVal: Float = 0
@State var stepVal = 0
@State var gameTime = Date()
  1. 在Body中加入一个FormSectionPicker
struct ContentView: View {
@State var choice = 0
@State var showText = false
@State var transitModes = ["Bike", "Car", "Bus"]
@State var sliderVal: Float = 0
@State var stepVal = 0
@State var gameTime = Date()

var body: some View {
Form {
Section {
Picker("Transit Modes", selection: $choice) {
ForEach(0..<transitModes.count) { index in
Text("\(self.transitModes[index])")
}
}.pickerStyle(.segmented)
Text("Current choice: \(self.transitModes[choice])")
}
}
}
}
  1. 再添加一个section,加入Toggle
Section {
Toggle(isOn: $showText) {
Text("Show Text")
}
if showText {
Text("The Text toggle is on")
}
}
  1. 再添加一个section,加入Slider
Section {
Slider(value: $sliderVal, in: 0...10, step: 0.001)
Text("Slider current value\(sliderVal, specifier: "%.1f")")
}
  1. 再添加一个section,加入Stepper
Section {
Stepper("Stepper", value: $stepVal, in: 0...5)
Text("Stepper current value\(stepVal)")
}
  1. 再添加一个section,加入DatePicker
 Section {
DatePicker("Please select a date", selection: $gameTime)
}
  1. 再添加一个section,加入DatePicker,再限制下时间范围
Section {
DatePicker("select a date", selection: $gameTime, in: Date()...)
}

最终如下

image-20211223113151810

How it works…

Picker是用来从一组值中选择某一个的,设置style为Segmented就得到了一个UIKit的SegmentView。

Toggle控制开和关,当前状态存储在binding的vlaue中。

Slider需要3个参数:

  • value: 当前值
  • in: slider的范围
  • step: 步进的值

Stepper也同Slider一样需要那3个参数。

最后演示了一下DatePicker的使用,注意binding的类型是date。

picker的style会根据他的容器不同而不同。比如在Form或者List中的picker会和在VStack中的不一样。

我们可以通过.pickerStyle来修改默认style。

Api详情

Picker

Creating a Picker

///SelectionValue遵循Hashable, Content和Label都遵循View
init(selection: Binding<SelectionValue>, content: () -> Content, label: () -> Label)

注意Picker的每个选项都需要带上tag,而且tag的值和类型要和Binding的变量一样。

enum Flavor: String, CaseIterable {
case chocolate
case vanilla
case strawberry
}
struct ContentView: View {
@State var selectedFlavor: Flavor = .chocolate
var body: some View {
Form {
Section {
Picker("Flaver", selection: $selectedFlavor) {
//注意需要标上tag,而且类型需要和Binding的一样
Text("\(Flavor.chocolate.rawValue)").tag(Flavor.chocolate)
Text("\(Flavor.vanilla.rawValue)").tag(Flavor.vanilla)
Text("\(Flavor.strawberry.rawValue)").tag(Flavor.strawberry)
}.pickerStyle(.segmented)
Text("Current Flaver: \(selectedFlavor.rawValue)")
}
}
}
}

可能有人就有疑问了,为什么在how to do那里的Picker的例子就没有打tag呢?

因为ForEach会自动为你打tag,这个tag的值是遵循了Identifiable协议的对象的id字段。所以之前的那个例子不用显示的带上tag。如果我们要改写上面的例子用ForEach实现呢?

///遵循Identifiable协议
enum Flavor: String, CaseIterable, Identifiable {
case chocolate
case vanilla
case strawberry
var id: Flavor { self }
}
struct ContentView: View {
@State var selectedFlavor: Flavor = .chocolate
var body: some View {
Form {
Section {
Picker("Flaver", selection: $selectedFlavor) {
// 不用显示的打tag,默认用id作为tag,id和Binding都是Flavor类型的
// 当然,如果你愿意,也可以自己显示的指定tag
ForEach(Flavor.allCases) { flavor in
Text("\(flavor.rawValue)")
}
}.pickerStyle(.segmented)
Text("Current Flaver: \(selectedFlavor.rawValue)")
}
}
}
}

Styling Pickers

/// 设置picker的样式
func pickerStyle<S>(_ style: S) -> some View where S : PickerStyle
//segment样式
static var segmented: SegmentedPickerStyle { get }
//类似于UIPicker的那种滚轮样式
static var wheel: WheelPickerStyle { get }

image-20211223142959083

//macOS上的勾选样式
static var radioGroup: RadioGroupPickerStyle { get }
//菜单,类似于UIPopoverController,点了出现小弹窗,包含所有选项
static var menu: MenuPickerStyle { get }

image-20211223142823304

//所有选项都同时显示出来,
static var inline: InlinePickerStyle { get }

image-20211223142709578

Toggle

Creating a Toggle

init(isOn: Binding<Bool>, label: () -> Label)
...

Creating a Toggle from a Configuration

//通常用在自定义ToggleStyle中
init(_ configuration: ToggleStyleConfiguration)

举个例子

struct RedBorderToggleStyle: ToggleStyle {
func makeBody(configuration: Configuration) -> some View {
Toggle(configuration)//通过cinfiguration初始化
.padding()
.border(Color.red)
}
}

Toggle(isOn: $showText) {
Text("Show Text")
}.toggleStyle(RedBorderToggleStyle())

Styling a Toggle

func toggleStyle<S>(_ style: S) -> some View where S : ToggleStyle

下面是一些默认的style

/// switch样式
static var `switch`: SwitchToggleStyle { get }
/// label作为btn, on的时候,背景设为tintColor
static var button: ButtonToggleStyle { get }

image-20211223144303808

/// 勾选框,只有macOS有
static var checkbox: CheckboxToggleStyle { get }

image-20211223144416027

Slider

/**
value : binding的值
bounds: 值的范围
step: 步进的大小
label: 描述的view
minimumValueLabel
maximumValueLabel最大和最小端的view
*/
init<V>(value: Binding<V>, in bounds: ClosedRange<V>, step: V.Stride = 1, label: () -> Label, minimumValueLabel: () -> ValueLabel, maximumValueLabel: () -> ValueLabel, onEditingChanged: @escaping (Bool) -> Void = { _ in }) where V : BinaryFloatingPoint, V.Stride : BinaryFloatingPoint

Stepper

Creating a Stepper

/**
value : binding的值
bounds: 值的范围
step: 步进的大小
label: 描述的view
*/
init<V>(value: Binding<V>, in bounds: ClosedRange<V>, step: V.Stride = 1, label: () -> Label, onEditingChanged: @escaping (Bool) -> Void = { _ in }) where V : Strideable

Creating a Stepper with Specified Increment and Decrement Behavior

// 不在和某个value绑定,将两个按钮的事件暴露出来,提供给你自定义你的逻辑
init(label: () -> Label, onIncrement: (() -> Void)?, onDecrement: (() -> Void)?, onEditingChanged: @escaping (Bool) -> Void = { _ in })

DatePicker

Creating a Date Picker

init(selection: Binding<Date>, displayedComponents: DatePicker<Label>.Components = [.hourAndMinute, .date], label: () -> Label)
/// 可以指定range, start...end
init(selection: Binding<Date>, in range: ClosedRange<Date>, displayedComponents: DatePicker<Label>.Components = [.hourAndMinute, .date], label: () -> Label)
/// 可以指定range, start...
init(selection: Binding<Date>, in range: PartialRangeFrom<Date>, displayedComponents: DatePicker<Label>.Components = [.hourAndMinute, .date], label: () -> Label)
/// 可以指定range, ...end
init(selection: Binding<Date>, in range: PartialRangeThrough<Date>, displayedComponents: DatePicker<Label>.Components = [.hourAndMinute, .date], label: () -> Label)

Setting Date Picker Components

//设置显示的components
static let date: DatePickerComponents// 年月日
static let hourAndMinute: DatePickerComponents//小时,分钟

Styling Date Pickers

///设置picker样式
func datePickerStyle<S>(_ style: S) -> some View where S : DatePickerStyle
static var wheel: WheelDatePickerStyle { get }

image-20211223152724050

/// macOS上特有的,类似于Stepper
static var stepperField: StepperFieldDatePickerStyle { get }
/// macOS上特有的
static var field: FieldDatePickerStyle { get }
static var graphical: GraphicalDatePickerStyle { get }

image-20211223152907356

///component以文本格式显示
static var compact: CompactDatePickerStyle { get }

image-20211223153206130

ColorPicker

选择颜色

///绑定Color
init(selection: Binding<Color>, supportsOpacity: Bool = true, label: () -> Label)
///绑定CGColor
init(selection: Binding<CGColor>, supportsOpacity: Bool = true, label: () -> Label)
ColorPicker(selection: $color) {
Text("选择颜色")
}

image-20211223153959117