【Swift】UIKit:UIAlertController、UIImageView、UIDatePicker、UIPickerView和UISwitch - 指南

六、弹窗提示组件:UIAlertController

UIAlertController 是 iOS 8+ 推出的弹窗/底部弹出框组件,用于替代旧版 UIAlertViewUIActionSheet,支持两种样式(alert 居中弹窗、actionSheet 底部弹出),可添加按钮、输入框,实现提示、确认、输入等交互场景。

6.1 核心功能

  • 提供两种弹窗样式:alert(居中显示,用于提示、确认)、actionSheet(底部弹出,用于选择操作);
  • 支持添加多种类型按钮:默认按钮(default)、取消按钮(cancel)、危险按钮(destructive,文字红色);
  • 可添加文本输入框(如账号密码输入);
  • 通过闭包(handler)绑定按钮点击后的逻辑,无需代理。

6.2 关键属性与方法

类别属性/方法作用说明
初始化UIAlertController(title:message:preferredStyle:)创建弹窗:title 标题、message 副标题、preferredStyle 样式(.alert/.actionSheet
添加按钮addAction(_:)添加按钮(UIAlertAction 类型,需指定标题、样式、点击回调)
添加输入框addTextField(configurationHandler:)添加文本输入框,可配置占位符、密码隐藏等(如登录场景的账号密码框)
显示弹窗present(_:animated:completion:)在当前控制器上显示弹窗(animatedtrue 时带动画)
按钮类型UIAlertAction.Style按钮样式:.default(默认)、.cancel(取消,自动居左/底部)、.destructive(危险,红色)

6.3 代码逻辑解析

场景1:简单提示弹窗(仅确认按钮)

@objc func simpleHint() {
    // 1. 创建居中弹窗:标题「提示」,副标题「一个简单提示...」,样式.alert
    let alert = UIAlertController(title: "提示", message: "一個簡單提示,請按確認繼續", preferredStyle: .alert)
    // 2. 添加确认按钮:样式.default,点击后打印日志
    let okAction = UIAlertAction(title: "確認", style: .default) { _ in
        print("按下確認後,閉包裡的動作")
    }
    alert.addAction(okAction)
    // 3. 显示弹窗(带动画)
    present(alert, animated: true)
}

场景2:危险操作弹窗(取消+删除按钮)

@objc func deleteSomething() {
    // 1. 创建居中弹窗:标题「删除」,提示文字红色
    let alert = UIAlertController(title: "刪除", message: "刪除字樣會變紅色的", preferredStyle: .alert)
    // 2. 添加取消按钮:样式.cancel,点击无逻辑(handler为nil)
    let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
    alert.addAction(cancelAction)
    // 3. 添加删除按钮:样式.destructive(红色),点击无逻辑
    let deleteAction = UIAlertAction(title: "刪除", style: .destructive, handler: nil)
    alert.addAction(deleteAction)
    // 4. 显示弹窗
    present(alert, animated: true)
}

场景3:带输入框的登录弹窗

@objc func login() {
    // 1. 创建居中弹窗:标题「登入」,提示输入账号密码
    let alert = UIAlertController(title: "登入", message: "請輸入帳號與密碼", preferredStyle: .alert)
    // 2. 添加账号输入框:占位符「帳號」
    alert.addTextField { textField in
        textField.placeholder = "帳號"
    }
    // 3. 添加密码输入框:占位符「密碼」,密码隐藏
    alert.addTextField { textField in
        textField.placeholder = "密碼"
        textField.isSecureTextEntry = true  // 密码隐藏为•
    }
    // 4. 添加取消按钮
    alert.addAction(UIAlertAction(title: "取消", style: .cancel))
    // 5. 添加登录按钮:点击后获取输入框内容并打印
    let loginAction = UIAlertAction(title: "登入", style: .default) { [weak self] _ in
        // 安全获取输入框(alert.textFields为数组,按添加顺序排列)
        guard let accField = alert.textFields?.first,
              let pwdField = alert.textFields?.last else { return }
        print("輸入的帳號為:\(accField.text ?? "")")
        print("輸入的密碼為:\(pwdField.text ?? "")")
        // 此处可添加实际登录逻辑(如网络请求)
    }
    alert.addAction(loginAction)
    // 6. 显示弹窗
    present(alert, animated: true)
}
场景4:底部弹出选择框(actionSheet)
@objc func bottomAlert() {
    // 1. 创建底部弹出框:样式.actionSheet
    let alert = UIAlertController(title: "底部提示", message: "這個提示會從底部彈出", preferredStyle: .actionSheet)
    // 2. 添加取消按钮(自动居底部,点击关闭弹窗)
    alert.addAction(UIAlertAction(title: "取消", style: .cancel))
    // 3. 添加确认按钮
    alert.addAction(UIAlertAction(title: "確認", style: .default))
    // 4. 显示弹窗(从底部滑入)
    present(alert, animated: true)
}

在这里插入图片描述

6.4 注意事项

  • iPad 适配问题actionSheet 样式在 iPad 上无法直接显示,需通过 popoverPresentationController 设置锚点(如按钮),否则会崩溃;
  • 输入框安全获取alert.textFields 可能为 nil,需用 guard 或可选绑定安全获取,避免崩溃;
  • 闭包循环引用:若在按钮 handler 中访问 self(如调用控制器方法),需用 [weak self] 避免循环引用(代码中登录场景已添加);
  • 按钮数量限制:弹窗按钮数量建议不超过 3 个(actionSheet 可多些),过多会导致界面拥挤,需改用其他组件(如 UITableView 选择列表)。

七、图片展示组件:UIImageView

UIImageView 是用于展示图片的组件(继承自 UIView),支持静态图片显示与动态图片轮播(如广告 banner),可配置图片拉伸、缩放、内容模式,是 App 中展示图标、封面、轮播图的核心组件。

7.1 核心功能

  • 显示单张静态图片(image 属性);
  • 支持多张图片轮播(animationImages 属性),可控制轮播时长、重复次数;
  • 配置图片内容模式(如填充、居中、按比例缩放);
  • 可作为手势识别的载体(如点击图片跳转)。

7.2 关键属性与方法

类别属性/方法作用说明
静态图片image设置单张静态图片(如 UIImage(named: "01.jpg"),需图片资源导入项目)
动态轮播animationImages设置轮播图片数组([UIImage] 类型)
animationDuration轮播一次的总时长(秒),如 6 表示 3 张图每张显示 2 秒
animationRepeatCount轮播重复次数(0 为无限循环)
轮播控制startAnimating()开始轮播
stopAnimating()停止轮播
内容模式contentMode图片显示模式(如 .scaleAspectFit 按比例缩放,不裁剪;.scaleToFill 拉伸填充)

7.3 代码逻辑解析

class ViewController: UIViewController {
    let fullScreenSize = UIScreen.main.bounds.size
    var myImageView: UIImageView!  // 全局变量,用于控制轮播
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 创建UIImageView:大小200x200
        myImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        // 2. 配置轮播图片:需确保项目中存在"01.jpg"、"02.jpg"、"03.jpg"
        let imgArr = [
            UIImage(named: "01.jpg")!,
            UIImage(named: "02.jpg")!,
            UIImage(named: "03.jpg")!
        ]
        myImageView.animationImages = imgArr  // 设置轮播数组
        // 3. 配置轮播参数:6秒一轮,无限循环
        myImageView.animationDuration = 6
        myImageView.animationRepeatCount = 0  // 0=无限
        // 4. 开始轮播
        myImageView.startAnimating()
        // 5. 位置:屏幕中上部,加入视图
        myImageView.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.4)
        self.view.addSubview(myImageView)
        // 6. 创建「播放」按钮:点击继续轮播
        let playBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
        playBtn.setImage(UIImage(named: "play"), for: .normal)  // 播放图标
        playBtn.addTarget(self, action: #selector(play), for: .touchUpInside)
        playBtn.center = CGPoint(x: fullScreenSize.width * 0.35, y: fullScreenSize.height * 0.65)
        self.view.addSubview(playBtn)
        // 7. 创建「停止」按钮:点击停止轮播
        let stopBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
        stopBtn.setImage(UIImage(named: "stop"), for: .normal)  // 停止图标
        stopBtn.addTarget(self, action: #selector(stop), for: .touchUpInside)
        stopBtn.center = CGPoint(x: fullScreenSize.width * 0.65, y: fullScreenSize.height * 0.65)
        self.view.addSubview(stopBtn)
    }
    // 8. 播放按钮回调
    @objc func play() {
        print("play images auto play")
        myImageView.startAnimating()
    }
    // 9. 停止按钮回调
    @objc func stop() {
        print("stop images auto play")
        myImageView.stopAnimating()
    }
}

在这里插入图片描述

7.4 注意事项

  • 图片资源导入:需将图片文件(如 01.jpgplay.png)拖入 Xcode 项目,并确保「Add to targets」勾选当前项目,否则 UIImage(named:) 会返回 nil,导致崩溃(建议用 guard let 安全获取);

  • 轮播性能问题:轮播图片过多或尺寸过大会占用内存,建议对图片进行压缩,或使用「懒加载」仅加载当前显示的图片;

  • 内容模式选择:若图片尺寸与 UIImageView 不一致,需设置 contentMode

    • .scaleAspectFit:按比例缩放,图片完整显示(可能留空白);
    • .scaleAspectFill:按比例缩放,填满视图(可能裁剪边缘,需配合 clipsToBounds = true);
    • .scaleToFill:拉伸图片填满视图(可能变形,不推荐)。

八、日期时间选择组件:UIDatePicker

UIDatePicker 是用于选择日期、时间的原生组件(继承自 UIControl),支持多种选择模式(如日期+时间、仅日期、仅时间),可配置时间范围、分钟间隔、显示语言,无需自定义布局,适用于生日选择、预约时间选择等场景。

8.1 核心功能

  • 提供多种选择模式:日期+时间(dateAndTime)、仅日期(date)、仅时间(time)、倒计时(countDownTimer);
  • 限制可选时间范围(minimumDate 最小日期、maximumDate 最大日期);
  • 自定义分钟间隔(如 15 分钟一步);
  • 支持多语言显示(locale 属性,如中文、英文);
  • 通过 target-action 绑定日期变化后的回调,实时获取选择结果。

8.2 关键属性与方法

类别属性/方法作用说明
初始化UIDatePicker(frame:)创建日期选择器,通常设置宽度为屏幕宽度(适配不同设备)
选择模式datePickerMode设置模式:.dateAndTime(日期+时间)、.date(仅日期)、.time(仅时间)、.countDownTimer(倒计时)
时间范围minimumDate/maximumDate设置可选的最小/最大日期(Date 类型,需通过 DateFormatter 转换字符串为日期)
分钟间隔minuteInterval设置分钟选择间隔(如 15 表示只能选 00、15、30、45 分)
语言配置locale设置显示语言(如 Locale(identifier: "zh_TW") 为繁体中文,"zh_CN" 为简体中文)
日期回调addTarget(_:action:for:)绑定日期变化事件(UIControl.Event.valueChanged),实时获取选择结果
日期格式化DateFormatterDate 类型转换为字符串(如 "2025-10-30 14:30"),或反之

8.3 代码逻辑解析

class ViewController: UIViewController {
    let fullScreenSize = UIScreen.main.bounds.size
    var myDatePicker: UIDatePicker!
    var myLabel: UILabel!  // 用于显示选择的日期时间
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 创建日期选择器:宽度为屏幕宽度,高度100
        myDatePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: fullScreenSize.width, height: 100))
        // 2. 配置选择模式:日期+时间
        myDatePicker.datePickerMode = .dateAndTime
        // 3. 配置分钟间隔:15分钟一步
        myDatePicker.minuteInterval = 15
        // 4. 配置可选时间范围:2016-01-02 18:08 到 2017-12-25 10:45
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm"  // 定义日期字符串格式
        // 字符串转Date:需与format格式完全一致
        let minDate = formatter.date(from: "2016-01-02 18:08")
        let maxDate = formatter.date(from: "2017-12-25 10:45")
        myDatePicker.minimumDate = minDate
        myDatePicker.maximumDate = maxDate
        // 5. 配置语言:繁体中文
        myDatePicker.locale = Locale(identifier: "zh_TW")
        // 6. 绑定日期变化事件:选择日期时触发datePickerChanged方法
        myDatePicker.addTarget(self, action: #selector(datePickerChanged), for: .valueChanged)
        // 7. 位置:屏幕中上部,加入视图
        myDatePicker.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.4)
        self.view.addSubview(myDatePicker)
        // 8. 创建标签:显示选择的日期时间
        myLabel = UILabel(frame: CGRect(x: 0, y: 0, width: fullScreenSize.width, height: 50))
        myLabel.backgroundColor = .lightGray
        myLabel.textAlignment = .center
        myLabel.text = "请选择日期时间"  // 初始提示
        myLabel.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.15)
        self.view.addSubview(myLabel)
    }
    // 9. 日期变化回调:更新标签显示
    @objc func datePickerChanged(datePicker: UIDatePicker) {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm"  // 与设置范围时的格式一致
        // Date转字符串,更新标签
        myLabel.text = formatter.string(from: datePicker.date)
    }
}

