iOS 18中SwiftUI NavigationLink请求通讯录权限时自动返回的解决办法
在 iOS 18 环境下,通过 NavigationView 多级推送页面后,在目标页面触发通讯录权限请求时,系统权限弹窗弹出前页面会自动退回上一级。尝试推送、全屏覆盖等方式均无法解决,但该功能在 iOS 16 中可正常运行。
问题原因推测
iOS 18 对已过时的 NavigationView 兼容性存在缺陷,权限请求的时机与视图生命周期的交互触发了导航栈的意外状态重置;或者权限请求的线程处理导致 SwiftUI 视图状态异常。
解决方案
1. 替换为 NavigationStack(优先推荐)
NavigationView 在 iOS 16+ 已被官方标记为过时,iOS 18 对其的支持存在兼容性问题,改用 NavigationStack 可从根源避免此类导航栈异常。
2. 确保权限请求在主线程异步触发
通过主线程异步调度发起权限请求,确保视图完全渲染、状态稳定后再触发请求,避免与视图加载过程冲突。
修改后的完整代码示例
import SwiftUI import Contacts struct AView: View { @State private var goB = false var body: some View { // 替换为 NavigationStack NavigationStack { VStack { NavigationLink(destination: BView(), isActive: $goB) { EmptyView() } Button("前往 B 页面") { goB = true } } .navigationTitle("页面 A") } } } struct BView: View { @State private var goC = false var body: some View { VStack { NavigationLink(destination: CView(), isActive: $goC) { EmptyView() } Button("前往 C 页面") { goC = true } } .navigationTitle("页面 B") } } struct CView: View { @State private var goD = false var body: some View { VStack { NavigationLink(destination: DView(), isActive: $goD) { EmptyView() } Button("前往 D 页面") { goD = true } } .navigationTitle("页面 C") } } struct DView: View { @State private var permissionStatus: CNAuthorizationStatus = .notDetermined var body: some View { VStack { Text("页面 D") Button("请求通讯录权限") { guard permissionStatus == .notDetermined else { // 已授权或拒绝,可提示用户前往设置修改 return } // 主线程异步触发请求,确保视图状态稳定 DispatchQueue.main.async { let store = CNContactStore() store.requestAccess(for: .contacts) { granted, error in print("权限获取状态:\(granted)") if let error = error { print("权限请求错误:\(error.localizedDescription)") } // 更新权限状态 permissionStatus = CNContactStore.authorizationStatus(for: .contacts) } } } } .navigationTitle("页面 D") .onAppear { // 视图加载时检查当前权限状态 permissionStatus = CNContactStore.authorizationStatus(for: .contacts) } } }
额外说明
如果替换 NavigationStack 后仍有问题,可通过提前检查权限状态的方式,避免重复触发未确定状态的权限请求,进一步降低视图状态异常的概率。
内容的提问来源于stack exchange,提问作者mmy
相关产品推荐
相关产品推荐

