Swift实现指定时间段内按用户选择日期发送重复本地通知
Swift实现指定日期时段内间隔重复本地通知+CollectionView选中日期传递方案
需求与问题
需求
希望在上午10点至晚上10点之间,按照用户输入的时间间隔(示例为2小时),仅在用户选中的日期重复发送本地通知。
问题
- 如何在Swift中实现上述通知调度逻辑?
- 使用CollectionView展示日期选项时,在
didSelectItemAt方法中如何传递用户选中的日期?
用户提供的原始代码
var dateComponents = DateComponents() dateComponents.hour = 10 dateComponents.minute = 00 let userCalendar = Calendar.current var dateStart = userCalendar.date(from: dateComponents) var dateComponent = DateComponents() dateComponent.hour = 22 dateComponent.minute = 00 var dateEnd = userCalendar.date(from: dateComponent) while dateStart < dateEnd { //Add repeating hours start time UserDefaults.standard.set(self.txtField.text, forKey: "hoursSendary") let Nohours = UserDefaults.standard.integer(forKey: "hoursSendary") dateStart = dateStart?.addingTimeInterval(TimeInterval(Nohours * 60 * 60)) //Schedule notification With body and title. scheduleNotification(body: "Free your eyes", titles: "Screen Alarm",day: 1) } func scheduleNotification( body: String, titles:String,day:Int) { UserDefaults.standard.set(self.txtField.text, forKey: "hoursSendary") let Nohours = UserDefaults.standard.integer(forKey: "hoursSendary") let date = Date(timeIntervalSinceNow:TimeInterval(Nohours * 60 * 60) ) var components = DateComponents() components.weekday = day// sunday = 1 ... saturday = 7 components.weekdayOrdinal = 10 components.timeZone = .current components.hour = Nohours let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false) let content = UNMutableNotificationContent() content.title = titles content.body = body content.sound = UNNotificationSound.default content.categoryIdentifier = "ScreenAlarm" let request = UNNotificationRequest(identifier: "NotificationAt-\(date))", content: content, trigger: trigger) UNUserNotificationCenter.current().delegate = self //UNUserNotificationCenter.current().removeAllPendingNotificationRequests() UNUserNotificationCenter.current().add(request) {(error) in if let error = error { print("Uh oh! We had an error: \(error)") } } }
解决方案
一、实现指定日期时段内间隔重复本地通知
原代码问题分析
dateStart和dateEnd仅设置了时分,未关联用户选中的具体日期,会默认使用当前日期scheduleNotification中DateComponents配置错误:weekdayOrdinal = 10无意义,且未绑定选中日期的年/月/日- 重复读写
UserDefaults属于冗余操作,直接传递参数即可 - 通知标识符生成存在语法错误(多了一个右括号)
修正后的实现步骤与代码
- 先存储用户选中的日期集合
- 为每个选中日期生成当天10:00到22:00之间的所有触发时间点
- 基于每个时间点创建通知请求并调度
1. 定义存储选中日期的变量
var selectedDates: [Date] = [] // 存储用户从CollectionView选中的日期
2. 生成所有通知触发条件
func generateNotificationTriggers(forSelectedDates dates: [Date], intervalHours: Int) -> [UNCalendarNotificationTrigger] { let calendar = Calendar.current var triggers: [UNCalendarNotificationTrigger] = [] for date in dates { // 提取选中日期的年、月、日 guard let year = calendar.dateComponents([.year], from: date).year, let month = calendar.dateComponents([.month], from: date).month, let day = calendar.dateComponents([.day], from: date).day else { continue } // 当天开始时间:10:00 var startComponents = DateComponents() startComponents.year = year startComponents.month = month startComponents.day = day startComponents.hour = 10 startComponents.minute = 0 // 当天结束时间:22:00 var endComponents = DateComponents() endComponents.year = year endComponents.month = month endComponents.day = day endComponents.hour = 22 endComponents.minute = 0 guard var currentTime = calendar.date(from: startComponents), let endTime = calendar.date(from: endComponents) else { continue } // 遍历生成所有间隔时间点 while currentTime <= endTime { let triggerComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: currentTime) let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: false) triggers.append(trigger) // 增加间隔小时 currentTime = calendar.date(byAdding: .hour, value: intervalHours, to: currentTime)! } } return triggers }
3. 调度通知
func scheduleNotifications(forTriggers triggers: [UNCalendarNotificationTrigger], body: String, title: String) { let center = UNUserNotificationCenter.current() center.delegate = self // 可选:移除已有待发送通知,避免重复调度 center.removeAllPendingNotificationRequests() for (index, trigger) in triggers.enumerated() { let content = UNMutableNotificationContent() content.title = title content.body = body content.sound = UNNotificationSound.default content.categoryIdentifier = "ScreenAlarm" // 生成唯一标识符 let request = UNNotificationRequest(identifier: "ScreenAlarm-\(index)", content: content, trigger: trigger) center.add(request) { error in if let error = error { print("通知调度失败:\(error.localizedDescription)") } } } }
4. 调用示例(用户确认后执行)
// 获取用户输入的间隔小时 guard let intervalText = txtField.text, let intervalHours = Int(intervalText), intervalHours > 0 else { // 处理输入无效的情况,比如弹出提示 print("请输入有效的间隔小时数") return } // 生成所有触发条件 let triggers = generateNotificationTriggers(forSelectedDates: selectedDates, intervalHours: intervalHours) // 调度通知 scheduleNotifications(forTriggers: triggers, body: "Free your eyes", title: "Screen Alarm")
二、CollectionView选中日期的传递
实现思路
- 准备日期数据源(比如未来7天的日期)
- 在
didSelectItemAt中获取选中索引对应的日期,维护选中日期集合 - 自定义Cell展示日期并反馈选中状态
1. 准备日期数据源
var dateOptions: [Date] = [] // 初始化时生成日期选项(示例:未来7天) func setupDateOptions() { let calendar = Calendar.current for dayOffset in 0..<7 { guard let date = calendar.date(byAdding: .day, value: dayOffset, to: Date()) else { continue } dateOptions.append(date) } }
2. CollectionView代理方法实现
// MARK: - UICollectionView DataSource func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dateOptions.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DateCell", for: indexPath) as! DateCollectionViewCell let date = dateOptions[indexPath.item] cell.configure(with: date) // 设置初始选中状态 cell.isSelected = selectedDates.contains(date) return cell } // MARK: - UICollectionView Delegate func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedDate = dateOptions[indexPath.item] // 多选逻辑:切换选中状态 if let index = selectedDates.firstIndex(of: selectedDate) { selectedDates.remove(at: index) } else { selectedDates.append(selectedDate) } // 更新选中Cell的样式 if let cell = collectionView.cellForItem(at: indexPath) as? DateCollectionViewCell { cell.isSelected.toggle() } // 单选逻辑(替换多选逻辑): // selectedDates = [selectedDate] // collectionView.reloadData() }
3. 自定义日期Cell
class DateCollectionViewCell: UICollectionViewCell { @IBOutlet weak var dateLabel: UILabel! override var isSelected: Bool { didSet { // 切换选中样式 backgroundColor = isSelected ? .systemBlue : .systemGray5 dateLabel.textColor = isSelected ? .white : .label } } func configure(with date: Date) { let formatter = DateFormatter() formatter.dateFormat = "MM/dd" // 可自定义格式,比如"EEE, MMM dd" dateLabel.text = formatter.string(from: date) } }
内容的提问来源于stack exchange,提问作者githma amarasinghe
相关产品推荐
相关产品推荐

