Log In, Registration UI 만들기

이세진·2022년 6월 24일
0

iOS

목록 보기
21/46
post-custom-banner

생성일: 2022년 1월 10일 오후 11:32

로그인, 회원가입 화면을 보면 동일한 모양의 textField와 Button, 배경색이 반복되는 것을 확인 할 수 있다.

이것들을 전부 하나하나 코드로 짜서 구현할 수 있겠지만 당연히 반복되는 코드를 작성하는 것은 비효율적이다

따라서 이것을 해결하기 위해 함수나 클래스를 생성하여 코드의 양을 축소하는 작업이 필요하다.

UI를 만들 때 코드를 간소화 하는 방법 (중복 제거)

  1. 새로운 클래스 만들기 (subclass)

    1. 상속을 활용하여 커스텀 클래스를 생성한다.
    // CustomTextField.swift
    import UIKit
    
    class CustomTextField: UITextField {
        
        init(placeholder: String) {
            super.init(frame: .zero)
            
            // 텍스트 필드 테두리와 place holder 사이에 간격 만들기
            let spacer = UIView()
            spacer.setDimensions(height: 50, width: 12)
            leftView = spacer
            leftViewMode = .always
            
            borderStyle = .none
            textColor = .white
            keyboardAppearance = .dark
            keyboardType = .emailAddress
            backgroundColor = UIColor(white: 1, alpha: 0.1)
            setHeight(50)
            attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [.foregroundColor: UIColor(white: 1, alpha: 0.7)])
            
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    • 반복되는 TextField를 하나의 subclass로 만들어 코드 중복을 방지한다.
  2. Extension 활용

    1. 동일한 배경색을 두 ViewController에서 사용하기 때문에 배경색을 지정하는 함수를 extension을 활용하여 추가한다.

      // extensions.swift
      extension UIViewController {
          // 배경색 그라데이션으로 만들기
          func configureGradientLayer() {
              let gradient = CAGradientLayer()
              gradient.colors = [UIColor.systemPurple.cgColor, UIColor.systemBlue.cgColor]
              gradient.locations = [0, 1]
              view.layer.addSublayer(gradient)
              gradient.frame = view.frame
          }
      }
    2. 동일한 모양의 Button들이 필요하기 때문에 해당 버튼의 모양을 설정하는 함수와 title을 설정하게 해주는 custom함수를 extension을 활용하여 추가한다.

      extension UIButton {
          func attributedTitle(firstPart: String, secondPart: String) {
              let atts: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor(white: 1, alpha: 0.87), .font: UIFont.systemFont(ofSize: 16)]
              let attributedTitle = NSMutableAttributedString(string: "\(firstPart) ", attributes: atts)
              
              let boldAtts: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor(white: 1, alpha: 0.87), .font: UIFont.boldSystemFont(ofSize: 16)]
              attributedTitle.append(NSAttributedString(string: secondPart, attributes: boldAtts))
              
              setAttributedTitle(attributedTitle, for: .normal)
          }
          
          func customTitle(title: String) {
              setTitle(title, for: .normal)
              setTitleColor(.white, for: .normal)
              backgroundColor = #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)
              layer.cornerRadius = 5
              setHeight(50)
              titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
          }
      }

로직

  • 로그인과 회원가입 화면에서 email, password 등의 필수 정보를 입력하지 않았을 때 로그인/회원가입 버튼을 비활성화하기
  • 이를 위해 ViewModel 개념을 활용함
  1. LoginController.swift와 RegistrationController.swift에 각 텍스트 필드를 실시간으로 트래킹하는 함수 추가, UI(버튼)을 실시간으로 업데이트 하는 함수 추가 (프로토콜 활용)

    func configureNotificationObservers() {
    			# 이 함수를 viewDidLoad에서 실행시킨다.
          emailTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
          passwordTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
      }
    
    @objc func textDidChange(sender: UITextField) {
          if sender == emailTextField {
              viewModel.email = sender.text
          } else {
              viewModel.password = sender.text
          }
          
          updateForm()
      }
    
    //MARK: - FormVeiwModel Protocol
    extension LoginController: FormViewModel {
        func updateForm() {
            loginButton.backgroundColor = viewModel.buttonBackgroundColor
            loginButton.setTitleColor(viewModel.buttonTitleColor, for: .normal)
            loginButton.isEnabled = viewModel.formIsValid
        }
    }
  2. ViewModel 폴더에 AuthenticationViewModel.swift 생성

    import UIKit
    
    //MARK: - Protocols
    protocol FormViewModel {
        func updateForm()
    }
    
    protocol AuthenticationViewModel {
        var formIsValid: Bool { get }
        var buttonBackgroundColor: UIColor { get }
        var buttonTitleColor: UIColor { get }
    }
    
    //MARK: - ViewModels
    struct LoginViewModel: AuthenticationViewModel {
        var email: String?
        var password: String?
        
        var formIsValid: Bool {
            return email?.isEmpty == false && password?.isEmpty == false
        }
        
        var buttonBackgroundColor: UIColor {
            return formIsValid ? #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1) : #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1).withAlphaComponent(0.5)
        }
        
        var buttonTitleColor: UIColor {
            return formIsValid ? .white : UIColor(white: 1, alpha: 0.67)
        }
    }
    
    struct RegistrationViewModel: AuthenticationViewModel {
        var email: String?
        var password: String?
        var fullname: String?
        var username: String?
        
        var formIsValid: Bool {
            return email?.isEmpty == false && password?.isEmpty == false && fullname?.isEmpty == false && username?.isEmpty == false
        }
        
        var buttonBackgroundColor: UIColor {
            return formIsValid ? #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1) : #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1).withAlphaComponent(0.5)
        }
        
        var buttonTitleColor: UIColor {
            return formIsValid ? .white : UIColor(white: 1, alpha: 0.67)
    
        }
    }
    • computed property와 protocol을 활용하여 ViewModel 구조체 작성
    • ViewModel을 활용하여 View를 실시간으로 수정하는 로직을 구성했다.

프로필 이미지 선택 기능

  • 회원가입 화면에서 프로필 이미지를 선택하는 기능을 추가한다.
    //MARK: - UIImagePickerControllerDelegate, UINavigationControllerDelegate
    extension RegistrationController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        
        // 프로필 이미지 선택 후 작동
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            
            guard let selectedImage = info[.editedImage] as? UIImage else { return }
            
            plusPhotoButton.layer.cornerRadius = plusPhotoButton.frame.width / 2
            plusPhotoButton.layer.masksToBounds = true
            plusPhotoButton.layer.borderColor = UIColor.white.cgColor
            plusPhotoButton.layer.borderWidth = 1
            plusPhotoButton.setImage(selectedImage.withRenderingMode(.alwaysOriginal), for: .normal)
            
            self.dismiss(animated: true, completion: nil)
            
        }
    }
    UIImagePickerControllerDelegate 프로토콜을 사용하여 이미지를 선택한 후 작동하는 함수를 불러온다. 이 함수에서 이미지를 선택하는 버튼이 이미지 선택후 해당 이미지로 변하도록 설정한다.
  • 이미지를 추가하는 버튼에 addTarget을 통해 이벤트를 감지하게 한 후 다음과 같이 selector를 구현한다.
    @objc func handleProfilePhotoSelect() {
            let picker = UIImagePickerController()
            picker.delegate = self
            picker.allowsEditing = true
            
            present(picker, animated: true, completion: nil)
        }

실행 결과

profile
나중은 결코 오지 않는다.
post-custom-banner

0개의 댓글