在这里插入图片描述

8.4 注意事项

  • 日期格式一致性DateFormatterdateFormat 必须与字符串格式完全匹配(如 "yyyy-MM-dd HH:mm" 对应 "2016-01-02 18:08"),大小写、分隔符错误会导致 date(from:) 返回 nil

  • 时区问题Date 类型默认使用设备时区,若需统一时区(如 UTC),需设置 formatter.timeZone = TimeZone(identifier: "UTC")

  • iOS 14+ 样式变化:iOS 14 后 UIDatePicker 支持 preferredStyle 属性(.compact 紧凑样式、.inline 内嵌样式),若需兼容旧版本,需判断系统版本;

  • 默认日期设置:若未设置 myDatePicker.date,默认显示当前日期时间,如需指定默认日期,需用 formatter.date(from:) 转换后赋值。


九、自定义选项选择组件:UIPickerView

UIPickerView 是用于多列自定义选项选择的滚动式组件(继承自 UIView),支持自定义列数、每列选项内容,通过代理模式(UIPickerViewDelegate/UIPickerViewDataSource)实现数据配置与交互回调,适用于日期、地区、分类等多维度选择场景(如“星期+餐次”选择),也可替代键盘作为 UITextField 的输入视图。

9.1 核心功能

  • 支持多列选项(如 2 列分别显示“星期”和“餐次”);
  • 自定义每列的选项内容(文字、图片均可,需通过代理方法实现);
  • 实时响应选项变化(didSelectRow 回调);
  • 可作为 UITextField 的 inputView,替代系统键盘实现自定义输入。

