diff --git a/WhereAreYou/WhereAreYou/Core/SceneDelegate.swift b/WhereAreYou/WhereAreYou/Core/SceneDelegate.swift index 369627d..aae2210 100644 --- a/WhereAreYou/WhereAreYou/Core/SceneDelegate.swift +++ b/WhereAreYou/WhereAreYou/Core/SceneDelegate.swift @@ -14,10 +14,27 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = makeRootTabBarController() + window?.rootViewController = makeRootLoginViewController() window?.makeKeyAndVisible() } + private func makeRootLoginViewController() -> UIViewController { + let loginViewController = LoginViewController() + loginViewController.onAppleLoginTap = { [weak self] in + self?.switchToHome() + } + return loginViewController + } + + private func switchToHome() { + guard let window else { return } + let tabBarController = makeRootTabBarController() + + UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve) { + window.rootViewController = tabBarController + } + } + private func makeRootTabBarController() -> UITabBarController { let homeNav = UINavigationController(rootViewController: HomeViewController()) homeNav.tabBarItem = UITabBarItem( @@ -57,4 +74,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func sceneDidEnterBackground(_ scene: UIScene) { } } - diff --git a/WhereAreYou/WhereAreYou/Presentation/Login/AppleLoginButton.swift b/WhereAreYou/WhereAreYou/Presentation/Login/AppleLoginButton.swift new file mode 100644 index 0000000..497f040 --- /dev/null +++ b/WhereAreYou/WhereAreYou/Presentation/Login/AppleLoginButton.swift @@ -0,0 +1,48 @@ +// +// AppleLoginButton.swift +// WhereAreYou +// +// Created by 김성훈 on 8/21/26. +// + +import UIKit +import AuthenticationServices + +/// Apple 로그인 버튼 — HIG 표준 컴포넌트를 감싸는 얇은 래퍼 +final class AppleLoginButton: UIView { + + var onTap: (() -> Void)? + + private lazy var button: ASAuthorizationAppleIDButton = { + let button = ASAuthorizationAppleIDButton(type: .signIn, style: .black) + button.cornerRadius = 12 + button.translatesAutoresizingMaskIntoConstraints = false + button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) + return button + }() + + init() { + super.init(frame: .zero) + translatesAutoresizingMaskIntoConstraints = false + setUpLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setUpLayout() { + addSubview(button) + NSLayoutConstraint.activate([ + button.topAnchor.constraint(equalTo: topAnchor), + button.leadingAnchor.constraint(equalTo: leadingAnchor), + button.trailingAnchor.constraint(equalTo: trailingAnchor), + button.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + } + + @objc private func buttonTapped() { + onTap?() + } + +} diff --git a/WhereAreYou/WhereAreYou/Presentation/Login/LoginViewController.swift b/WhereAreYou/WhereAreYou/Presentation/Login/LoginViewController.swift new file mode 100644 index 0000000..cb9f9b9 --- /dev/null +++ b/WhereAreYou/WhereAreYou/Presentation/Login/LoginViewController.swift @@ -0,0 +1,112 @@ +// +// LoginViewController.swift +// WhereAreYou +// +// Created by 김성훈 on 8/21/26. +// + +import UIKit + +/// 로그인 화면 — 앱 진입 시 최초로 표시되는 화면 +final class LoginViewController: UIViewController { + + // MARK: - Properties + + var onAppleLoginTap: (() -> Void)? + + // MARK: - UI + + private let backgroundImageView: UIImageView = { + let imageView = UIImageView(image: .loginBackground) + imageView.contentMode = .scaleAspectFill + imageView.clipsToBounds = true + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() + + private let logoImageView: UIImageView = { + let imageView = UIImageView(image: .logo) + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() + + private let titleLabel: UILabel = { + let label = UILabel() + label.text = "어딘데" + label.font = .boldPreferredFont(forTextStyle: .title1) + label.textColor = .blue1 + label.textAlignment = .center + label.adjustsFontForContentSizeCategory = true + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private let subtitleLabel: UILabel = { + let label = UILabel() + label.text = "약속 장소로 가는 길, 함께 확인해요" + label.font = .preferredFont(forTextStyle: .subheadline) + label.textColor = .secondaryLabel + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private let appleLoginButton: AppleLoginButton = AppleLoginButton() + + // MARK: - Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + setUpView() + setUpLayout() + setUpActions() + } + + // MARK: - Set Up + + private func setUpView() { + view.backgroundColor = .pointBackground + } + + private func setUpLayout() { + view.addSubview(backgroundImageView) + view.addSubview(logoImageView) + view.addSubview(titleLabel) + view.addSubview(subtitleLabel) + view.addSubview(appleLoginButton) + + NSLayoutConstraint.activate([ + backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor), + backgroundImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + backgroundImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + backgroundImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + + logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + logoImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -240), + logoImageView.widthAnchor.constraint(equalToConstant: 119), + logoImageView.heightAnchor.constraint(equalToConstant: 74), + + titleLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 16), + titleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), + titleLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), + + subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), + subtitleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), + subtitleLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), + + appleLoginButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), + appleLoginButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), + appleLoginButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -40), + appleLoginButton.heightAnchor.constraint(equalToConstant: 50) + ]) + } + + private func setUpActions() { + appleLoginButton.onTap = { [weak self] in + // TODO: Apple 로그인 인증 로직 연결 필요 + self?.onAppleLoginTap?() + } + } + +} diff --git a/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/Contents.json b/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/Contents.json new file mode 100644 index 0000000..78f1488 --- /dev/null +++ b/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "loginBack.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/loginBack.png b/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/loginBack.png new file mode 100644 index 0000000..bde1df3 Binary files /dev/null and b/WhereAreYou/WhereAreYou/Resource/Assets.xcassets/LoginBackground.imageset/loginBack.png differ