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

嵌入LoginView的SignInWithAppleButton点击无弹窗问题

问题分析与解决方案

我正在为应用实现Sign in with AppleID功能,完成后跳转至MongoDB Atlas。已实现可正常运行的SignInWithAppleButton,但该按钮仅能单独在ContentView中运行;集成到LoginView后,点击按钮时登录弹窗无法弹出。LoginView代码如下:

@Environment(\.colorScheme) var colorScheme

enum Field: Hashable {
    case username
    case password
}

@Binding var username: String?
@Binding var userEmail: String?
@Binding var firstLogin: Bool?
@Binding var prename: String
@Binding var surname: String
@State var email: String = ""

@State var wds: WatchlistDataService?
@StateObject var vm: LoginViewModel = LoginViewModel()

@State private var resetEmail = ""
@State private var password = ""
@State private var errorMessage = ""
@State private var inProgress = false
@State var showDialog = false
@State var showPWReset = false
@State var showPWResetConfirmation = false

@State var showOverlay = true

@FocusState private var focussedField: Field?

var body: some View {
    NavigationView {
        ZStack {
            LinearGradient(gradient: Gradient(colors: [Color.theme.backGroundGreen, Color.theme.backGroundPurple]), startPoint: .topTrailing, endPoint: .bottomLeading)
                .ignoresSafeArea()
            VStack(spacing: 16) {
                Image("Logo")
                    .resizable()
                    .frame(width: 300, height: 61)
                    .padding(.top, 75)
                    .padding(.bottom, 40)
                Text(errorMessage)
                    .foregroundColor(.red)
                    .padding(.horizontal)
                TextField("E-Mail Adresse", text: $email)
                    .padding(10)
                    .overlay {
                        RoundedRectangle(cornerRadius: 5)
                            .stroke(Color.theme.accent.opacity(0.1), lineWidth: 2)
                    }
                    .padding(.horizontal, 10)
                    .focused($focussedField, equals: .username)
                    .submitLabel(.next)
                    .onSubmit { focussedField = .password }
                
                SecureField("Passwort", text: $password)
                    .padding(10)
                    .overlay {
                        RoundedRectangle(cornerRadius: 5)
                            .stroke(Color.theme.accent.opacity(0.1), lineWidth: 2)
                    }
                    .padding(.horizontal, 10)
                    .focused($focussedField, equals: .password)
                    .onSubmit{userAction()
                        endTextEditing()}
                    .submitLabel(.go)
                HStack {
                    loginButton
                        .disabled(email == "" || password == "")
                    NavigationLink {
                        RegistrationView(prename: $prename, surname: $surname, email: $email)
                    } label: {
                        registerButton
                    }
                }
                                  
                signInWithApple
                
                HStack{
                    Spacer()
                    NavigationLink {
                        PasswordResetView()
                    } label: {
                        Text("Ich habe mein Passwort vergessen")
                            .font(.caption)
                            .underline()
                            .foregroundColor(colorScheme == .light ? .blue : Color.theme.accent)
                    }
                    .padding()
                    .ignoresSafeArea(.keyboard, edges: .bottom)
                    Spacer()
                }
                if inProgress {
                    ProgressView()
                }
            }
            .onTapGesture {
                self.endTextEditing()
            }
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    focussedField = .username
                }
                Analytics.logEvent(AnalyticsEventScreenView,
                                   parameters: [AnalyticsParameterScreenName: "Login"])
            }
            .confirmationDialog("Bitte gültige Email Adresse eingeben.", isPresented: $showDialog, titleVisibility: .visible, actions: {
                Button("Ok") {
                    showDialog = false
                }
            })
        }

    }
    .navigationBarTitle("")
            .navigationBarBackButtonHidden(true)
            .navigationBarHidden(true)
}

extension LoginView {
    var loginButton: some View {
        Button {
            userAction()
        } label: {
            ZStack{
                RoundedRectangle(cornerRadius: 10)
                    .stroke(lineWidth: 1)
                    .foregroundColor(Color.theme.accent)
                RoundedRectangle(cornerRadius: 10)
                    .foregroundColor(Color.theme.accent.opacity(0.2))
                Text("Mit E-Mail anmelden")
                    .foregroundStyle(Color.theme.primaryFont)
            }
            .frame(width: 180, height: 40)
        }
    }
    
