3.4 常用 UI 控件:第一批叶片


3.4 常用 UI 控件:第一批叶片

本节摘要:枝条有了节律,该长叶片了。本节过一遍最常用的六种控件——UILabel、UIButton、UITextField、UIImageView、UISwitch、UISlider——各自的"配置三件事"与事件接线方式(Target-Action 与闭包两种),再把它们组装成阳台花园的"添加植物"表单页。键盘的收起与占位文案这类贴身细节一并处理,为第 4 章列表页备好输入侧零件。

长出第一批叶片

控件是视图树上标准化的叶片:系统替你画好了样子、定义了行为,你只管配置与接线。学控件的正确姿势不是背属性清单,而是抓住每片叶子的"配置三件事":内容、样式、事件。下面按出现频率过一遍,每种都给出阳台花园里的真实用法。

控件 职责 配置三件事 阳台花园用法
UILabel 只读文本 text、font/textColor、numberOfLines 植物名字、倒计时状态
UIButton 接收点击 setTitle、titleLabel 字体、addTarget 或 addAction 浇水按钮、保存按钮
UITextField 单行输入 placeholder、keyboardType、delegate 或事件 输入植物名字
UIImageView 显示图片 image、contentMode、layer 圆角 植物照片占位
UISwitch 开关布尔 isOn、onTintColor、addTarget 值变化 "接收提醒"开关
UISlider 范围取值 min/max/value、addTarget 值变化 浇水周期天数选择

接线有两种风格,值得都见一遍——Target-Action 是老派正规军,闭包 addAction 是新式轻武器:

// 风格一:Target-Action(选择器指向方法,历史最悠久) waterButton.addTarget(self, action: #selector(waterTapped), for: .touchUpInside) // 风格二:addAction 闭包(iOS 14 起,逻辑就地闭合,不用跳方法) waterButton.addAction(UIAction { [weak self] _ in self?.plant.water() self?.render() }, for: .touchUpInside)

两种等价,选顺手的;闭包风格记得带 [weak self]——2.8 的教训在控件接线处同样适用。.touchUpInside 读作"手指在按钮范围内抬起",这是标准点击事件。

表单页组装:从需求到控件清单

现在干一件完整的事。背景:用户要往阳台花园里添加植物,需要填名字、选拨不拨提醒、拖一个浇水周期。操作:按需求映射控件,再逐个接线。

