🟨 구현 화면
🟨 ProfileDataFormViewController.swift
import UIKit
import PhotosUI
class ProfileDataFormViewController: UIViewController {
private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.alwaysBounceVertical = true
scrollView.keyboardDismissMode = .onDrag
return scrollView
}()
private let displayNameTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.keyboardType = .default
textField.backgroundColor = .secondarySystemFill
textField.leftViewMode = .always
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
textField.layer.masksToBounds = true
textField.layer.cornerRadius = 8
textField.attributedPlaceholder = NSAttributedString(string: "Display Name", attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray])
return textField
}()
private let usernameTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.keyboardType = .default
textField.backgroundColor = .secondarySystemFill
textField.leftViewMode = .always
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
textField.layer.masksToBounds = true
textField.layer.cornerRadius = 8
textField.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray])
return textField
}()
private let hintLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Fill in you data"
label.font = .systemFont(ofSize: 32, weight: .bold)
label.textColor = .label
return label
}()
private let avatarPlaceholderImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.clipsToBounds = true
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 60
imageView.backgroundColor = .lightGray
imageView.image = UIImage(systemName: "camera.fill")
imageView.tintColor = .gray
imageView.isUserInteractionEnabled = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
private let bioTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = .secondarySystemFill
textView.layer.masksToBounds = true
textView.layer.cornerRadius = 8
textView.textContainerInset = .init(top: 15, left: 15, bottom: 15, right: 15)
textView.text = "Tell the world about yourself"
textView.textColor = .gray
textView.font = .systemFont(ofSize: 16)
return textView
}()
private let submitButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Submit", for: .normal)
button.tintColor = .label
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
button.layer.masksToBounds = true
button.layer.cornerRadius = 25
button.backgroundColor = .systemMint
button.isEnabled = false
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.addSubview(scrollView)
scrollView.addSubview(hintLabel)
scrollView.addSubview(avatarPlaceholderImageView)
scrollView.addSubview(displayNameTextField)
scrollView.addSubview(usernameTextField)
scrollView.addSubview(bioTextView)
scrollView.addSubview(submitButton)
isModalInPresentation = true
displayNameTextField.delegate = self
usernameTextField.delegate = self
bioTextView.delegate = self
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapToDismiss)))
configureConstraints()
avatarPlaceholderImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapToUpload)))
}
@objc private func didTapToUpload() {
var configuration = PHPickerConfiguration()
configuration.filter = .images
configuration.selectionLimit = 1
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true)
}
@objc private func didTapToDismiss() {
view.endEditing(true)
}
private func configureConstraints() {
let scrollViewConstraints = [
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]
let hintLabelConstraints = [
hintLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
hintLabel.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 30)
]
let avatarPlaceholderImageViewConstraints = [
avatarPlaceholderImageView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
avatarPlaceholderImageView.heightAnchor.constraint(equalToConstant: 120),
avatarPlaceholderImageView.widthAnchor.constraint(equalToConstant: 120),
avatarPlaceholderImageView.topAnchor.constraint(equalTo: hintLabel.bottomAnchor, constant: 30)
]
let displayNameTextFieldConstraints = [
displayNameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
displayNameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
displayNameTextField.topAnchor.constraint(equalTo: avatarPlaceholderImageView.bottomAnchor, constant: 40),
displayNameTextField.heightAnchor.constraint(equalToConstant: 50)
]
let usernameTextFieldConstraints = [
usernameTextField.leadingAnchor.constraint(equalTo: displayNameTextField.leadingAnchor),
usernameTextField.trailingAnchor.constraint(equalTo: displayNameTextField.trailingAnchor),
usernameTextField.topAnchor.constraint(equalTo: displayNameTextField.bottomAnchor, constant: 20),
usernameTextField.heightAnchor.constraint(equalToConstant: 50)
]
let bioTextViewConstraints = [
bioTextView.leadingAnchor.constraint(equalTo: displayNameTextField.leadingAnchor),
bioTextView.trailingAnchor.constraint(equalTo: displayNameTextField.trailingAnchor),
bioTextView.topAnchor.constraint(equalTo: usernameTextField.bottomAnchor, constant: 20),
bioTextView.heightAnchor.constraint(equalToConstant: 150)
]
let submitButtonConstraints = [
submitButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
submitButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
submitButton.heightAnchor.constraint(equalToConstant: 50),
submitButton.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -20)
]
NSLayoutConstraint.activate(scrollViewConstraints)
NSLayoutConstraint.activate(hintLabelConstraints)
NSLayoutConstraint.activate(avatarPlaceholderImageViewConstraints)
NSLayoutConstraint.activate(displayNameTextFieldConstraints)
NSLayoutConstraint.activate(usernameTextFieldConstraints)
NSLayoutConstraint.activate(bioTextViewConstraints)
NSLayoutConstraint.activate(submitButtonConstraints)
}
}
extension ProfileDataFormViewController: UITextViewDelegate, UITextFieldDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
scrollView.setContentOffset(CGPoint(x: 0, y: textView.frame.origin.y - 100), animated: true)
if textView.textColor == .gray {
textView.textColor = .label
textView.text = ""
}
}
func textViewDidEndEditing(_ textView: UITextView) {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
if textView.text.isEmpty {
textView.text = "Tell the world about yourself"
textView.textColor = .gray
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x: 0, y: textField.frame.origin.y - 100), animated: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
extension ProfileDataFormViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
for result in results {
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] object, error in
if let image = object as? UIImage {
DispatchQueue.main.async {
self?.avatarPlaceholderImageView.image = image
}
}
}
}
}
}
🟨 ProfileDataFormViewViewModel.swift
import Foundation
import Combine
import UIKit
import FirebaseAuth
import FirebaseStorage
final class ProfileDataFormViewViewModel: ObservableObject {
private var subscriptions: Set<AnyCancellable> = []
@Published var displayName: String?
@Published var username: String?
@Published var bio: String?
@Published var avatarPath: String?
@Published var imageData: UIImage?
@Published var isFormValid: Bool = false
@Published var error: String = ""
@Published var isOnboardingFinished: Bool = false
func validateUserProfileForm() {
guard let displayName = displayName,
displayName.count > 2,
let username = username,
username.count > 2,
let bio = bio,
bio.count > 2,
imageData != nil else {
isFormValid = false
return
}
isFormValid = true
}
}
🟨 TIL
- 키보드를 누르면 텍스트 필드가 화면에 잘 보이게 하기
- 텍스트 필드에 값을 누르면 y축으로 올라가고
- 그만 치면 y축이 원래 위치로 내려온다.
extension ProfileDataFormViewController: UITextViewDelegate, UITextFieldDelegate {
...
func textFieldDidBeginEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x: 0, y: textField.frame.origin.y - 100), animated: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
'Clone App > Twitter' 카테고리의 다른 글
[Twitter Clone] Load Profile Data to profileView (0) | 2024.06.12 |
---|---|
[Twitter Clone] Connect to Firebase Storage (0) | 2024.06.12 |
[Twitter Clone] Set user Info into firebase storage (1) | 2024.06.09 |
[Twitter Clone] Add signOut, Login View, Error View (0) | 2024.06.06 |
[Twitter Clone] Add ViewModel and bind view (0) | 2024.06.05 |