SwiftUI中如何通过Toggle开关控制通知的开启与关闭
解决SwiftUI中Toggle与通知权限联动的问题
看起来你在尝试把Toggle状态和系统通知权限绑定,但遇到了@State的willSet/didSet不生效的问题,而且权限处理逻辑也有小疏漏,我来帮你梳理清楚解决方案:
问题核心分析
- @State的didSet/willSet失效:在SwiftUI的状态管理机制下,@State变量的didSet经常会因为视图刷新被绕过,无法稳定触发逻辑。官方更推荐用
.onChange(of:)修饰符来监听状态变化。 - 权限判断逻辑颠倒:你的
pushEnabledAtOSLevel函数里,把.denied状态当成了权限允许的情况,这完全搞反了——.denied是用户已经拒绝权限,此时应该引导用户去设置开启,而不是直接发通知。 - 关闭Toggle的逻辑缺失:你只处理了打开Toggle的场景,但关闭时需要取消所有已调度的通知(我们无法直接修改系统权限,只能控制本地推送的启停)。
修正后的完整实现
下面是调整后的代码,每一步都做了优化:
import SwiftUI import UserNotifications struct NotificationSettingsView: View { @State private var notificationsOn: Bool = false var body: some View { Toggle(isOn: $notificationsOn) { Text("Bildirimler") .font(.system(size: 17, design: .rounded)) } // 监听Toggle状态变化,替代didSet .onChange(of: notificationsOn) { newValue in if newValue { openNotifications() } else { closeNotifications() } } // 视图加载时同步系统权限状态到Toggle .onAppear { syncToggleWithSystemPermission() } } // 打开通知:检查权限并请求(必要时) private func openNotifications() { UNUserNotificationCenter.current().getNotificationSettings { settings in DispatchQueue.main.async { switch settings.authorizationStatus { case .notDetermined: // 用户未做选择,发起权限请求 UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in DispatchQueue.main.async { if success { print("Bildirimlere izin verildi.") self.notificationsOn = true self.sendTestNotification() } else { print("Bildirim izni reddedildi: \(error?.localizedDescription ?? "Bilinmeyen hata")") self.notificationsOn = false } } } case .authorized: // 已授权,直接发送测试通知 print("Bildirimler zaten aktif.") self.sendTestNotification() case .denied, .provisional, .ephemeral: // 权限被拒绝或其他限制状态,切回Toggle关闭状态 print("Bildirim izni reddedildi, lütfen ayarlar kısmından açın.") self.notificationsOn = false @unknown default: self.notificationsOn = false } } } } // 关闭通知:取消所有待发送的通知请求 private func closeNotifications() { UNUserNotificationCenter.current().removeAllPendingNotificationRequests() print("Tüm bekleyen bildirimler iptal edildi.") } // 发送测试通知 private func sendTestNotification() { let content = UNMutableNotificationContent() content.title = "Elini Yıka" content.subtitle = "Çabuk ol git! Elini Yıka!" content.sound = UNNotificationSound.default let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) { error in if let error = error { print("Bildirim gönderilemedi: \(error.localizedDescription)") } } } // 同步Toggle状态与当前系统权限 private func syncToggleWithSystemPermission() { UNUserNotificationCenter.current().getNotificationSettings { settings in DispatchQueue.main.async { self.notificationsOn = settings.authorizationStatus == .authorized } } } }
关键优化点
- 用
.onChange替代didSet:这是SwiftUI官方推荐的状态监听方式,能稳定捕获Toggle的状态变化。 - 权限逻辑分层处理:区分未授权、已授权、已拒绝等不同状态,对应不同的处理逻辑,避免逻辑混乱。
- 关闭Toggle时清理通知:取消所有待发送的通知请求,实现“关闭Toggle即停止推送”的需求。
- 主线程更新UI:所有修改@State变量的操作都切回主线程,避免后台线程操作UI导致的崩溃或状态不同步。
- 初始状态同步:视图加载时自动同步系统权限状态到Toggle,避免打开页面时状态不一致的问题。
这样调整后,你的Toggle就能和通知权限正确联动:打开时请求权限并发送通知,关闭时取消所有待推送通知,状态同步也更可靠。
内容的提问来源于stack exchange,提问作者Ufuk Köşker
相关产品推荐
相关产品推荐

