SwiftUI中fullScreenCover报错:已有有效定向事务令牌时请求新令牌
问题详情
在SwiftUI中使用fullScreenCover修饰符时触发断言错误:
"A new orientation transaction token is being requested while a valid one already exists."
错误发生在全屏视图过渡阶段(关闭当前视图并展示新视图),报错信息包含fromVC和toVC的实例引用。当前窗口与场景方向均设置为竖屏(portrait),且存在一个原始方向同样为竖屏的活跃定向事务令牌。
已尝试但无效的方案:
- 显式设置
windowOrientation与sceneOrientation - 将
fullScreenCover包裹在NavigationStack内部 - 展示新视图前调用
presentationMode.wrappedValue.dismiss()关闭当前视图
完整报错信息
2023-06-10 21:37:10.137760+0300 SignEase[50521:19426350] [断言]
当已有有效令牌时,仍在请求新的定向事务令牌。原因=全屏过渡(正在关闭):
fromVC=<_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x13b80f200>;
toVC=<_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x13a8d7c00>;;;
windowOrientation=portrait; sceneOrientation=portrait;
existingTransaction=<__UIForcedOrientationTransactionToken: 0x280b814a0; state: active; originalOrientation: portrait (1)>
相关代码
import SwiftUI import Firebase import FirebaseCore import FirebaseAuth @available(iOS 16.0, *) struct SignUp: View { @StateObject private var viewModal = SignViewData() @Environment(\.presentationMode) var presentationMode @State private var mainView = false @State private var SignInView = false var body: some View { ZStack { VStack(alignment: .leading){ Text("Sign Up") .font(.largeTitle) .fontWeight(.bold) .foregroundStyle(.linearGradient(colors: [.pink.opacity(0.6), .primary.opacity(0.5)], startPoint: .topLeading, endPoint: .bottomTrailing)) .padding(.leading,10) .padding(.bottom,20) Section{ TextField("", text: $viewModal.text) .textContentType(.emailAddress) .keyboardType(.emailAddress) .autocapitalization(.none) .disableAutocorrection(true) .placeholder(when: viewModal.text.isEmpty) { Text("Email address") .foregroundColor(.primary) .blendMode(.overlay) } .customField(icon: "envelope.open.fill") SecureField("", text: $viewModal.password) .textContentType(.password) .placeholder(when: viewModal.password.isEmpty) { Text("Password") .foregroundColor(.primary) .blendMode(.overlay) } .customField(icon: "key.fill") } .padding(.leading,10) .padding(.trailing,10) .padding(.bottom,10) Button { Task{ do{ try await viewModal.signUp() if viewModal.value == true{ DispatchQueue.main.async { // Add DispatchQueue.main.async block mainView.toggle() } } return } catch { print(error) } } } label: { ButtonView(title: "Create Account") } .padding(.leading,10) .padding(.trailing,10) .fullScreenCover(isPresented: $mainView) { // Any time i segue into this view i keep getting the error down below NavigationStack{ RootView() } } Divider() .padding(5) .padding(.leading,10) .padding(.trailing,10) HStack{ Text("Already have an account?") .font(.subheadline) .foregroundStyle(.linearGradient(colors: [.pink.opacity(0.7), .primary.opacity(0.5)], startPoint: .topLeading, endPoint: .bottomTrailing)) Button { SignInView.toggle() } label: { Text("Sign In") .font(.subheadline) .fontWeight(.semibold) .foregroundColor(.black) } } .fullScreenCover(isPresented:$SignInView){ NavigationStack{ SignIn() } } .padding(.leading,20) }.padding(15) .frame(width: 400,height: 370) .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 30, style: .continuous)) .shadow(color: Color("Shadow").opacity(0.3), radius: 10, x: 0, y: 10) .strokeStyle(cornerRadius: 30) .padding(20) }.background( Image("Background 4") ) } } @available(iOS 16.0, *) struct SignUp_Previews: PreviewProvider { static var previews: some View { SignUp() } } extension View { func placeholder<Content: View>( when shouldShow: Bool, alignment: Alignment = .leading, @ViewBuilder placeholder: () -> Content) -> some View { ZStack(alignment: alignment) { placeholder().opacity(shouldShow ? 1 : 0) self } } }
可行解决方案
1. 延迟触发全屏视图展示
在切换状态时添加微小延迟,避开当前活跃的定向事务周期:
if viewModal.value == true { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { mainView.toggle() } }
2. 使用item绑定替代布尔值触发
通过绑定Identifiable类型的可选对象来触发全屏视图,这种方式的过渡逻辑更稳定:
// 新增触发标识结构体 @State private var showRootView: RootViewTrigger? struct RootViewTrigger: Identifiable { let id = UUID() } // 修改按钮动作 Button { Task{ do{ try await viewModal.signUp() if viewModal.value == true{ DispatchQueue.main.async { showRootView = RootViewTrigger() } } return } catch { print(error) } } } label: { ButtonView(title: "Create Account") } // 修改fullScreenCover绑定方式 .fullScreenCover(item: $showRootView) { _ in NavigationStack{ RootView() } }
3. 检查目标视图的定向设置
确保RootView、SignInView中没有强制设置界面方向的代码(比如UIKit的preferredInterfaceOrientationForPresentation方法),避免和当前定向事务冲突。
4. 升级系统与开发工具版本
该断言错误在iOS 16早期版本中存在,升级到iOS 16.4+或Xcode 14.3+可修复部分系统层面的bug。
内容的提问来源于stack exchange,提问作者Lawal Abdulganiy