9.2 关键属性与方法

类别属性/方法作用说明
数据配置(必须实现)numberOfComponents(in:)数据源方法:返回 UIPickerView 的列数(如 return 2 表示 2 列)
pickerView(_:numberOfRowsInComponent:)数据源方法:返回指定列的选项行数(如第 0 列返回“星期”数组长度 7)
外观配置pickerView(_:titleForRow:forComponent:)代理方法:返回指定列、指定行的选项文字(如第 0 列第 0 行返回“星期日”)
交互回调pickerView(_:didSelectRow:inComponent:)代理方法:选项变化时触发,返回当前选中的列和行(用于更新选中状态)
输入视图替换UITextField.inputView将 UIPickerView 设为 UITextField 的输入视图,点击输入框时弹出 PickerView 而非键盘
子控制器管理addChild(_:)若将代理逻辑拆分到独立控制器(如 MyViewController),需通过此方法添加子控制器,避免内存泄漏

9.3 代码逻辑解析

场景1:独立 UIPickerView(多列选择)

// 1. 独立控制器实现代理逻辑(MyViewController.swift)
class MyViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    // 数据源:2列分别对应“星期”和“餐次”
    let week = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]
    let meals = ["早餐","午餐","晚餐","宵夜"]
    // 记录选中结果
    var whatDay = "星期日"
    var whatMeal = "早餐"
    // 必须实现:返回列数(2列)
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    // 必须实现:返回指定列的行数(第0列7行,第1列4行)
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return component == 0 ? week.count : meals.count
    }
    // 配置选项文字:第0列显示星期,第1列显示餐次
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return component == 0 ? week[row] : meals[row]
    }
    // 选项变化回调:更新选中结果并打印
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if component == 0 {
            whatDay = week[row]
        } else {
            whatMeal = meals[row]
        }
        print("選擇的是 \(whatDay) , \(whatMeal)")
    }
}
// 2. 主控制器创建并配置PickerView(ViewController.swift)
class ViewController: UIViewController {
    let fullScreenSize = UIScreen.main.bounds.size
    override func viewDidLoad() {
        super.viewDidLoad()
        // 创建PickerView:位置在屏幕中上部,高度150
        let myPickerView = UIPickerView(frame: CGRect(
            x: 0,
            y: fullScreenSize.height * 0.3,
            width: fullScreenSize.width,
            height: 150
        ))
        // 初始化代理控制器,添加为子控制器(避免内存泄漏)
        let myVC = MyViewController()
        self.addChild(myVC) // 关键:子控制器需加入父控制器管理
        // 绑定代理和数据源
        myPickerView.delegate = myVC
        myPickerView.dataSource = myVC
        self.view.addSubview(myPickerView)
    }
}

