75、iOS开发中的弹窗、快捷操作与本地通知

iOS开发:弹窗、快捷操作与本地通知

iOS开发中的弹窗、快捷操作与本地通知

1. 弹窗与操作表

在iOS开发里,弹窗(Alert)和操作表(Action Sheet)是常用的交互组件。

1.1 弹窗文本输入处理

当在弹窗中添加文本输入框时,可按如下代码设置:

alert.addTextField { tf in
    tf.keyboardType = .numberPad
    tf.addTarget(self,
        action: #selector(self.textChanged), for: .editingChanged)
}

上述代码为文本框设置了数字键盘类型,并添加了文本编辑改变的监听。当用户编辑文本时, textChanged 方法会被调用。为了在文本框有内容时启用弹窗的“确定”按钮,可通过响应链从文本框找到弹窗控制器:

@objc func textChanged(_ sender: Any) {
    let tf = sender as! UITextField
    var resp : UIResponder! = tf
    while !(resp is UIAlertController) { resp = resp.next }
    let alert = resp as! UIAlertController
    alert.actions[1].isEnabled = (tf.text != "")
}

不过,使用硬件键盘时,即使文本框为空,用户按“Return”键仍可关闭弹窗。为避免这种情况,需为文本框设置代理并实现 textFieldShouldReturn 方法:

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    return textField.text != ""
}
1.2 操作表的使用

操作表( UIAlertController 样式为 .actionSheet )可看作iOS版的菜单,主要由按钮构成。在iPhone上,它从屏幕底部滑出;在iPad上,它以弹出框形式呈现。以下是创建基本操作表的代码:

let action = UIAlertController(
    title: "Choose New Layout", message: nil, preferredStyle: .actionSheet)
action.addAction(UIAlertAction(title: "Cancel", style: .cancel))
func handler(_ act:UIAlertAction) {
    // ... do something here with act.title ...
}
for s in ["3 by 3", "4 by 3", "4 by 4", "5 by 4", "5 by 5"] {
    action.addAction(UIAlertAction(title: s,
        style: .default, handler: handler))
}
self.present(action, animated: true)

在iPad上,操作表会以弹出框形式展示,因此需要为弹出框的箭头指定指向,否则会在运行时崩溃:

self.present(action, animated: true)
if let pop = action.popoverPresentationController {
    let v = sender as! UIView
    pop.sourceView = v
    pop.sourceRect = v.bounds
}

操作表的取消按钮在iPad上会被隐藏,因为用户点击弹出框外部即可关闭;在iPhone上,即使显示取消按钮,用户点击操作表外部也能关闭,且此时取消按钮的处理函数会被调用。

操作表也可在弹出框内展示,此时包含它的弹出框会被当作iPhone处理,操作表从弹出框底部滑出,取消按钮不会被隐藏。操作表的模态展示样式默认为 .overCurrentContext ,无需额外设置。

2. 弹窗替代方案

弹窗和操作表存在局限性,界面元素仅能包含标题文本、按钮(弹窗还可包含一两个文本框)。若需要更多界面元素,可考虑以下替代方案:
- 弹出框(Popover) :类似二级窗口,可实现真正的模态效果,内部可展示二级视图或操作表。
- 表单(Form Sheet) :使用 .formSheet 展示样式的视图,相当于比屏幕小的对话框窗口。
- 全屏展示的视图控制器 :在iPhone上,任何展示的视图本质上都是模态对话框,如某些应用的颜色选择器。

3. 快捷操作

快捷操作是用户对应用图标进行3D触控时弹出的一列按钮(若设备不支持3D触控,则无法使用),它提供了便捷访问应用功能的方式。快捷操作分为两种:
| 类型 | 描述 |
| ---- | ---- |
| 静态快捷操作 | 在应用的 Info.plist 中描述,即使应用未运行甚至从未运行,系统也能通过读取 Info.plist 展示。 |
| 动态快捷操作 | 通过代码配置,应用代码运行后才可用,代码可修改和移除动态快捷操作,但无法影响静态快捷操作。 |

