You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何实现用户自定义时间段禁用推送通知

嘿,刚好我之前做过类似的功能,Swift完全支持实现自定义时间段禁用推送通知的需求,下面给你详细拆解实现思路和步骤:

一、核心思路说明

iOS系统本身并没有直接提供「按时间段自动禁用推送」的原生API,但我们可以通过客户端本地拦截的方式实现这个功能——简单来说,就是在收到推送通知时,判断当前时间是否处于用户设置的禁用时间段,再决定是否展示通知。

如果需要完全禁止后台推送的展示,还可以配合后端逻辑:在用户设置的禁用时间段内,后端暂停向APNs发送推送请求。不过客户端侧的本地拦截是最基础且必须的部分。

二、具体实现步骤

1. 存储用户自定义的禁用时间段

我们可以用UserDefaults来持久化用户设置的时间段,比如存储开始/结束的小时和分钟:

// 保存用户设置的免打扰时间段
func saveDoNotDisturbPeriod(startHour: Int, startMinute: Int, endHour: Int, endMinute: Int) {
    UserDefaults.standard.set(startHour, forKey: "DNDStartHour")
    UserDefaults.standard.set(startMinute, forKey: "DNDStartMinute")
    UserDefaults.standard.set(endHour, forKey: "DNDEndHour")
    UserDefaults.standard.set(endMinute, forKey: "DNDEndMinute")
    // 保存后可以刷新通知相关状态
}

// 获取已保存的免打扰时间段
func getDoNotDisturbPeriod() -> (startHour: Int?, startMinute: Int?, endHour: Int?, endMinute: Int?) {
    let defaults = UserDefaults.standard
    let startHour = defaults.object(forKey: "DNDStartHour") as? Int
    let startMinute = defaults.object(forKey: "DNDStartMinute") as? Int
    let endHour = defaults.object(forKey: "DNDEndHour") as? Int
    let endMinute = defaults.object(forKey: "DNDEndMinute") as? Int
    return (startHour, startMinute, endHour, endMinute)
}

2. 判断当前时间是否在禁用时间段

写一个工具函数来检查当前时间是否处于用户设置的免打扰区间,注意要处理跨天的情况(比如22:00到次日06:00):

func isInDoNotDisturbPeriod() -> Bool {
    let (startHour, startMinute, endHour, endMinute) = getDoNotDisturbPeriod()
    guard let startH = startHour, let startM = startMinute, let endH = endHour, let endM = endMinute else {
        return false // 未设置时默认不禁用
    }
    
    let currentDate = Date()
    let calendar = Calendar.current
    guard let currentH = calendar.dateComponents([.hour], from: currentDate).hour,
          let currentM = calendar.dateComponents([.minute], from: currentDate).minute else {
        return false
    }
    
    // 转换为总分钟数,方便比较
    let startTotal = startH * 60 + startM
    let endTotal = endH * 60 + endM
    let currentTotal = currentH * 60 + currentM
    
    if startTotal < endTotal {
        // 时间段在同一天(如08:00-18:00)
        return currentTotal >= startTotal && currentTotal <= endTotal
    } else {
        // 时间段跨天(如22:00-06:00)
        return currentTotal >= startTotal || currentTotal <= endTotal
    }
}

3. 拦截推送通知的展示

通过UNUserNotificationCenterDelegate的方法,在收到推送时判断是否展示:

// 可以在AppDelegate或者专门的通知管理类中实现
extension AppDelegate: UNUserNotificationCenterDelegate {
    // 前台收到推送时的回调
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        if isInDoNotDisturbPeriod() {
            // 处于禁用时间段,不展示通知
            completionHandler([])
        } else {
            // 正常展示通知,可根据需求设置选项(横幅、声音等)
            completionHandler([.banner, .sound])
        }
    }
    
    // 通知被点击后的回调(不管是否在禁用时间段,用户点击都可以正常处理)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        // 处理通知点击逻辑,比如跳转对应页面
        completionHandler()
    }
}

4. 设置页面的UI实现

用时间选择器让用户设置免打扰时间段,这里以UIKit为例(SwiftUI可以用DatePicker绑定@AppStorage变量,更简洁):

class NotificationSettingsVC: UIViewController {
    private let startDatePicker = UIDatePicker()
    private let endDatePicker = UIDatePicker()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadSavedSettings()
    }
    
    private func setupUI() {
        // 配置时间选择器为仅显示时间
        startDatePicker.datePickerMode = .time
        endDatePicker.datePickerMode = .time
        
        // 添加保存按钮
        let saveBtn = UIButton(type: .system)
        saveBtn.setTitle("保存免打扰设置", for: .normal)
        saveBtn.addTarget(self, action: #selector(saveSettings), for: .touchUpInside)
        
        // 布局(这里省略AutoLayout代码,根据你的项目需求实现)
        view.addSubview(startDatePicker)
        view.addSubview(endDatePicker)
        view.addSubview(saveBtn)
    }
    
    private func loadSavedSettings() {
        let (startHour, startMinute, endHour, endMinute) = getDoNotDisturbPeriod()
        let calendar = Calendar.current
        
        // 加载开始时间
        if let h = startHour, let m = startMinute {
            var components = DateComponents()
            components.hour = h
            components.minute = m
            startDatePicker.date = calendar.date(from: components) ?? Date()
        }
        
        // 加载结束时间
        if let h = endHour, let m = endMinute {
            var components = DateComponents()
            components.hour = h
            components.minute = m
            endDatePicker.date = calendar.date(from: components) ?? Date()
        }
    }
    
    @objc private func saveSettings() {
        let calendar = Calendar.current
        let startComponents = calendar.dateComponents([.hour, .minute], from: startDatePicker.date)
        let endComponents = calendar.dateComponents([.hour, .minute], from: endDatePicker.date)
        
        guard let startH = startComponents.hour, let startM = startComponents.minute,
              let endH = endComponents.hour, let endM = endComponents.minute else {
            return
        }
        
        saveDoNotDisturbPeriod(startHour: startH, startMinute: startM, endHour: endH, endMinute: endM)
        
        // 提示用户保存成功
        let alert = UIAlertController(title: "设置已保存", message: "免打扰时间段将生效", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        present(alert, animated: true)
    }
}
三、额外注意事项
  • 后台推送的限制:如果应用在后台,系统会直接展示推送,此时客户端无法通过willPresent拦截。如果需要完全禁止后台推送展示,需要后端配合:在禁用时间段内停止向APNs发送推送请求。
  • 通知权限:确保应用已获取推送权限,启动时可以请求权限:
func requestNotificationPermission() {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        if let error = error {
            print("请求通知权限失败:\(error.localizedDescription)")
        }
    }
}
  • SwiftUI适配:如果用SwiftUI,推荐用@AppStorage直接绑定时间选择器,简化存储逻辑,比如:
@AppStorage("DNDStartHour") private var startHour = 22
@AppStorage("DNDStartMinute") private var startMinute = 0
// 然后在DatePicker中绑定这些变量

内容的提问来源于stack exchange,提问作者CodeSteger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:43:31