在这里插入图片描述

场景2:UIPickerView + UITextField(替代键盘)

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    let meals = ["早餐","午餐","晚餐","宵夜"] // PickerView数据源
    let fullScreenSize = UIScreen.main.bounds.size
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 创建TextField:用于显示选中结果
        let mealTextField = UITextField(frame: CGRect(
            x: 0, y: 0,
            width: fullScreenSize.width, height: 40
        ))
        mealTextField.backgroundColor = .lightGray
        mealTextField.textAlignment = .center
        mealTextField.text = meals[0] // 默认选中第0项
        mealTextField.tag = 100 // 用tag区分多个TextField
        mealTextField.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.15)
        self.view.addSubview(mealTextField)
        // 2. 创建PickerView,设为TextField的输入视图(替代键盘)
        let myPickerView = UIPickerView()
        myPickerView.delegate = self
        myPickerView.dataSource = self
        mealTextField.inputView = myPickerView // 关键:替换输入视图
        // 3. 点击空白隐藏PickerView(添加手势)
        let tap = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
        tap.cancelsTouchesInView = false
        self.view.addGestureRecognizer(tap)
    }
    // 数据源方法:1列
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    // 数据源方法:4行(餐次数组长度)
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return meals.count
    }
    // 配置选项文字
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return meals[row]
    }
    // 选项变化:更新TextField内容
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        // 通过tag找到目标TextField
        let textField = self.view?.viewWithTag(100) as? UITextField
        textField?.text = meals[row]
    }
    // 隐藏PickerView(结束编辑)
    @objc func hideKeyboard() {
        self.view.endEditing(true)
    }
}

