생성일: 2022년 1월 10일 오후 11:32
로그인, 회원가입 화면을 보면 동일한 모양의 textField와 Button, 배경색이 반복되는 것을 확인 할 수 있다.
이것들을 전부 하나하나 코드로 짜서 구현할 수 있겠지만 당연히 반복되는 코드를 작성하는 것은 비효율적이다
따라서 이것을 해결하기 위해 함수나 클래스를 생성하여 코드의 양을 축소하는 작업이 필요하다.
새로운 클래스 만들기 (subclass)
// 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")
}
}
Extension 활용
동일한 배경색을 두 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
}
}
동일한 모양의 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)
}
}
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
}
}
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)
}
}
//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 프로토콜을 사용하여 이미지를 선택한 후 작동하는 함수를 불러온다. 이 함수에서 이미지를 선택하는 버튼이 이미지 선택후 해당 이미지로 변하도록 설정한다.@objc func handleProfilePhotoSelect() {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}