UIApplicationShortcutItem 类用于描述快捷操作按钮,包含以下属性及对应的 Info.plist 键:
| 属性 | Info.plist 键 | 描述 |
| ---- | ---- | ---- |
| type | UIApplicationShortcutItemType | 任意字符串,用于区分点击的按钮,必填。 |
| localizedTitle | UIApplicationShortcutItemTitle | 按钮标题,字符串,必填。 |
| localizedSubtitle | UIApplicationShortcutItemSubtitle | 按钮副标题,字符串,可选。 |
| icon | UIApplicationShortcutItemIconType、UIApplicationShortcutItemIconFile | 按钮图标,可选,建议提供,否则默认显示填充圆形。 |
| userInfo | UIApplicationShortcutItemUserInfo | 可选字典,用于存储额外信息,使用方式自定义。 |

以下是创建动态快捷操作的示例代码:

let subtitle = "In 1 hour..." // or whatever
let time = 60 // or whatever
let item = UIApplicationShortcutItem(type: "coffee.schedule",
    localizedTitle: "Coffee Reminder", localizedSubtitle: subtitle,
    icon: UIApplicationShortcutIcon(templateImageName: "cup"),
    userInfo: ["time":time as NSNumber])
UIApplication.shared.shortcutItems = [item]

用户点击快捷操作按钮时,应用委托的 application(_:performActionFor:completionHandler:) 方法会被调用,可通过 userInfo 获取额外信息:

func application(_ application: UIApplication,
    performActionFor shortcutItem: UIApplicationShortcutItem,
    completionHandler: @escaping (Bool) -> ()) {
        if shortcutItem.type == "coffee.schedule" {
            if let d = shortcutItem.userInfo {
                if let time = d["time"] as? Int {
                    // ... do something with time ...
                    completionHandler(true)
                }
            }
        }
        completionHandler(false)
}
4. 本地通知

本地通知是一种即使应用不在前台甚至未运行也能向用户发出的提醒,由系统负责展示。本地通知可在以下位置出现:
- 锁屏界面
- 通知中心(用户从屏幕顶部下滑显示的界面)
- 屏幕顶部横幅

本地通知触发时可播放声音,还能在应用图标上显示或移除徽章数字。不过,用户可在设置应用中控制本地通知的显示方式,如禁止某些通知、选择横幅显示类型等。

用户与本地通知的交互方式有:
- 点击通知,将应用置于前台,若应用未运行则启动应用。
- 在锁屏界面或通知中心滑动通知,显示“清除”“管理”“打开”等标准按钮。
- 查看通知,可通过滑动、拖动或用力按压通知,展示包含更多内容的二级界面,如自定义操作按钮、附件(图片、音频、视频)等。

使用本地通知需遵循以下步骤:

graph LR
    A[获取通知授权] --> B[注册通知类别]
    B --> C[创建并调度本地通知]
    C --> D[处理用户响应]

具体步骤如下:
1. 获取通知授权 :在iOS 12中,有两种获取初始授权的策略:
- 完全授权 :自iOS 8起就有的授权方式,调度本地通知前,系统会弹出一次性授权提醒,用户需选择是否授权。授权后,应用可在锁屏界面、通知中心和横幅显示通知,可设置应用图标徽章和播放声音。
- 临时授权 :iOS 12新增的授权方式,无需用户明确授权,应用可自动获得“安静”通知权限,通知仅在通知中心显示,无声音和图标徽章。用户可在通知中心选择保留或关闭应用通知,点击“保留”可通过操作表授予应用完全授权。

在调度本地通知前,需先检查是否已授权,可调用用户通知中心的 getNotificationSettings 方法,根据返回的 UNNotificationSettings 对象的 authorizationStatus 属性判断授权状态:
| 授权状态 | 描述 |
| ---- | ---- |
| .denied | 用户明确禁止应用的所有通知,除非用户更改设置,否则调度通知无意义。 |
| .authorized | 拥有完全授权,可调度本地通知。 |
| .provisional | 拥有临时授权,可调度本地通知。 |
| .notDetermined | 需尝试获取授权,调用 requestAuthorization(options:) 方法,根据 options 参数决定授权方式。 |

以下是检查授权状态并请求授权的代码示例:

