Build Instagram App: Part 7 (Swift 5) - 2020 - Xcode 11 - iOS Development
private func configureModels() {
let section1Labels = ["Name", "UserName", "Bio"]
var section1 = [EditProfileFormModel]()
for label in section1Labels {
let model = EditProfileFormModel(label: label, placeholder: "Enter \(label)...", value: nil)
section1.append(model)
}
let section2Labels = ["Email", "UserName", "Bio"]
var section2 = [EditProfileFormModel]()
for label in section2Labels {
let model = EditProfileFormModel(label: label, placeholder: "Enter \(label)...", value: nil)
section2.append(model)
}
editProfileModel.send([section1, section2])
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: FormTableViewCell.identifier, for: indexPath) as? FormTableViewCell else { return UITableViewCell() }
let model = viewModel.editProfileModel.value[indexPath.section][indexPath.row]
cell.configure(with: model)
return cell
}
func configure(with model: EditProfileFormModel) {
formLabel.text = model.label
textField.placeholder = model.placeholder
textField.text = model.value
}
import Foundation
struct EditProfileFormModel {
let label: String
let placeholder: String
var value: String?
}
mport Foundation
import Combine
class EditProfileViewModel {
let editProfileModel: CurrentValueSubject<[[EditProfileFormModel]], Never> = .init([])
init() {
configureModels()
}
private func configureModels() {
let section1Labels = ["Name", "UserName", "Bio"]
var section1 = [EditProfileFormModel]()
for label in section1Labels {
let model = EditProfileFormModel(label: label, placeholder: "Enter \(label)...", value: nil)
section1.append(model)
}
let section2Labels = ["Email", "UserName", "Bio"]
var section2 = [EditProfileFormModel]()
for label in section2Labels {
let model = EditProfileFormModel(label: label, placeholder: "Enter \(label)...", value: nil)
section2.append(model)
}
editProfileModel.send([section1, section2])
}
}
mport UIKit
import Combine
class EditProfileViewController: UIViewController {
private let tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(FormTableViewCell.self, forCellReuseIdentifier: FormTableViewCell.identifier)
return tableView
}()
private var cancellables = Set<AnyCancellable>()
private let viewModel = EditProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
setUI()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
private func setUI() {
view.backgroundColor = .systemBackground
view.addSubview(tableView)
tableView.tableHeaderView = createTableHeaderView()
tableView.delegate = self
tableView.dataSource = self
setNavigationBar()
}
private func createTableHeaderView() -> UIView {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: view.width, height: view.height / 3).integral)
let size = headerView.height / 1.5
let profilePhotoButton = UIButton(frame: CGRect(x: (view.width-size) / 2, y: (headerView.height-size) / 2, width: size, height: size))
headerView.addSubview(profilePhotoButton)
profilePhotoButton.layer.masksToBounds = true
profilePhotoButton.layer.cornerRadius = size / 2.0
profilePhotoButton
.tapPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.profilePhotoButtonDidTap()
}
.store(in: &cancellables)
profilePhotoButton.setBackgroundImage(UIImage(systemName: "person.circle")?.withTintColor(.label, renderingMode: .alwaysOriginal), for: .normal)
profilePhotoButton.layer.borderWidth = 1.0
profilePhotoButton.layer.borderColor = UIColor.secondarySystemBackground.cgColor
return headerView
}
private func setNavigationBar() {
title = "Edit Profile"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(saveButtonDidTap))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelButtonDidTap))
}
private func profilePhotoButtonDidTap() {
}
@objc private func saveButtonDidTap() {
// save data into db
dismiss(animated: true)
}
@objc private func cancelButtonDidTap() {
dismiss(animated: true)
}
private func changeProfilePicture() {
let actionSheet = UIAlertController(title: "Profile Picture", message: "Change profile picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: { _ in
}))
actionSheet.addAction(UIAlertAction(title: "Choose from Library", style: .default, handler: { _ in
}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
actionSheet.popoverPresentationController?.sourceView = view
actionSheet.popoverPresentationController?.sourceRect = view.bounds
present(actionSheet, animated: true)
}
}
extension EditProfileViewController: UITableViewDelegate {
}
extension EditProfileViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard section == 1 else { return nil }
return "Private Information"
}
func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.editProfileModel.value.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: FormTableViewCell.identifier, for: indexPath) as? FormTableViewCell else { return UITableViewCell() }
let model = viewModel.editProfileModel.value[indexPath.section][indexPath.row]
cell.configure(with: model)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.editProfileModel.value[section].count
}
}
configure
커스텀 함수 사용import UIKit
import Combine
class FormTableViewCell: UITableViewCell {
static let identifier = "FormTableViewCell"
private let formLabel: UILabel = {
let label = UILabel()
label.textColor = .label
label.numberOfLines = 1
return label
}()
private let textField: UITextField = {
let textField = UITextField()
textField.returnKeyType = .done
return textField
}()
private var cancellables = Set<AnyCancellable>()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
formLabel.frame = CGRect(x: 5, y: 0, width: contentView.width / 2, height: contentView.height)
textField.frame = CGRect(x: formLabel.right + 5, y: 0, width: contentView.width - 10 - formLabel.width, height: contentView.height)
}
override func prepareForReuse() {
super.prepareForReuse()
formLabel.text = nil
textField.placeholder = nil
textField.text = nil
}
private func setUI() {
contentView.addSubview(formLabel)
contentView.addSubview(textField)
clipsToBounds = true
selectionStyle = .none
textField
.controlPublisher(for: .editingDidEndOnExit)
.sink { [weak self] _ in
self?.textField.resignFirstResponder()
}
.store(in: &cancellables)
}
func configure(with model: EditProfileFormModel) {
formLabel.text = model.label
textField.placeholder = model.placeholder
textField.text = model.value
}
}