iOS常用控件详解
1. UISwitch相关说明
在iOS 6中开发者呼吁并经过一些“破解”后添加的UISwitch的
onImage
和
offImage
属性,在iOS 7及以后版本中已不起作用。
2. UIStepper
2.1 基本介绍
UIStepper允许用户增加或减少一个数值。它看起来像两个并排的按钮,一个(默认)标有减号,另一个标有加号。用户可以点击或按住按钮,还可以在两个按钮间滑动手指进行交互。它只有一种尺寸,任何设置其大小的尝试都会被忽略。它维护一个数值,即
value
属性。每次用户增加或减少数值时,数值会按
stepValue
改变。如果达到
minimumValue
或
maximumValue
,相应的按钮会被禁用,除非
wraps
属性为
true
,此时数值会从最小值重新开始超过最大值,反之亦然。当用户改变步进器的值时,会报告一个
Value Changed
控制事件。
以下是一个使用UIStepper更新进度视图的示例代码:
@IBAction func doStep(_ sender: Any) {
let step = sender as! UIStepper
self.prog.setProgress(
Float(step.value / (step.maximumValue - step.minimumValue)),
animated:true)
}
2.2 特性设置
-
连续更新
:如果
isContinuous为true(默认值),长按其中一个按钮会反复更新值,更新开始较慢,然后变快。 -
自动重复
:如果
autorepeat为false,直到与步进器的整个交互结束,更新的值才会作为Value Changed控制事件报告,默认值为true。
2.3 外观定制
步进器的外观可以定制。其轮廓和按钮标题的颜色由
tintColor
决定,该颜色可能从视图层次结构的上级继承。还可以使用以下方法指定构成步进器结构的图像:
-
setDecrementImage(_:for:)
-
setIncrementImage(_:for:)
-
setDividerImage(_:forLeftSegmentState:rightSegmentState:)
-
setBackgroundImage(_:for:)
这些图像的工作方式与搜索栏的范围栏类似。背景图像应该是可调整大小的,它们会拉伸到两个按钮后面,图像的一半作为每个按钮的背景。如果按钮被禁用,它会显示
.disabled
背景图像;否则,显示
.normal
背景图像,用户点击时显示
.highlighted
背景图像。如果要提供背景图像,可能需要提供所有三种状态的图像;如果某个状态的背景图像为
nil
,则使用默认图像。同样,可能也需要提供三种分隔图像,以覆盖一个或两个分段被高亮显示的三种组合。增量和减量图像会替换默认的减号和加号,它们会合成在背景图像之上,除非明确提供
.alwaysOriginal
图像,否则它们会被视为模板图像,由
tintColor
着色。如果只提供
.normal
图像,它会自动调整以适应其他两种状态。
3. UIPageControl
3.1 基本介绍
UIPageControl是一排点,每个点称为一个页面,它通常与其他类似页面的界面(如
isPagingEnabled
设置为
true
的
UIScrollView
)结合使用。通常需要自己协调页面控制与其他界面的交互。
3.2 常用属性和方法
-
页面数量
:点的数量由
numberOfPages属性决定。可以调用size(forNumberOfPages:)方法来了解容纳给定数量点所需的最小边界大小。可以使页面控制比点更宽,以增加用户可点击的目标区域。 -
点击交互
:用户可以点击当前页面点的一侧来增加或减少当前页面,此时页面控制会报告一个
Value Changed控制事件。 -
颜色定制
:点的颜色可以区分当前页面(
currentPage)和其他页面。默认情况下,当前页面显示为实心点,其他点稍有透明度。可以自定义pageIndicatorTintColor(一般点的颜色)和currentPageIndicatorTintColor(当前页面点的颜色),因为默认的点颜色是白色,在正常情况下可能很难看到。 -
背景颜色
:可以设置
backgroundColor,以显示用户可点击的区域,或通过对比使点更清晰可见。 -
单页隐藏
:如果
hidesForSinglePage为true,当numberOfPages变为1时,页面控制会变得不可见。 -
延迟显示
:如果
defersCurrentPageDisplay为true,当用户点击增加或减少页面控制的值时,当前页面的显示不会改变。会报告一个Value Changed控制事件,但需要自己的代码处理此操作并调用updateCurrentPageDisplay。
4. UIDatePicker
4.1 基本介绍
UIDatePicker看起来像
UIPickerView
,但它不是
UIPickerView
的子类,它使用
UIPickerView
来绘制自身,但不提供对该选择器视图的官方访问。其目的是表达日期和时间的概念,处理日历和数字的复杂性,让开发者无需处理这些细节。当用户更改其设置时,日期选择器会报告一个
Value Changed
控制事件。
4.2 模式设置
UIDatePicker有四种模式(
datePickerMode
),决定其绘制方式:
| 模式 | 显示内容 |
| ---- | ---- |
|
.time
| 显示时间,例如有小时和分钟组件 |
|
.date
| 显示日期,例如有月、日和年组件 |
|
.dateAndTime
| 显示日期和时间,例如显示星期几、月、日,以及小时和分钟组件 |
|
.countDownTimer
| 显示小时和分钟数,例如有小时和分钟组件 |
4.3 显示设置
- 区域设置影响 :日期选择器显示的具体组件和值默认取决于用户在设置应用(通用 → 语言与地区 → 地区)中的偏好。例如,美国时间显示1到12的小时数加上分钟和AM或PM,而英国时间显示1到24的小时数加上分钟。如果用户在设置应用中更改区域格式,日期选择器的显示会立即改变。
-
日历和时区
:日期选择器有
calendar和timeZone属性,分别为Calendar和TimeZone,默认值为nil,这意味着日期选择器会响应用户的系统级设置。也可以手动更改这些值,但不要更改.countDownTimer模式的日期选择器的timeZone,否则显示的值会偏移,会让自己和用户感到困惑。 -
分钟间隔
:如果有分钟组件,默认会显示每分钟,但可以通过
minuteInterval属性更改。最大值为30,此时分钟组件的值为0和30。任何将minuteInterval设置为不能被60整除的值的尝试都会被默默忽略。
4.4 日期设置
日期选择器表示的日期(除非其模式为
.countDownTimer
)是
date
属性,类型为
Date
。默认日期是日期选择器实例化时的当前时间。对于
.date
模式的日期选择器,默认时间是当地时间的午夜12点;对于
.time
模式的日期选择器,默认日期是今天。内部值以当地时区计算,因此如果更改了日期选择器的
timeZone
,它可能与显示的值不同。
4.5 最大最小日期
日期选择器中启用的最大和最小日期由
maximumDate
和
minimumDate
属性决定。超出此范围的值可能会显示为禁用状态。日期选择器可以显示的范围实际上没有实际限制,因为表示其组件的“滚筒”不是物理的,并且值会在用户旋转时动态添加。以下是一个设置日期选择器初始最小和最大日期的示例代码:
dp.datePickerMode = .date
var dc = DateComponents(year:1954, month:1, day:1)
let c = Calendar(identifier:.gregorian)
let d1 = c.date(from: dc)!
dp.minimumDate = d1
dp.date = d1
dc.year = 1955
let d2 = c.date(from: dc)!
dp.maximumDate = d2
4.6 倒计时模式
.countDownTimer
模式的日期选择器显示的是
countDownDuration
,这是一个
TimeInterval
,是一个表示秒数的双精度浮点数,尽管显示的最小间隔是分钟。
.countDownTimer
日期选择器实际上不会进行倒计时,需要以其他方式进行倒计时,并使用其他界面来显示倒计时。苹果时钟应用的定时器标签显示了一个典型的界面,用户最初配置选择器视图来设置
countDownDuration
,但一旦开始倒计时,选择器视图会隐藏,一个标签会显示剩余时间。需要注意的是,
.countDownTimer
日期选择器的
Value Changed
事件不可靠(尤其是在应用启动后不久,以及用户尝试将定时器设置为零时)。解决方法是不要依赖
Value Changed
事件,例如在界面中提供一个按钮,让用户点击以让代码读取日期选择器的
countDownDuration
。
4.7 日期转换
要在
Date
和字符串之间进行转换,需要使用
DateFormatter
:
@IBAction func dateChanged(_ sender: Any) {
let dp = sender as! UIDatePicker
let d = dp.date
let df = DateFormatter()
df.timeStyle = .full
df.dateStyle = .full
print(df.string(from: d))
// Tuesday, August 10, 1954 at 3:16:00 AM GMT-07:00
}
5. UISlider
5.1 基本介绍
UISlider表示一个可连续设置的值(
value
,类型为
Float
),介于某个最小值和最大值(
minimumValue
和
maximumValue
,默认值分别为0和1)之间。它表现为一个沿着轨道定位的对象,即滑块。当用户更改滑块的位置时,滑块会报告一个
Value Changed
控制事件。如果
isContinuous
为
true
(默认值),用户按下并拖动滑块时会连续报告事件;如果为
false
,则仅在用户释放滑块时报告事件。当用户按下滑块时,滑块处于
.highlighted
状态。要通过动画更改滑块的值,可以在动画函数中调用
setValue(_:animated:)
。
5.2 点击轨道响应
通常希望修改滑块的行为,使用户点击其轨道时,滑块移动到用户点击的位置。但滑块本身不会响应轨道上的点击,不会报告控制事件。不过,可以使用手势识别器实现此功能,以下是一个附加到UISlider的
UITapGestureRecognizer
的操作方法:
@objc func tapped(_ g:UIGestureRecognizer) {
let s = g.view as! UISlider
if s.isHighlighted {
return // tap on thumb, let slider deal with it
}
let pt = g.location(in:s)
let track = s.trackRect(forBounds: s.bounds)
if !track.insetBy(dx: 0, dy: -10).contains(pt) {
return // not on track, forget it
}
let percentage = pt.x / s.bounds.size.width
let delta = Float(percentage) * (s.maximumValue - s.minimumValue)
let value = s.minimumValue + delta
delay(0.1) {
UIView.animate(withDuration: 0.15) {
s.setValue(value, animated:true) // animate sliding the thumb
}
}
}
5.3 外观定制
-
颜色设置
:滑块的
tintColor(可能从视图层次结构的上级继承)决定了滑块左侧轨道的颜色。可以使用minimumTrackTintColor和maximumTrackTintColor属性更改轨道两部分的颜色,使用thumbTintColor属性更改滑块的颜色。 -
轨道图像
:轨道两端的图像是
minimumValueImage和maximumValueImage,默认值为nil。如果将它们设置为实际图像,滑块会尝试在其自身边界内定位它们,并缩小轨道的绘制以进行补偿。可以在子类中重写以下方法来更改此行为: -
minimumValueImageRect(forBounds:) -
maximumValueImageRect(forBounds:) -
trackRect(forBounds:)
以下是一个扩展轨道宽度到滑块全宽,并将图像绘制在滑块边界外的示例:
override func maximumValueImageRect(forBounds bounds: CGRect) -> CGRect {
return super.maximumValueImageRect(
forBounds:bounds).offsetBy(dx: 31, dy: 0)
}
override func minimumValueImageRect(forBounds bounds: CGRect) -> CGRect {
return super.minimumValueImageRect(
forBounds: bounds).offsetBy(dx: -31, dy: 0)
}
override func trackRect(forBounds bounds: CGRect) -> CGRect {
var result = super.trackRect(forBounds: bounds)
result.origin.x = 0
result.size.width = bounds.size.width
return result
}
-
滑块图像
:滑块也是一个图像,可以使用
setThumbImage(_:for:)方法设置。主要有两种相关状态,.normal和.highlighted。如果为这两种状态都提供图像,用户拖动滑块时滑块会自动更改。默认情况下,图像会在轨道中以滑块当前值表示的点为中心;可以在子类中重写thumbRect(forBounds:trackRect:value:)方法来移动此位置。例如:
override func thumbRect(forBounds bounds: CGRect,
trackRect rect: CGRect, value: Float) -> CGRect {
return super.thumbRect(forBounds: bounds,
trackRect: rect, value: value).offsetBy(dx: 0, dy: -7)
}
需要注意的是,增大或偏移滑块的大小可能会误导用户对可触摸拖动区域的判断。滑块本身是可触摸的
UIControl
,只有与滑块边界相交的滑块部分才是可拖动的。用户可能会尝试拖动绘制在滑块边界外的部分,这会失败并让用户感到困惑。一种解决方案是增加滑块的高度,如果使用自动布局,可以在nib编辑器中添加明确的高度约束,或在代码中重写
intrinsicContentSize
;另一种解决方案是创建子类并使用点击测试修改。
-
轨道图像设置
:轨道由两个图像组成,一个在滑块左侧,一个在右侧。可以使用
setMinimumTrackImage(_:for:)和setMaximumTrackImage(_:for:)方法设置。如果为.normal和.highlighted状态都提供图像,用户拖动滑块时图像会更改。图像应该是可调整大小的,这样滑块才能巧妙地让用户感觉是在沿着单个静态轨道拖动滑块。实际上有两个图像,当用户拖动滑块时,一个图像水平增长,另一个图像水平缩小。
以下是一个使用单个15×15圆形对象(硬币)图像创建轨道的示例:
let coinEnd = UIImage(named:"coin")!.resizableImage(withCapInsets:
UIEdgeInsets(top: 0, left: 7, bottom: 0, right: 7), resizingMode: .stretch)
self.setMinimumTrackImage(coinEnd, for:.normal)
self.setMaximumTrackImage(coinEnd, for:.normal)
6. UISegmentedControl
6.1 基本介绍
UISegmentedControl是一排可点击的分段,每个分段类似于一个按钮。用户点击分段来选择选项。默认情况下(
isMomentary
为
false
),最近点击的分段会保持选中状态。如果
isMomentary
为
true
,点击的分段会短暂高亮显示(默认情况下,高亮显示与选中状态无区别,但可以更改),之后不会显示分段选择,但内部被点击的分段仍然是选中分段。
6.2 选中状态管理
-
选中分段索引
:可以使用
selectedSegmentIndex属性设置和获取选中的分段。当在代码中设置该属性时,即使是isMomentary为true的分段控件,选中的分段也会保持可见选中状态。selectedSegmentIndex值为UISegmentedControlNoSegment表示没有分段被选中。当用户点击尚未可见选中的分段时,分段控件会报告一个Value Changed事件。 - 动画选择 :分段控件的选择更改是可动画的,可以在动画函数中更改选择,例如:
UIView.animateWithDuration(0.4, animations: {
self.seg.selectedSegmentIndex = 1
})
要在用户点击分段时更缓慢地动画更改,可以将分段控件的图层速度设置为小数。
6.3 分段状态管理
-
启用状态
:可以使用
setEnabled(_:forSegmentAt:)方法分别启用或禁用分段,并使用isEnabledForSegment(at:)方法获取其启用状态。默认情况下,禁用的分段会绘制为褪色状态,用户无法点击,但仍可以在代码中选择它。 -
颜色设置
:分段控件的轮廓和选择的颜色由
tintColor决定,该颜色可能从视图层次结构的上级继承。
6.4 分段内容设置
分段可以有标题或图像,设置其中一个时,另一个会变为
nil
。图像被视为模板图像,由
tintColor
着色,除非明确提供
.alwaysOriginal
图像。标题由
tintColor
着色,除非设置其属性以包含不同的颜色。设置和获取现有分段的标题和图像的方法如下:
-
setTitle(_:forSegmentAt:)
,
titleForSegment(at:)
-
setImage(_:forSegmentAt:)
,
imageForSegment(at:)
如果在代码中创建分段控件,可以使用
init(items:)
方法配置分段,该方法接受一个数组,每个项可以是字符串或图像:
let seg = UISegmentedControl(items:
[UIImage(named:"one")!.withRenderingMode(.alwaysOriginal), "Two"])
seg.frame.origin = CGPoint(30,30)
self.view.addSubview(seg)
6.5 分段动态管理
以下是动态管理分段的方法:
-
insertSegment(withTitle:at:animated:)
-
insertSegment(with:at:animated:)
(第一个参数是
UIImage
)
-
removeSegment(at:animated:)
-
removeAllSegments
可以使用只读的
numberOfSegments
属性获取分段的数量。
6.6 分段大小和位置设置
-
分段大小
:如果
apportionsSegmentWidthsByContent属性为false,分段大小会相等;如果为true,每个分段的宽度会根据其内容单独调整。也可以使用setWidth(_:forSegmentAt:)方法显式设置分段的宽度,并使用widthForSegment(at:)方法获取宽度。将分段宽度设置为0表示该分段将自动调整大小。 -
内容偏移
:可以使用
setContentOffset(_:forSegmentAt:)方法更改分段内内容(标题或图像)的位置,并使用contentOffsetForSegment(at:)方法获取偏移量。
6.7 外观定制
可以使用以下方法定制分段控件的外观,这些方法与设置步进器或搜索栏的范围栏外观的方法类似:
-
setBackgroundImage(_:for:barMetrics:)
-
setDividerImage(_:forLeftSegmentState:rightSegmentState:barMetrics:)
-
setTitleTextAttributes(_:for:)
-
setContentPositionAdjustment(_:forSegmentType:barMetrics:)
7. 控件特性总结
7.1 事件响应总结
| 控件名称 | 主要事件 | 说明 |
|---|---|---|
| UIStepper | Value Changed | 用户改变步进器的值时触发 |
| UIPageControl | Value Changed | 用户点击改变当前页面时触发 |
| UIDatePicker | Value Changed |
用户更改日期选择器设置时触发,但
.countDownTimer
模式下不可靠
|
| UISlider | Value Changed |
用户更改滑块位置时触发,
isContinuous
决定触发时机
|
| UISegmentedControl | Value Changed | 用户点击未选中的分段时触发 |
7.2 外观定制总结
不同控件有不同的外观定制方式,以下是一个简单的对比:
| 控件名称 | 主要外观定制属性和方法 |
| ---- | ---- |
| UIStepper |
tintColor
、
setDecrementImage
、
setIncrementImage
、
setDividerImage
、
setBackgroundImage
|
| UIPageControl |
pageIndicatorTintColor
、
currentPageIndicatorTintColor
、
backgroundColor
|
| UIDatePicker | 主要受系统设置影响,可设置
calendar
、
timeZone
、
minuteInterval
等 |
| UISlider |
tintColor
、
minimumTrackTintColor
、
maximumTrackTintColor
、
thumbTintColor
、
setThumbImage
、
setMinimumTrackImage
、
setMaximumTrackImage
等 |
| UISegmentedControl |
tintColor
、
setTitle
、
setImage
、
setBackgroundImage
、
setDividerImage
、
setTitleTextAttributes
等 |
7.3 控件使用流程图
graph LR
A[选择控件类型] --> B{UIStepper}
A --> C{UIPageControl}
A --> D{UIDatePicker}
A --> E{UISlider}
A --> F{UISegmentedControl}
B --> B1[设置初始值和范围]
B --> B2[定制外观]
B --> B3[处理Value Changed事件]
C --> C1[设置页面数量]
C --> C2[定制颜色]
C --> C3[处理Value Changed事件]
D --> D1[选择模式]
D --> D2[设置日期范围和间隔]
D --> D3[处理Value Changed事件(注意.countDownTimer模式)]
E --> E1[设置最小值和最大值]
E --> E2[定制轨道和滑块外观]
E --> E3[处理Value Changed事件]
F --> F1[配置分段内容]
F --> F2[设置选中状态和动画]
F --> F3[处理Value Changed事件]
8. 实际应用场景示例
8.1 音乐播放应用中的控件使用
在音乐播放应用中,可以综合使用这些控件来提供更好的用户体验:
-
UISlider
:用于控制音量大小,用户拖动滑块可以连续调整音量。
let volumeSlider = UISlider()
volumeSlider.minimumValue = 0
volumeSlider.maximumValue = 1
volumeSlider.value = 0.5
volumeSlider.addTarget(self, action: #selector(volumeChanged), for:.valueChanged)
self.view.addSubview(volumeSlider)
@objc func volumeChanged(_ sender: UISlider) {
// 根据滑块的值调整音量
let volume = sender.value
// 实现音量调整逻辑
}
- UISegmentedControl :用于切换播放模式,如单曲循环、顺序播放、随机播放。
let playModeSegment = UISegmentedControl(items: ["单曲循环", "顺序播放", "随机播放"])
playModeSegment.selectedSegmentIndex = 1
playModeSegment.addTarget(self, action: #selector(playModeChanged), for:.valueChanged)
self.view.addSubview(playModeSegment)
@objc func playModeChanged(_ sender: UISegmentedControl) {
let selectedIndex = sender.selectedSegmentIndex
// 根据选中的分段设置播放模式
}
- UIDatePicker :用于设置定时关闭播放的时间。
let timerPicker = UIDatePicker()
timerPicker.datePickerMode = .countDownTimer
timerPicker.addTarget(self, action: #selector(timerChanged), for:.valueChanged)
self.view.addSubview(timerPicker)
@objc func timerChanged(_ sender: UIDatePicker) {
let duration = sender.countDownDuration
// 根据时长设置定时关闭逻辑
}
8.2 图片浏览应用中的控件使用
在图片浏览应用中:
-
UIPageControl
:与
UIScrollView
配合使用,显示当前图片的页码。
let pageControl = UIPageControl()
pageControl.numberOfPages = imageCount
pageControl.currentPage = 0
pageControl.addTarget(self, action: #selector(pageChanged), for:.valueChanged)
self.view.addSubview(pageControl)
@objc func pageChanged(_ sender: UIPageControl) {
let currentPage = sender.currentPage
// 根据当前页码滚动到对应的图片
}
- UISlider :可以用于调整图片的缩放比例。
let zoomSlider = UISlider()
zoomSlider.minimumValue = 1
zoomSlider.maximumValue = 5
zoomSlider.value = 1
zoomSlider.addTarget(self, action: #selector(zoomChanged), for:.valueChanged)
self.view.addSubview(zoomSlider)
@objc func zoomChanged(_ sender: UISlider) {
let zoomScale = sender.value
// 根据缩放比例调整图片的显示大小
}
9. 注意事项和常见问题
9.1 UIDatePicker的.countDownTimer模式问题
-
问题描述
:
.countDownTimer模式下Value Changed事件不可靠,尤其是在应用启动后不久和用户尝试将定时器设置为零时。 -
解决方案
:不要依赖
Value Changed事件,例如在界面中提供一个按钮,让用户点击以让代码读取日期选择器的countDownDuration。
9.2 UISlider的拇指区域问题
- 问题描述 :增大或偏移滑块的拇指可能会误导用户对可触摸拖动区域的判断,只有与滑块边界相交的拇指部分才是可拖动的。
- 解决方案 :
-
增加滑块的高度,使用自动布局时可在nib编辑器中添加明确的高度约束,或在代码中重写
intrinsicContentSize。 -
创建子类并使用点击测试修改,重写
hitTest方法。
9.3 UISegmentedControl的高度问题
- 问题描述 :分段控件的高度不会自动增加以适应过高的分段图像,图像高度会被压缩。
-
解决方案
:使用自动布局时,通过约束或重写
intrinsicContentSize来更改高度,或设置背景图像。
10. 总结
通过对UIStepper、UIPageControl、UIDatePicker、UISlider和UISegmentedControl等常用iOS控件的详细介绍,我们了解了它们的基本功能、事件响应、外观定制以及实际应用场景。在开发iOS应用时,合理选择和使用这些控件可以大大提升用户体验。同时,要注意每个控件可能出现的问题,并采取相应的解决方案。希望本文能为iOS开发者在使用这些控件时提供有价值的参考。
超级会员免费看
2154

被折叠的 条评论
为什么被折叠?