在这里插入图片描述

9.4 注意事项

  • 代理方法必须实现numberOfComponents(in:)pickerView(_:numberOfRowsInComponent:) 是数据源协议的必须实现方法,未实现会导致崩溃;

  • 控制器管理:若将代理逻辑拆分到独立控制器(如 MyViewController),必须通过 addChild(_:) 将其添加为子控制器,否则代理对象会被提前释放,PickerView 无数据;

  • 多TextField区分:当多个 TextField 共用 PickerView 时,需通过 tag 或属性记录当前激活的 TextField,避免更新错误;

  • 自定义选项外观:若需显示图片或富文本选项,可重写 pickerView(_:viewForRow:forComponent:reusing:) 方法,返回自定义 View(如 UIImageView + UILabel)。


十、开关组件:UISwitch

UISwitch 是用于二元状态切换的开关组件(继承自 UIControl),支持“开启/关闭”两种状态,可自定义滑块、背景颜色,通过 valueChanged 事件响应状态变化,适用于功能开关(如“通知开启”“夜间模式”)。

10.1 核心功能

  • 直观展示二元状态(开启 isOn = true / 关闭 isOn = false);

  • 自定义外观(滑块颜色、开启/关闭状态的背景色);

  • 支持点击切换,通过 target-action 绑定状态变化回调。

10.2 关键属性与方法

类别属性/方法作用说明
状态控制isOn布尔值,获取/设置开关状态(true 开启,false 关闭,默认 false
外观配置thumbTintColor滑块颜色(如 UIColor.orange,滑块是开关中间的圆形按钮)
tintColor关闭状态(isOn = false)的背景色(如 UIColor.blue
onTintColor开启状态(isOn = true)的背景色(如 UIColor.brown
交互回调addTarget(_:action:for:)绑定 valueChanged 事件,状态变化时触发回调(如 #selector(onChange)

10.3 代码逻辑解析

class ViewController: UIViewController {
    let fullScreenSize = UIScreen.main.bounds.size
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 默认样式开关
        let defaultSwitch = UISwitch()
        defaultSwitch.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.3)
        self.view.addSubview(defaultSwitch)
        // 2. 自定义样式开关
        let customSwitch = UISwitch()
        customSwitch.thumbTintColor = .orange    // 滑块橙色
        customSwitch.tintColor = .blue           // 关闭时背景蓝色
        customSwitch.onTintColor = .brown        // 开启时背景棕色
        // 绑定状态变化事件
        customSwitch.addTarget(self, action: #selector(onChange(_:)), for: .valueChanged)
        customSwitch.center = CGPoint(x: fullScreenSize.width * 0.5, y: fullScreenSize.height * 0.5)
        self.view.addSubview(customSwitch)
    }
    // 状态变化回调:根据开关状态切换背景色
    @objc func onChange(_ sender: AnyObject) {
        // 将sender强转为UISwitch,获取当前状态
        let tempSwitch = sender as! UISwitch
        // 切换父视图背景色
        self.view.backgroundColor = tempSwitch.isOn ? .black : .white
    }
}

在这里插入图片描述

10.4 注意事项

  • 状态判断安全:回调中 sender 需强转为 UISwitch 才能访问 isOn 属性,建议用 guard let 避免强转失败(如 guard let tempSwitch = sender as? UISwitch else { return });

  • 初始状态设置:若需默认开启,需在添加到视图前设置 customSwitch.isOn = true,否则外观可能不更新;

  • 自定义限制:UISwitch 不支持修改大小(frame 的宽高设置无效),仅能通过系统属性自定义颜色,如需特殊大小需自定义 View。

资料推荐:https://github.com/0voice

posted @ 2025-12-20 15:42  gccbuaa  阅读(1)  评论(0)    收藏  举报