iOS 搜索栏与控件的深入解析
1. UISearchBar 概述
UISearchBar 本质上是文本字段的包装器,它将文本字段作为其子视图之一,但无法直接访问该文本字段。默认情况下,它显示为带有放大镜图标的圆角矩形,用户可在此输入文本。不过,它本身并不执行搜索操作,也不显示搜索结果,常见的做法是在表格视图中显示搜索结果,而 UISearchController 类能轻松实现这一点。
2. UISearchBar 的基本属性
-
文本与占位符
:搜索栏的当前文本通过
text属性获取,还可以设置占位符,当没有输入文本时占位符会显示。 - 提示信息 :可在搜索栏上方显示提示信息,解释其用途。
-
代理方法
:通过
UISearchBarDelegate代理方法可监听编辑事件,具体如下: -
searchBarShouldBeginEditing(_:) -
searchBarTextDidBeginEditing(_:) -
searchBar(_:textDidChange:) -
searchBar(_:shouldChangeTextIn:replacementText:) -
searchBarShouldEndEditing(_:) -
searchBarTextDidEndEditing(_:)
3. UISearchBar 的样式设置
- barStyle :有两种样式可选:
-
.default:扁平浅灰色背景和白色搜索字段。 -
.black:黑色背景和黑色搜索字段。 - searchBarStyle :有三种样式:
-
.default -
.prominent:与.default相同。 -
.minimal:透明背景和深色透明搜索字段。 -
背景颜色与图像
:可通过
barTintColor更改背景颜色,若barStyle为.black,该颜色也会影响搜索字段本身。还能设置自定义背景图像,使用setBackgroundImage(_:for:barMetrics:)方法,此图像会覆盖其他确定背景的方式。
4. 搜索字段的调整
-
位置调整
:可使用
searchFieldBackgroundPositionAdjustment属性调整搜索字段相对于背景的位置,使用searchTextPositionAdjustment属性调整文本在搜索字段内的位置。 -
替换搜索字段图像
:调用
setSearchFieldBackgroundImage(_:for:)方法替换搜索字段的图像,第二个参数为UIControl.State,可能的值为.normal和.disabled,但 API 未提供禁用搜索字段的方法,可通过以下代码实现:
for v in self.sb.subviews[0].subviews {
if let tf = v as? UITextField {
tf.isEnabled = false
break
}
}
5. 搜索栏的按钮设置
-
内部按钮
:搜索字段有文本时会自动显示内部取消按钮(通常是圆圈中的 X)。右侧可能显示搜索结果按钮(
showsSearchResultsButton)或书签按钮(showsBookmarkButton),若同时要求显示两者,将显示搜索结果按钮。输入文本时,这些按钮会消失以显示取消按钮。 -
外部取消按钮
:可通过
showsCancelButton属性或setShowsCancelButton(_:animated:)方法显示外部取消按钮。 - 按钮点击代理方法 :
-
searchBarResultsListButtonClicked(_:) -
searchBarBookmarkButtonClicked(_:) -
searchBarCancelButtonClicked(_:) -
自定义图标
:使用
setImage(_:for:state:)方法自定义搜索图标和内部右侧图标的图像,图标类型通过UISearchBar.Icon指定: -
.search -
.clear(内部取消按钮) -
.bookmark -
.resultsList -
状态参数可选
.normal和.highlighted,若未提供正常图像,将使用默认图像;若提供正常图像但未提供高亮图像,两者都将使用正常图像。设置isSearchResultsButtonSelected为true可反转搜索结果按钮的行为。
6. 范围按钮
搜索栏还可显示范围按钮,用于让用户改变搜索的含义。通过
showsScopeBar
属性使范围按钮显示,按钮标题通过
scopeButtonTitles
属性设置,当前选中的范围按钮通过
selectedScopeButtonIndex
属性获取。用户点击不同范围按钮时,代理会收到通知:
searchBar(_:selectedScopeButtonIndexDidChange:)
。
范围栏的外观可高度自定义:
-
背景图像
:通过
scopeBarBackgroundImage
设置范围栏的背景图像,会根据需要拉伸或平铺。
-
按钮背景图像
:使用
setScopeBarButtonBackgroundImage(_:for:)
方法设置按钮的背景图像,状态可选
.normal
和
.selected
。
-
分隔线图像
:调用
setScopeBarButtonDividerImage(_:forLeftSegmentState:rightSegmentState:)
方法自定义按钮之间的分隔线图像。
-
文本属性
:使用
setScopeBarButtonTitleTextAttributes(_:for:)
方法自定义范围按钮标题的文本属性。
7. 其他设置
-
外部取消按钮
:虽然无法直接通过搜索栏访问外部取消按钮,但它是
UIBarButtonItem,可使用UIBarButtonItem外观代理进行自定义。 -
键盘处理
:搜索栏的键盘显示问题与文本字段相同,其文本输入属性也与文本字段类似。用户点击键盘上的搜索键时,代理会收到通知,此时需调用
resignFirstResponder方法关闭键盘并执行搜索操作:searchBarSearchButtonClicked(_:)。 -
嵌入方式
:搜索栏可作为条形按钮项的自定义视图嵌入工具栏或导航栏,也可作为标题视图嵌入导航栏。此外,
UISearchBar本身也可作为顶部栏使用。
8. UIControl 概述
UIControl 是 UIView 的子类,主要作为多个内置控件类的超类,赋予它们共同的行为。控件的重要共性是能自动跟踪和分析触摸事件,并通过动作消息将其作为重要的控制事件报告给代码。每个控件实现了部分可能的控制事件,控制事件如下:
| 控制事件 | 描述 |
| ---- | ---- |
|
.touchDown
| 触摸按下 |
|
.touchDownRepeat
| 重复触摸按下 |
|
.touchDragInside
| 在控件内拖动 |
|
.touchDragOutside
| 在控件外拖动 |
|
.touchDragEnter
| 从控件外拖入 |
|
.touchDragExit
| 从控件内拖出 |
|
.touchUpInside
| 在控件内抬起触摸 |
|
.touchUpOutside
| 在控件外抬起触摸 |
|
.touchCancel
| 触摸取消 |
|
.valueChanged
| 值改变 |
|
.editingDidBegin
| 编辑开始 |
|
.editingChanged
| 编辑内容改变 |
|
.editingDidEnd
| 编辑结束 |
|
.editingDidEndOnExit
| 编辑在退出时结束 |
|
.allTouchEvents
| 所有触摸事件 |
|
.allEditingEvents
| 所有编辑事件 |
|
.allEvents
| 所有事件 |
控制事件大致分为三组:
- 用户触摸屏幕(如
Touch Down
、
Touch Drag Inside
等)。
- 用户编辑文本(如
Editing Did Begin
、
Editing Changed
等)。
- 用户改变控件的值(
Value Changed
)。
不同控件通常发出的控制事件如下:
| 控件 | 控制事件 |
| ---- | ---- |
| UIButton | 所有触摸事件 |
| UIDatePicker | 值改变 |
| UIPageControl | 所有触摸事件,值改变 |
| UIRefreshControl | 值改变 |
| UISegmentedControl | 值改变 |
| UISlider | 所有触摸事件,值改变 |
| UISwitch | 所有触摸事件,值改变 |
| UIStepper | 所有触摸事件,值改变 |
| UITextField | 除抬起事件外的所有触摸事件,所有编辑事件 |
| UIControl(通用) | 所有触摸事件 |
每个控件还有一个主要控制事件
primaryActionTriggered
:
- 对于
UIButton
,主要控制事件是
Touch Up Inside
。
- 对于
UITextField
,主要控制事件是
Did End On Exit
。
- 其他控件的主要控制事件是
Value Changed
。
9. 控制事件的处理
对于每个想要监听的控制事件,可将一个或多个目标 - 动作对附加到控件上,可在 nib 编辑器或代码中完成。每个控件的控制事件及其目标 - 动作对构成一个调度表,可通过以下方法和属性操作和查询该调度表:
-
addTarget(_:action:for:)
-
removeTarget(_:action:for:)
-
actions(forTarget:forControlEvent:)
-
allTargets
-
allControlEvents
(至少有一个目标 - 动作对的控制事件的位掩码)
动作方法可以采用以下三种签名之一:
- 控件和 UIEvent
- 仅控件
- 无参数
其中,仅包含控件的签名最为常见。
当控制事件发生时,控件会查询其调度表,找到与该控制事件关联的所有目标 - 动作对,并将每个动作消息发送给相应的目标。实际上,
UIControl
并不直接发送动作消息,而是通知共享应用程序发送。若要让控件立即发出与特定控制事件对应的动作消息,可调用
sendActions(for:)
方法。
10. 控件状态
控件有
isEnabled
、
isSelected
和
isHighlighted
属性,它们相互独立,共同对应控件的状态,状态是三种可能值的位掩码(
UIControl.State
):
-
.highlighted
(
isHighlighted
为
true
)
-
.disabled
(
isEnabled
为
false
)
-
.selected
(
isSelected
为
true
)
-
.normal
:对应零状态位掩码,即
isEnabled
为
true
,
isSelected
和
isHighlighted
都为
false
。
未启用的控件不会响应用户交互,控件是否以不同方式显示以提示用户这一事实取决于具体控件。例如,禁用的
UISwitch
会变暗淡,而默认的圆角矩形文本字段不会给用户明显的禁用提示。控件的选择和高亮显示的视觉效果也因控件而异。
11. 控件内容对齐
控件有
contentHorizontalAlignment
和
contentVerticalAlignment
属性,仅当控件有可对齐的内容时才起作用,常用于
UIButton
定位其标题和内部图像。
12. UISwitch 控件
UISwitch 用于表示布尔值,外观类似滑动开关,其
isOn
属性为
true
或
false
。用户可通过滑动或点击来切换开关状态,状态改变时会报告
Value Changed
控制事件。可使用
setOn(_:animated:)
方法改变
isOn
属性的值并伴有动画效果。
UISwitch 的大小是固定的,任何设置其大小的尝试都会被忽略。可通过以下属性自定义其外观:
-
onTintColor
:开关处于开启状态时轨道的颜色。
-
thumbTintColor
:可滑动按钮的颜色。
-
tintColor
:开关处于关闭状态时轮廓的颜色。
需要注意的是,没有
offTintColor
属性,开关处于关闭状态时轨道是透明的,无法自定义其颜色。一种变通方法是在开关后面放置一个彩色的开关形状图像,代码如下:
func putColor(_ color: UIColor, behindSwitch sw: UISwitch) {
guard sw.superview != nil else {return}
let onswitch = UISwitch()
onswitch.isOn = true
let r = UIGraphicsImageRenderer(bounds:sw.bounds)
let im = r.image { ctx in
// 图像绘制代码
}
}
综上所述,UISearchBar 和 UIControl 及其子类在 iOS 开发中有着丰富的功能和多样的自定义选项,开发者可以根据具体需求灵活运用这些特性,打造出符合用户体验的界面。
iOS 搜索栏与控件的深入解析
13. 触摸事件的特殊情况
在触摸事件中,没有明确的
Touch Down Inside
事件,因为任何触摸事件序列都始于
Touch Down
,且该事件必须在控件内部发生,否则这一系列触摸事件就不属于该控件,也就不会产生控制事件。
对于控件“外部”的定义存在一定的虚拟范围。当用户在控件内点击并开始拖动时,即使拖动超出了控件的边界,
Inside
事件仍会触发。但当拖动距离控件超过一定距离时,会越过一个无形的边界,触发
Touch Drag Exit
事件,此后将报告
Outside
事件,直到拖动回到无形边界内,此时触发
Touch Drag Enter
事件,再次报告
Inside
事件。
以
UIButton
为例,越过这个无形边界时,按钮会自动取消高亮显示。因此,若要捕获合法的按钮点击,可能只需考虑
Touch Up Inside
事件。
对于其他控件,情况可能会稍有复杂。例如,
UISwitch
在拖动到一定距离时会取消高亮显示,但触摸仍被视为合法,仍可改变其值。所以,当用户手指离开屏幕时,
UISwitch
会报告
Touch Up Inside
事件,即使同时报告
Touch Drag Outside
事件。
下面是一个简单的 mermaid 流程图,展示触摸事件的流程:
graph LR
A[Touch Down] --> B{Inside Control?}
B -- Yes --> C[Inside Events]
B -- No --> D[No Control Events]
C --> E{Drag Outside Boundary?}
E -- Yes --> F[Touch Drag Exit]
E -- No --> C
F --> G[Outside Events]
G --> H{Drag Back Inside?}
H -- Yes --> I[Touch Drag Enter]
H -- No --> G
I --> C
14. 自定义控件示例
接下来通过一个简单的示例,展示如何编写自定义控件。假设我们要创建一个自定义的圆形按钮,它在被点击时会改变颜色。
import UIKit
class CustomCircleButton: UIControl {
private var circleLayer: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
circleLayer = CAShapeLayer()
circleLayer.path = UIBezierPath(ovalIn: bounds).cgPath
circleLayer.fillColor = UIColor.blue.cgColor
layer.addSublayer(circleLayer)
addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
@objc private func buttonTapped() {
circleLayer.fillColor = UIColor.red.cgColor
sendActions(for: .valueChanged)
}
override func layoutSubviews() {
super.layoutSubviews()
circleLayer.path = UIBezierPath(ovalIn: bounds).cgPath
}
}
在上述代码中,我们创建了一个名为
CustomCircleButton
的自定义控件,它继承自
UIControl
。在
setup
方法中,我们创建了一个圆形的
CAShapeLayer
并添加到控件的图层上。同时,我们为控件添加了一个
Touch Up Inside
事件的目标 - 动作对,当按钮被点击时,会调用
buttonTapped
方法,该方法会改变圆形的填充颜色并发送
Value Changed
控制事件。
15. 总结与应用建议
通过对 UISearchBar、UIControl 及其子类的详细介绍,我们了解到这些控件在 iOS 开发中具有丰富的功能和强大的自定义能力。以下是一些应用建议:
-
UISearchBar
:
- 合理使用代理方法,监听用户的编辑和操作事件,实现搜索功能。
- 充分利用样式设置和自定义选项,打造符合应用风格的搜索栏。
- 注意处理键盘显示和搜索结果展示的问题,提升用户体验。
-
UIControl
:
- 明确不同控件的主要控制事件,根据需求添加目标 - 动作对。
- 利用控件状态属性,实现不同状态下的视觉效果。
- 对于复杂的交互需求,可以考虑自定义控件,满足特定的业务逻辑。
下面是一个总结表格,对比不同控件的主要特点:
| 控件 | 主要功能 | 主要控制事件 | 自定义选项 |
| ---- | ---- | ---- | ---- |
| UISearchBar | 提供搜索输入框 | 编辑事件、按钮点击事件 | 样式、背景、图标等 |
| UIButton | 触发操作 | Touch Up Inside | 标题、图像、颜色等 |
| UISwitch | 表示布尔值 | Value Changed | 颜色 |
| CustomCircleButton(自定义) | 自定义交互 | Value Changed | 形状、颜色 |
总之,开发者在实际开发中应根据具体需求,灵活运用这些控件的特性,不断优化用户界面和交互体验。通过合理的设计和编码,能够打造出功能强大、美观易用的 iOS 应用。
超级会员免费看
1万+

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