final class AddPlantViewController: UIViewController { // 叶片清单:一行的需求一片叶 private let titleField = UITextField() // 名字输入 private let reminderSwitch = UISwitch() // 提醒开关 private let intervalSlider = UISlider() // 周期滑杆(3 到 15 天) private let intervalLabel = UILabel() // 周期数值回显 private let saveButton = UIButton(type: .system) override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground navigationItem.title = "添加植物" // —— 配置三件事逐个过 —— titleField.placeholder = "植物名字,如:琴叶榕" // 内容:占位提示 titleField.borderStyle = .roundedRect // 样式:带边框才像输入框 titleField.clearButtonMode = .whileEditing // 编辑时显示清空叉 titleField.returnKeyType = .done // 键盘回车键显示"完成" titleField.delegate = self // 事件:委托接线(见下文) reminderSwitch.isOn = true // 默认开提醒 intervalSlider.minimumValue = 3 // 内容:范围 intervalSlider.maximumValue = 15 intervalSlider.value = 3 intervalSlider.isContinuous = true // 拖动过程实时回调 intervalSlider.addAction(UIAction { [weak self] _ in self?.renderInterval() // 拖一下刷一次回显 }, for: .valueChanged) saveButton.setTitle("存入花园", for: .normal) saveButton.titleLabel?.font = .boldSystemFont(ofSize: 18) saveButton.addAction(UIAction { [weak self] _ in self?.save() }, for: .touchUpInside) layoutLeaves() // 布局统一走 3.5 的约束(此处省略细节) renderInterval() } private func renderInterval() { intervalLabel.text = "每 \(Int(intervalSlider.value)) 天浇水一次" // 滑杆值是 Float,展示要转 Int } private func save() { guard let name = titleField.text, !name.isEmpty else { return } // 空名校验 var plant = Plant(name: name, kind: .foliage, passedDays: 0) // 3.1 版模型:周期默认由品种决定 plant.customInterval = Int(intervalSlider.value) // 本页给模型加了可覆盖周期的新属性 print("已存入:\(plant.name),周期 \(plant.intervalDays) 天,提醒 \(reminderSwitch.isOn ? "开" : "关")") // 输出示例:已存入:琴叶榕,周期 5 天,提醒 开 } }

滑杆这处细节值得停一下:intervalSlider.value 是 Float,直接插值进文案会显示"每 5.0 天"——界面上的数字几乎永远要显式转成 Int,这条规则在整个 UI 生涯里反复出现。

键盘:输入控件的贴身事务

文本框一聚焦,键盘自动顶上来——但"挡住输入框"与"怎么收走"两件事系统不管,得自己处理。收键盘至少掌握三种:

// 方式一:点回车收(最常用,配委托) extension AddPlantViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() // 辞去"第一响应者",键盘随之收起 return true } // 顺带做实时校验:名字超长就截断 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let next = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) return next.count <= 12 // 返回 false = 拒绝这次修改 } } // 方式二:点空白处收(手势一次配置全局受益) override func viewDidLoad() { super.viewDidLoad() // —— 已有代码 —— let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tap) } @objc private func dismissKeyboard() { view.endEditing(true) } // 方式三:换页面时强制收(防"键盘漂在别的页上") override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) view.endEditing(true) // 3.3 的节律:离屏前收尾 }

第三种放在 viewWillDisappear 里不是随便选的——上一节的口诀"清理与资源寿命对齐",键盘正是一种需要随页面离场收回的资源。至于键盘挡输入框的问题,成熟方案是监听键盘高度通知、把整个视图上移,或直接用滚动视图包住表单;第 6 章实战会处理,现在先记住问题存在。

💡 关键直觉:控件的数量比你记忆中的属性多,但"内容、样式、事件"三件事是所有控件共同的骨架。遇到陌生控件,先问它内容是什么、怎么变好看、变化怎么通知你——再查文档,检索词都省了。

案例:滑杆与开关的联动

背景:产品要求"关掉提醒开关时,周期滑杆灰掉不可拖"。操作:给开关加值变化监听,联动滑杆的isEnabled。结果与解读:

reminderSwitch.addAction(UIAction { [weak self] _ in guard let self else { return } self.intervalSlider.isEnabled = self.reminderSwitch.isOn // 联动:开关决定滑杆可用性 self.intervalSlider.alpha = self.reminderSwitch.isOn ? 1.0 : 0.4 // 视觉同步降透明度 print("提醒\(self.reminderSwitch.isOn ? "开" : "关")") }, for: .valueChanged) // 拨动开关后控制台:提醒关;界面上滑杆变灰、拖不动

解读:isEnabled 管行为、alpha 管观感,两处一起改用户才不困惑——只灰不可点、或可点但不灰,都是半成品交互。变式:更复杂的联动(周期影响提醒文案)照同一个模式扩展:一个控件的值变化回调里重渲染另一片叶子的状态;若联动关系长成网状,就轮到第 5 章的状态驱动出场——那时你会发现这套手写联动全部消失。

本节要点回顾

  • 控件学习骨架:内容、样式、事件三件事,覆盖一切控件;
  • 接线两式:Target-Action 与 addAction 闭包等价,闭包记得 weak self;
  • 滑杆值是 Float,界面展示前显式转 Int;
  • 收键盘三招:回车 resignFirstResponder、空白点击 endEditing、离屏统一收;
  • 联动交互:isEnabled 管行为、alpha 管观感,成对修改。

叶片会动了,但位置还是钉死的——下一节让枝条学会自己伸展:Auto Layout。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U