SwiftUI集成Apple与Google登录按钮布局报错求助
解决“()->() cannot conform to shape style”错误并实现Apple/Google登录按钮并排显示
错误原因分析
该错误通常是编译器将闭包错误解析为ShapeStyle类型参数导致的,结合你的代码,核心问题是自定义按钮与系统登录按钮叠加时的语法歧义,以及部分样式设置的写法引发编译器解析混乱。
修正后的完整代码
import SwiftUI import AuthenticationServices import GoogleSignIn import GoogleSignInSwift import Firebase struct Login: View { @StateObject var loginModel = LoginViewModel() var body: some View { ScrollView(.vertical, showsIndicators: false){ VStack(alignment: .leading, spacing: 5){ Image(systemName: "person") .resizable() .aspectRatio(contentMode: .fill) .frame(width: 75, height: 75) .foregroundColor(.black) .ignoresSafeArea() (Text("Welcome") .foregroundColor(.black) + Text("\n Login to Continue") .foregroundColor(.secondary) ) .font(.title) .fontWeight(.semibold) .lineSpacing(10) .padding(.top, 20) .padding(.trailing, 15) // 自定义手机号输入框 CustomTextField(hint: "+16505551234", text: $loginModel.mobileNo) .disabled(loginModel.showOTPField) .opacity(loginModel.showOTPField ? 0.4 : 1) .overlay(alignment: .trailing, content:{ Button("Change"){ withAnimation(.easeInOut){ loginModel.showOTPField = false loginModel.otpCode = "" loginModel.CLIENT_CODE = "" } } .font(.caption) .foregroundColor(.indigo) .opacity(loginModel.showOTPField ? 1 : 0) .padding(.trailing, 15) }) .padding(.top, 40) // OTP输入框 CustomTextField(hint: "OTP Code", text: $loginModel.otpCode) .disabled(!loginModel.showOTPField) .opacity(!loginModel.showOTPField ? 0.4 : 1) .padding(.top, 20) // 获取/验证OTP按钮 Button(action: loginModel.showOTPField ? loginModel.verifyOTPCode : loginModel.getOTPCode) { HStack(spacing: 15){ Text(loginModel.showOTPField ? "Verify Code" : "Get OTP") .fontWeight(.semibold) .contentTransition(.identity) Image(systemName: "line.diagonal.arrow") .font(.title3) .rotationEffect(.init(degrees: 45)) } .foregroundColor(.black) .padding(.horizontal, 25) .padding(.vertical) .background( RoundedRectangle(cornerRadius: 10, style: .continuous) .fill(Color.black.opacity(0.05)) ) .padding(.top, 10) } // 分割文本 Text("(OR)") .foregroundColor(.gray) .frame(maxWidth: .infinity) .padding(.top,10) .padding(.bottom, 20) .padding(.horizontal) // 第三方登录按钮组 HStack (spacing: 10){ // Apple登录按钮 SignInWithAppleButton{ request in loginModel.nonce = randomNonceString() request.requestedScopes = [.email, .fullName] request.nonce = sha256(loginModel.nonce) } onCompletion: { result in switch result{ case .success(let user): guard let credential = user.credential as? ASAuthorizationAppleIDCredential else{ print("Error with Firebase") return } loginModel.appleAuthenticate(credential: credential) case .failure(let error): print(error.localizedDescription) } } .signInWithAppleButtonStyle(.black) .frame(height: 55) .cornerRadius(10) // Google登录按钮 if let clientID = FirebaseApp.app()?.options.clientID { GoogleSignInButton(action: { // 获取当前根视图控制器 guard let rootVC = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .first(where: {$0 is UIWindowScene})? .delegate as? UIWindowSceneDelegate??.window??.rootViewController else { print("Could not get root view controller") return } GIDSignIn.sharedInstance.signIn(with: GIDConfiguration(clientID: clientID), presenting: rootVC) { user, error in if let error = error { print(error.localizedDescription) return } if let user = user { loginModel.logGoogleUser(user: user) } } }) .frame(height: 55) .cornerRadius(10) } } .frame(maxWidth: .infinity) } .padding(.horizontal, 60) .padding(.vertical,15) } .alert(loginModel.errorMessage, isPresented: $loginModel.showError){} } } // 自定义输入框组件 struct CustomTextField: View { let hint: String @Binding var text: String var body: some View { TextField(hint, text: $text) .padding() .background(RoundedRectangle(cornerRadius: 10).fill(Color.black.opacity(0.05))) } } // 登录逻辑ViewModel class LoginViewModel: ObservableObject { @Published var mobileNo = "" @Published var otpCode = "" @Published var CLIENT_CODE = "" @Published var showOTPField = false @Published var errorMessage = "" @Published var showError = false var nonce = "" func getOTPCode() { // 实现获取OTP的业务逻辑 showOTPField = true } func verifyOTPCode() { // 实现验证OTP的业务逻辑 } func appleAuthenticate(credential: ASAuthorizationAppleIDCredential) { // 实现Apple登录Firebase的逻辑 } func logGoogleUser(user: GIDGoogleUser) { // 实现Google登录Firebase的逻辑 } } // Apple登录辅助函数 private func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0..<16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)") } return random } randoms.forEach { random in if remainingLength == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result } private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { String(format: "%02x", $0) }.joined() return hashString } // 预览视图 struct Login_Previews: PreviewProvider { static var previews: some View { Login() } }
关键修改说明
- 移除自定义按钮叠加逻辑:直接使用系统原生的
SignInWithAppleButton和GoogleSignInButton,避免自定义视图与系统按钮叠加导致的样式冲突和语法歧义。 - 修正Google登录视图控制器获取方式:使用标准的
UIApplication场景API获取根视图控制器,避免自定义方法引发的潜在错误。 - 简化样式写法:使用
.cornerRadius替代自定义RoundedRectangle叠加,消除编译器对ShapeStyle的解析混乱。 - 修正预览视图:确保预览的是
Login视图而非ContentView,避免不必要的警告。 - 规范方法命名:将
VerifyOTPCode改为小写开头的verifyOTPCode,符合Swift命名规范。
额外检查项
如果仍有错误,请确认:
GoogleSignIn、GoogleSignInSwift的版本与当前Xcode/Swift版本兼容。- Firebase已正确初始化,
FirebaseApp配置无误。 LoginViewModel中的方法签名与回调闭包参数类型完全匹配。
内容的提问来源于stack exchange,提问作者Sathish Kumar V
相关产品推荐
相关产品推荐

