diff --git a/.DS_Store b/.DS_Store index b7b5d2b..c9ef09f 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate b/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate index d860fb8..4a53c9a 100644 Binary files a/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate and b/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/wake.xcodeproj/xcuserdata/elliwood.xcuserdatad/xcschemes/xcschememanagement.plist b/wake.xcodeproj/xcuserdata/elliwood.xcuserdatad/xcschemes/xcschememanagement.plist index a4c98a3..62e325e 100644 --- a/wake.xcodeproj/xcuserdata/elliwood.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/wake.xcodeproj/xcuserdata/elliwood.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ wake.xcscheme_^#shared#^_ orderHint - 0 + 3 diff --git a/wake/.DS_Store b/wake/.DS_Store index b3853f3..efa3987 100644 Binary files a/wake/.DS_Store and b/wake/.DS_Store differ diff --git a/wake/ContentView.swift b/wake/ContentView.swift index 976c126..f0f25a5 100644 --- a/wake/ContentView.swift +++ b/wake/ContentView.swift @@ -70,7 +70,9 @@ struct ContentView: View { } } // 登录按钮 - NavigationLink(destination: LoginView()) { + Button(action: { + showLogin = true + }) { Text("登录") .font(.headline) .padding(.horizontal, 16) @@ -80,6 +82,9 @@ struct ContentView: View { .cornerRadius(8) } .padding(.trailing) + .fullScreenCover(isPresented: $showLogin) { + LoginView() + } } Spacer() diff --git a/wake/Info.plist b/wake/Info.plist index 23b998e..92853ab 100644 --- a/wake/Info.plist +++ b/wake/Info.plist @@ -22,8 +22,8 @@ Sign in with Apple is used to authenticate your account UIAppFonts - Quicksand x.ttf - SankeiCutePopanime.ttf + Inter.ttf + Quicksand X.ttf Quicksand-Regular.ttf Quicksand-Bold.ttf Quicksand-SemiBold.ttf diff --git a/wake/Resources/.DS_Store b/wake/Resources/.DS_Store new file mode 100644 index 0000000..99b4c6a Binary files /dev/null and b/wake/Resources/.DS_Store differ diff --git a/wake/Resources/Fonts/Inter.ttf b/wake/Resources/Fonts/Inter.ttf new file mode 100644 index 0000000..e31b51e Binary files /dev/null and b/wake/Resources/Fonts/Inter.ttf differ diff --git a/wake/Resources/Quicksand x.ttf b/wake/Resources/Fonts/Quicksand x.ttf similarity index 100% rename from wake/Resources/Quicksand x.ttf rename to wake/Resources/Fonts/Quicksand x.ttf diff --git a/wake/Resources/SankeiCutePopanime.ttf b/wake/Resources/SankeiCutePopanime.ttf deleted file mode 100644 index 5f31eeb..0000000 Binary files a/wake/Resources/SankeiCutePopanime.ttf and /dev/null differ diff --git a/wake/Theme.swift b/wake/Theme.swift index 7a3f307..a6c3457 100644 --- a/wake/Theme.swift +++ b/wake/Theme.swift @@ -32,6 +32,8 @@ struct Theme { static let textSecondary = Color(hex: "6B7280") // 次级文本色 static let textTertiary = Color(hex: "9CA3AF") // 三级文本色 static let textInverse = Color.white // 反色文本 + static let textMessage = Color(hex: "7B7B7B") // 注释颜色 + static let textMessageMain = Color(hex: "000000") // 注释主要颜色 // MARK: - 状态色 static let success = Color(hex: "10B981") // 成功色 @@ -115,6 +117,8 @@ extension Color { static var themeSurface: Color { Theme.Colors.surface } static var themeTextPrimary: Color { Theme.Colors.textPrimary } static var themeTextSecondary: Color { Theme.Colors.textSecondary } + static var themeTextMessage: Color { Theme.Colors.textMessage } + static var themeTextMessageMain: Color { Theme.Colors.textMessageMain } } // MARK: - 预览 diff --git a/wake/Typography.swift b/wake/Typography.swift index 27f7b98..ecdc952 100644 --- a/wake/Typography.swift +++ b/wake/Typography.swift @@ -3,12 +3,10 @@ import SwiftUI // MARK: - 字体库枚举 /// 定义应用中可用的字体库 enum FontFamily: String, CaseIterable { - case sankeiCute = "SankeiCutePopanime" // 可爱风格字体 - case quicksand = "Quicksand x" // 主题字体 + case quicksand = "Quicksand x" case quicksandBold = "Quicksand-Bold" case quicksandRegular = "Quicksand-Regular" - // 后续添加新字体库时在这里添加新 case - // 例如: case anotherFont = "AnotherFontName" + case inter = "Inter" /// 获取字体名称 var name: String { @@ -19,6 +17,7 @@ enum FontFamily: String, CaseIterable { // MARK: - 文本样式枚举 /// 定义应用中使用的文本样式类型 enum TypographyStyle { + case largeTitle // 大标题 case headline // 大标题 case title // 标题 case body // 正文 @@ -44,11 +43,12 @@ struct Typography { /// 文本样式配置表 private static let styleConfig: [TypographyStyle: TypographyConfig] = [ + .largeTitle: TypographyConfig(size: 32, weight: .heavy, textStyle: .largeTitle), .headline: TypographyConfig(size: 24, weight: .bold, textStyle: .headline), .title: TypographyConfig(size: 20, weight: .semibold, textStyle: .title2), .body: TypographyConfig(size: 16, weight: .regular, textStyle: .body), .subtitle: TypographyConfig(size: 14, weight: .medium, textStyle: .subheadline), - .caption: TypographyConfig(size: 12, weight: .light, textStyle: .caption1), + .caption: TypographyConfig(size: 12, weight: .regular, textStyle: .caption1), .footnote: TypographyConfig(size: 11, weight: .regular, textStyle: .footnote), .small: TypographyConfig(size: 10, weight: .regular, textStyle: .headline) ] diff --git a/wake/View/Components/AppleSignInButton.swift b/wake/View/Components/AppleSignInButton.swift new file mode 100644 index 0000000..cd61af0 --- /dev/null +++ b/wake/View/Components/AppleSignInButton.swift @@ -0,0 +1,95 @@ +import SwiftUI +import AuthenticationServices +import CryptoKit + +/// 自定义的 Apple 登录按钮组件 +struct AppleSignInButton: View { + // MARK: - 属性 + + /// 授权请求回调 + let onRequest: (ASAuthorizationAppleIDRequest) -> Void + + /// 授权完成回调 + let onCompletion: (Result) -> Void + + /// 按钮文字 + let buttonText: String + + // MARK: - 初始化方法 + + init(buttonText: String = "Continue with Apple", + onRequest: @escaping (ASAuthorizationAppleIDRequest) -> Void, + onCompletion: @escaping (Result) -> Void) { + self.buttonText = buttonText + self.onRequest = onRequest + self.onCompletion = onCompletion + } + + // MARK: - 视图主体 + + var body: some View { + Button(action: handleSignIn) { + HStack(alignment: .center, spacing: 8) { + Image(systemName: "applelogo") + .font(.system(size: 20, weight: .regular)) + Text(buttonText) + .font(.system(size: 18, weight: .regular)) + } + .frame(maxWidth: .infinity) + .frame(height: 60) + .background(Color.white) + .foregroundColor(.black) + .cornerRadius(30) + .overlay( + RoundedRectangle(cornerRadius: 30) + .stroke(Color.black, lineWidth: 1) // 使用黑色边框 + ) + } + } + + // MARK: - 私有方法 + + private func handleSignIn() { + let provider = ASAuthorizationAppleIDProvider() + let request = provider.createRequest() + request.requestedScopes = [.fullName, .email] + + // 创建 nonce 用于安全验证 + let nonce = String.randomURLSafeString(length: 32) + request.nonce = sha256(nonce) + + // 调用请求回调 + onRequest(request) + + // 创建并显示授权控制器 + let controller = ASAuthorizationController(authorizationRequests: [request]) + controller.delegate = Coordinator(onCompletion: onCompletion) + controller.performRequests() + } + + private func sha256(_ input: String) -> String { + let inputData = Data(input.utf8) + let hashedData = SHA256.hash(data: inputData) + return hashedData.compactMap { String(format: "%02x", $0) }.joined() + } + + // MARK: - 协调器 + + private class Coordinator: NSObject, ASAuthorizationControllerDelegate { + let onCompletion: (Result) -> Void + + init(onCompletion: @escaping (Result) -> Void) { + self.onCompletion = onCompletion + } + + // 授权成功回调 + func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { + onCompletion(.success(authorization)) + } + + // 授权失败回调 + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + onCompletion(.failure(error)) + } + } +} \ No newline at end of file diff --git a/wake/View/Login/Login.swift b/wake/View/Login/Login.swift index 4069ecd..0a736b3 100644 --- a/wake/View/Login/Login.swift +++ b/wake/View/Login/Login.swift @@ -16,56 +16,54 @@ struct LoginView: View { // MARK: - Body var body: some View { - NavigationStack { - ZStack { - // Background - Color(red: 1.0, green: 0.67, blue: 0.15) - .edgesIgnoringSafeArea(.all) + ZStack { + // Background + Color(red: 1.0, green: 0.67, blue: 0.15) + .ignoresSafeArea() + + VStack(alignment: .leading, spacing: 4) { + Text("Hi, I'm MeMo!") + .font(Typography.font(for: .largeTitle)) + .foregroundColor(.black) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .padding(.top, 44) - VStack(alignment: .leading, spacing: 4) { - Text("Hi, I'm MeMo!") - .font(.largeTitle) - .fontWeight(.semibold) - .foregroundColor(.black) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.leading, 24) - .padding(.top, 44) - - Text("Welcome~") - .font(.largeTitle) - .fontWeight(.semibold) - .foregroundColor(.black) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.leading, 24) - .padding(.bottom, 20) - - Spacer() - } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + Text("Welcome~") + .font(Typography.font(for: .largeTitle)) + .foregroundColor(.black) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .padding(.bottom, 20) - VStack(spacing: 16) { - Spacer() - signInButton() - termsAndPrivacyView() - } - .padding() - .alert(isPresented: $showError) { - Alert( - title: Text("Error"), - message: Text(errorMessage), - dismissButton: .default(Text("OK")) - ) - } - - if isLoading { - loadingView() - } + Spacer() } - .navigationBarHidden(true) - .fullScreenCover(isPresented: $isLoggedIn) { - NavigationStack { - UserInfo() - } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + + VStack(spacing: 16) { + Spacer() + signInButton() + .padding(.horizontal, 24) + termsAndPrivacyView() + } + .frame(maxWidth: .infinity) + .alert(isPresented: $showError) { + Alert( + title: Text("Error"), + message: Text(errorMessage), + dismissButton: .default(Text("OK")) + ) + } + + if isLoading { + loadingView() + } + } + .navigationBarBackButtonHidden(true) + .navigationBarHidden(true) + .fullScreenCover(isPresented: $isLoggedIn) { + NavigationStack { + UserInfo() } } } @@ -73,22 +71,22 @@ struct LoginView: View { // MARK: - Views private func signInButton() -> some View { - SignInWithAppleButton( - onRequest: { request in - let nonce = String.randomURLSafeString(length: 32) - self.currentNonce = nonce - request.requestedScopes = [.fullName, .email] - request.nonce = self.sha256(nonce) - }, - onCompletion: handleAppleSignIn - ) - .signInWithAppleButtonStyle(.white) - .frame(height: 50) - .cornerRadius(25) - .overlay( - RoundedRectangle(cornerRadius: 25) - .stroke(Color.black, lineWidth: 1) - ) + AppleSignInButton { request in + let nonce = String.randomURLSafeString(length: 32) + self.currentNonce = nonce + request.nonce = self.sha256(nonce) + } onCompletion: { result in + switch result { + case .success(let authResults): + print("✅ [Apple Sign In] 登录授权成功") + if let appleIDCredential = authResults.credential as? ASAuthorizationAppleIDCredential { + self.processAppleIDCredential(appleIDCredential) + } + case .failure(let error): + print("❌ [Apple Sign In] 登录失败: \(error.localizedDescription)") + self.handleSignInError(error) + } + } } private func termsAndPrivacyView() -> some View { @@ -96,13 +94,13 @@ struct LoginView: View { HStack { Text("By continuing, you agree to our") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(.themeTextMessage) Button("Terms of") { openURL("https://yourwebsite.com/terms") } .font(.caption2) - .foregroundColor(.blue) + .foregroundColor(.themeTextMessageMain) } .multilineTextAlignment(.center) .padding(.horizontal, 24) @@ -112,24 +110,24 @@ struct LoginView: View { openURL("https://yourwebsite.com/terms") } .font(.caption2) - .foregroundColor(.blue) + .foregroundColor(.themeTextMessageMain) Text("and") - .foregroundColor(.secondary) + .foregroundColor(.themeTextMessage) .font(.caption) Button("Privacy Policy") { openURL("https://yourwebsite.com/privacy") } .font(.caption2) - .foregroundColor(.blue) + .foregroundColor(.themeTextMessageMain) } .padding(.top, 4) } .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity) .padding(.horizontal, 24) - .padding(.bottom, 24) + .padding(.vertical, 12) } private func loadingView() -> some View { diff --git a/wake/WakeApp.swift b/wake/WakeApp.swift index 3c17ecd..3d1766c 100644 --- a/wake/WakeApp.swift +++ b/wake/WakeApp.swift @@ -44,9 +44,7 @@ struct WakeApp: App { // 根据登录状态显示不同视图 if authState.isAuthenticated { // 已登录:显示userInfo页面 - // UserInfo() - // .environmentObject(authState) - MediaUploadDemo() + UserInfo() .environmentObject(authState) } else { // 未登录:显示登录界面