let center = UNUserNotificationCenter.current()
center.getNotificationSettings { settings in
    switch settings.authorizationStatus {
    case .denied: break // or beg for authorization
    case .authorized, .provisional: break // or schedule a notification
    case .notDetermined:
        center.requestAuthorization(options:[.alert, .sound]) { ok, err in
            if let err = err {
                return // could do something with the error information
            }
            if ok {
                // authorized; could schedule a notification
            }
        }
    }
}
  1. 注册通知类别 :可根据需求注册通知类别,用于对通知进行分类管理。
  2. 创建并调度本地通知 :创建 UNMutableNotificationContent 对象设置通知内容,使用 UNCalendarNotificationTrigger 或其他触发器设置触发时间,最后通过 UNUserNotificationCenter 调度通知。
  3. 处理用户响应 :实现 UNUserNotificationCenterDelegate 协议的相关方法,处理用户点击通知或执行自定义操作的响应。

此外,还可通过编写通知内容扩展来进一步修改通知的二级界面,实现自定义设计。

iOS开发中的弹窗、快捷操作与本地通知

5. 本地通知授权详细解析

在iOS开发中,本地通知的授权是使用本地通知功能的重要前提。前面提到了获取授权的两种策略以及检查授权状态的方法,下面进一步详细解析授权过程中的关键要点。

getNotificationSettings 方法会异步返回一个 UNNotificationSettings 对象,该对象除了 authorizationStatus 属性外,还有其他一些属性可以描述应用在设置应用中的通知设置情况。这些属性在某些场景下可能会很有用,例如开发者可以根据这些设置来调整应用的行为。

requestAuthorization(options:) 方法中的 options 参数是一个 UNAuthorizationOptions 选项集,它决定了应用希望获得的通知权限范围。具体选项如下:
| 选项 | 描述 |
| ---- | ---- |
| .badge | 表示应用可能希望在通知触发时在应用图标上显示徽章数字。 |
| .sound | 表示应用可能希望在通知触发时播放声音。 |
| .alert | 表示应用可能希望在通知触发时显示通知提醒。 |
| .provideAppNotificationSettings | iOS 12 新增选项,后续会详细讨论。 |
| .provisional | iOS 12 新增选项,选择此选项可获得临时授权。 |

需要注意的是,在调用 requestAuthorization(options:) 时,要确保包含所有可能需要的选项,因为一旦授权完成,就无法再次请求这些权限。例如,如果不包含 .badge 选项,用户在设置应用中就不会看到允许应用显示徽章的开关。

以下是一个更详细的获取授权的代码示例,展示了如何根据不同的授权状态进行相应的处理:

let center = UNUserNotificationCenter.current()
center.getNotificationSettings { settings in
    switch settings.authorizationStatus {
    case .denied:
        // 可以弹出一个提醒框,请求用户前往设置应用授权
        let alert = UIAlertController(title: "授权提醒", message: "请前往设置应用授权本应用的通知权限", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
        alert.addAction(okAction)
        // 这里需要在主线程中展示 alert
        DispatchQueue.main.async {
            self.present(alert, animated: true, completion: nil)
        }
    case .authorized:
        // 拥有完全授权,可以直接调度通知
        self.scheduleLocalNotification()
    case .provisional:
        // 拥有临时授权,也可以调度通知
        self.scheduleLocalNotification()
    case .notDetermined:
        center.requestAuthorization(options: [.alert, .sound, .badge]) { ok, err in
            if let err = err {
                print("授权出错: \(err.localizedDescription)")
                return
            }
            if ok {
                // 授权成功,调度通知
                self.scheduleLocalNotification()
            } else {
                // 授权失败,可以给出提示
                let alert = UIAlertController(title: "授权失败", message: "您拒绝了本应用的通知授权", preferredStyle: .alert)
                let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
                alert.addAction(okAction)
                DispatchQueue.main.async {
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }
    }
}

func scheduleLocalNotification() {
    // 这里实现创建并调度本地通知的逻辑
    let content = UNMutableNotificationContent()
    content.title = "本地通知标题"
    content.body = "本地通知内容"
    content.sound = UNNotificationSound.default

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
    let request = UNNotificationRequest(identifier: "localNotification", content: content, trigger: trigger)

    let center = UNUserNotificationCenter.current()
    center.add(request) { (error) in
        if let error = error {
            print("调度通知出错: \(error.localizedDescription)")
        } else {
            print("通知调度成功")
        }
    }
}
6. 本地通知的高级应用

除了基本的本地通知功能,还可以利用本地通知实现一些高级应用场景。

6.1 自定义通知操作

在本地通知的二级界面中,可以添加自定义操作按钮。例如,在一个待办事项应用中,当通知提醒用户有任务到期时,可以添加“完成任务”和“推迟任务”两个自定义操作按钮。用户点击这些按钮后,应用可以执行相应的操作。

要实现自定义操作,需要先注册通知类别,并在类别中添加自定义操作。以下是一个示例代码:

// 注册通知类别
let completeAction = UNNotificationAction(identifier: "completeTask", title: "完成任务", options: [])
let postponeAction = UNNotificationAction(identifier: "postponeTask", title: "推迟任务", options: [])
let category = UNNotificationCategory(identifier: "taskReminder", actions: [completeAction, postponeAction], intentIdentifiers: [], options: [])

let center = UNUserNotificationCenter.current()
center.setNotificationCategories([category])

// 创建通知时指定类别
let content = UNMutableNotificationContent()
content.title = "任务提醒"
content.body = "您有一个任务到期了"
content.categoryIdentifier = "taskReminder"

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
let request = UNNotificationRequest(identifier: "taskNotification", content: content, trigger: trigger)

center.add(request) { (error) in
    if let error = error {
        print("调度通知出错: \(error.localizedDescription)")
    }
}

// 处理用户操作
extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        if response.actionIdentifier == "completeTask" {
            // 处理完成任务操作
            print("用户完成了任务")
        } else if response.actionIdentifier == "postponeTask" {
            // 处理推迟任务操作
            print("用户推迟了任务")
        }
        completionHandler()
    }
}
6.2 通知附件的使用

本地通知可以携带附件,如图片、音频、视频等。例如,在一个新闻应用中,当有新的新闻通知时,可以附带新闻的封面图片。

要添加附件,需要创建 UNNotificationAttachment 对象,并将其添加到通知内容中。以下是一个添加图片附件的示例代码:

let content = UNMutableNotificationContent()
content.title = "新闻通知"
content.body = "有一条新的新闻"

if let imageURL = Bundle.main.url(forResource: "newsImage", withExtension: "jpg") {
    do {
        let attachment = try UNNotificationAttachment(identifier: "newsImage", url: imageURL, options: nil)
        content.attachments = [attachment]
    } catch {
        print("添加附件出错: \(error.localizedDescription)")
    }
}

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
let request = UNNotificationRequest(identifier: "newsNotification", content: content, trigger: trigger)

let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
    if let error = error {
        print("调度通知出错: \(error.localizedDescription)")
    }
}
7. 总结

在iOS开发中,弹窗、快捷操作和本地通知是提升用户体验和实现交互功能的重要手段。弹窗和操作表虽然有一定的局限性,但在简单的交互场景中非常实用。当需要更复杂的界面时,可以考虑使用弹出框、表单或全屏展示的视图控制器等替代方案。

快捷操作提供了一种便捷的方式让用户快速访问应用的功能,分为静态和动态两种类型,开发者可以根据需求灵活配置。

本地通知则允许应用在后台或未运行时向用户发送提醒,使用本地通知需要经过获取授权、注册类别、创建并调度通知以及处理用户响应等步骤。同时,还可以通过自定义操作、添加附件和编写通知内容扩展等方式实现更高级的应用场景。

通过合理运用这些功能,开发者可以为用户打造更加丰富、便捷的应用体验。

graph LR
    A[开始] --> B[检查授权状态]
    B --> C{授权状态}
    C -->|.denied| D[请求用户前往设置授权]
    C -->|.authorized| E[调度本地通知]
    C -->|.provisional| E
    C -->|.notDetermined| F[请求授权]
    F --> G{授权结果}
    G -->|成功| E
    G -->|失败| H[提示用户授权失败]

以上流程图总结了本地通知授权和调度的主要流程,帮助开发者更好地理解和实现本地通知功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值