    var registerButton: some View {
            ZStack{
                RoundedRectangle(cornerRadius: 10)
                    .stroke(lineWidth: 1)
                    .foregroundColor(Color.theme.accent)
                RoundedRectangle(cornerRadius: 10)
                    .foregroundColor(Color.theme.accent.opacity(0.2))
                Text("Mit E-Mail registrieren")
                    .foregroundStyle(Color.theme.primaryFont)
            }
            .frame(width: 180, height: 40)
    }
    
    func loginWithApple(idToken: String){
        print("loginWithApple()")
        // Fetch IDToken via the Apple SDK
        let credentials = Credentials.apple(idToken: idToken)
        myRealmApp.login(credentials: credentials) { (result) in
            switch result {
            case .failure(let error):
                print("Login failed: \(error.localizedDescription)")
            case .success(let user):
                vm.updateLoginStatetoLoggedIn()
                print("Successfully logged in as user \(user)")
                // Now logged in, do something with user
                // Remember to dispatch to main if you are doing anything on the UI thread
            }
        }
    }
    
    var signInWithApple: some View {
        SignInWithAppleButton(.signIn) { request in
            print("request started...")
            request.requestedScopes = [.email, .fullName]
        } onCompletion: { result in
            print("request completed...")
            print ("result: \(result)")
            switch result {
            case .success(let authResults):
                print("Authorisation successful")
                switch authResults.credential {
                case let credential as ASAuthorizationAppleIDCredential:
                    if let idToken = credential.identityToken {
                        guard let idTokenString = String(data: idToken, encoding: .utf8) else {
                            print("Unable to serialize token string from data: \(idToken.debugDescription)")
                            return
                        }
                        loginWithApple(idToken: idTokenString)
                        self.username = credential.email
                        self.prename = credential.fullName?.givenName ?? ""
                        self.surname = credential.fullName?.familyName ?? ""
                        self.email = credential.email ?? ""
                    }
                default:
                    print("default")
                    break
                }
            case .failure(let error):
                print("Authorisation failed: \(error.localizedDescription)")
            }
        }
        .frame(height: 50)
        .padding()
    }
}

核心问题排查与修复方案

1. 解决NavigationView层级遮挡问题

SignInWithAppleButton的弹窗需要正确的UIWindow层级,嵌套在NavigationView中可能导致弹窗被覆盖。添加导航视图样式确保层级正确:

var body: some View {
    NavigationView {
        // 原有ZStack内容
    }
    .navigationViewStyle(.stack) // 添加此行
    .navigationBarTitle("")
    .navigationBarBackButtonHidden(true)
    .navigationBarHidden(true)
}

2. 避免点击事件被拦截

VStack上的onTapGesture会拦截所有区域的点击,包括Apple登录按钮。修改为仅在非按钮区域触发失焦:

// 替换原有VStack的.onTapGesture
VStack(spacing: 16) {
    // 原有内容
}
.gesture(
    TapGesture()
        .onEnded {
            endTextEditing()
        }
)

或者给Apple登录按钮添加高优先级手势,确保点击优先响应:

var signInWithApple: some View {
    SignInWithAppleButton(.signIn) { request in
        print("request started...")
        request.requestedScopes = [.email, .fullName]
    } onCompletion: { result in
        // 原有逻辑
    }
    .frame(height: 50)
    .padding()
    .highPriorityGesture(TapGesture()) // 添加此行
}

3. 清除输入框焦点再触发登录

当前输入框处于FocusState时,可能干扰Apple登录弹窗的触发。点击按钮时先取消焦点:

var signInWithApple: some View {
    Button(action: {
        focussedField = nil
        endTextEditing()
    }) {
        SignInWithAppleButton(.signIn) { request in
            request.requestedScopes = [.email, .fullName]
        } onCompletion: { result in
            // 原有逻辑
        }
        .frame(height: 50)
        .padding()
    }
}

4. 验证控制台日志

点击按钮后查看Xcode控制台,检查是否有以下错误:

  • Apple登录权限配置错误
  • 身份请求上下文异常
  • Token解析失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 09:27:04