안녕하세요 iOS 개발하는 루피입니다. 오늘은 MVC 패턴을 적용해 테이블 뷰를 구현해 보도록 하겠습니다.
사실 저는 아키텍처의 중요성에 대해 잘 느끼지 못했었는데요... 배워야 하는 지식인지는 알았지만, 도대체 뭐가 좋은지, 왜 써야 하는지에 대한 답은 찾지 못했습니다. 하지만 최근 프로젝트를 진행하며, 저만의 이유를 조금은 찾은 거 같습니다!!
팀원 중 한 명이 개인 사정으로 일정 기간 동안 프로젝트에 참여하지 못해서 남은 개발자들이 업무를 나누어서 맡아야 하는 상황이 발생했는데요, 이때 다른 팀원의 코드를 접근하기가 너무 편하더라고요!! 물론 해당 프로젝트는 MVVM 패턴을 사용하기는 했지만요... ㅎㅎ
암튼 오늘은 코드를 베이스로 MVC 패턴을 이용해 TableView를 공부해 보도록 하겠습니다.
목적
1. Model View Controller를 명확하게 나누어 구조가 한눈에 보이도록 만들 것이다.
2. Logic는 Controller에 만 작성할 것이다.
3. 스토리보드가 아닌 코드베이스로 구현을 할 것이다.
Model
저는 해외 축구를 보는 것을 좋아해 축구 선수의 정보를 바탕으로 모델링을 진행했습니다.
import Foundation
struct Player {
let name: String
let position: String
let goal : Int
let assist : Int
let nationality : String
let age : Date
let image : String
var formattedAge: String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: age)
}
}
extension Player {
static var SampleData = [
Player(name: "Lionel Messi", position: "RW", goal: 20, assist: 15, nationality: "Argentina", age: Date().addingTimeInterval(-31_536_000 * 36),image: "SJ.png"), // 36살
Player(name: "Cristiano Ronaldo", position: "ST", goal: 23, assist: 12, nationality: "Portugal", age: Date().addingTimeInterval(-31_536_000 * 38), image: "SJ.png"), // 38살
...
]
}
다음과 같이 SampleData를 이용하여 진행했습니다. SampleData에는 보이는 거와 같이 메시, 호날두라는 선수가 있습니다. SampleData가 더 존재하지만, 너무 길어지는 관계로 생략했습니다.
View
저는 뷰를 작성할 때 있어 로직을 최대한 지우고, 레이아웃만 존재하는 상태로 만들기 위해 노력했습니다.
import UIKit
class ListView: UIView {
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.backgroundColor = .red
tableView.rowHeight = 80
tableView.separatorStyle = .singleLine
return tableView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setViewHierarchy()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setViewHierarchy() {
[tableView].forEach {
self.addSubview($0)
}
}
private func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: topAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
}
import Foundation
import UIKit
class PlayerCellView: UITableViewCell {
lazy var playerImageView: UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFill
image.clipsToBounds = true
image.layer.masksToBounds = true
image.layer.borderColor = UIColor.red.cgColor
image.layer.borderWidth = 3
return image
}()
lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
label.textColor = .black
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setViewHierarchy()
setupConstraints()
}
override func layoutSubviews() {
super.layoutSubviews()
playerImageView.layer.cornerRadius = playerImageView.frame.width / 2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setViewHierarchy() {
[playerImageView, nameLabel].forEach {
contentView.addSubview($0)
}
}
private func setupConstraints() {
playerImageView.translatesAutoresizingMaskIntoConstraints = false
nameLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
playerImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
playerImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
playerImageView.widthAnchor.constraint(equalToConstant: 70),
playerImageView.heightAnchor.constraint(equalToConstant: 70),
nameLabel.centerYAnchor.constraint(equalTo: playerImageView.centerYAnchor),
nameLabel.leadingAnchor.constraint(equalTo: playerImageView.trailingAnchor, constant: 10),
nameLabel.trailingAnchor.constraint(lessThanOrEqualTo: contentView.trailingAnchor, constant: -10)
])
}
}
위와 같이 ListView와 PlayerCellView를 작성했습니다. 보이는 거와 같이 레이아웃을 설정하는 것 외에는 다른 코드를 작성하지 않은 것을 확인할 수 있습니다.
ViewController
VC는 Model 데이터를 받아와 View에 뿌려주는 역할을 맡게 하였습니다. 이렇게 될 경우 View와 관련된 로직을 수정하거나 Model에서 받아온 데이터를 조작할 때, VC 만 확인하면 되는 것이 너무 편하더라고요...ㅎㅎ. 물론 지금은 단순 하나의 TableView를 작성해서 프로젝트 크기가 거대하지는 않지만, 프로젝트가 너무 커지면 VC가 너무 거대해지는 문제점도 존재한다고 생각합니다. 하지만, 지금은 테이블 뷰를 공부하고 있기에 그런 고민들을 패스하도록 하겠습니다!
import UIKit
class ListViewController: UIViewController {
var myTableView = ListView()
var cellData = Player.SampleData
override func viewDidLoad() {
super.viewDidLoad()
self.view = myTableView
setupTableView()
}
private func setupTableView() {
myTableView.tableView.delegate = self
myTableView.tableView.dataSource = self
myTableView.tableView.register(PlayerCellView.self, forCellReuseIdentifier: "Cell")
}
}
extension ListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? PlayerCellView else { return UITableViewCell() }
let player = cellData[indexPath.row]
cell.nameLabel.text = player.name
cell.playerImageView.image = UIImage(named: player.image)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// InfoViewController로 화면 전환
let infoVC = InfoViewController()
infoVC.playerData = cellData[indexPath.row]
// 화면 전환 (navigationController 사용)
self.navigationController?.pushViewController(infoVC, animated: true)
}
}
저는 다음과 같이 VC를 작성했습니다. Cell 같은 경우 커스텀 Cell를 적용하기 위해서 기존 cell에서 다운캐스팅하는 방식을 이용해 적용하였습니다.
네 이렇게 간단한 TableView를 MVC패턴을 고려하여 구현해 보았습니다. 같은 MVC 패턴이어도 모두 다르게 생각하고, 자신만의 방식으로 접근한다고 생각하는데요 언제든지 더 좋은 방법이라던지 자신만의 방식이 있다면 피드백 주시면 감사하겠습니다!! 👀
'iOS > UIKit' 카테고리의 다른 글
| [UIKit] ViewController의 생명주기 (0) | 2025.01.11 |
|---|---|
| [UIKit] Preparing your UI to run in the foreground (0) | 2025.01.10 |
| [UIKit] 공식문서로 App의 LifeCycle 관리에 대해 알아 보자구요 (0) | 2025.01.10 |
| [UIKit] 코드베이스 세팅하는 방법 (0) | 2024.12.16 |
| [UIKit] UIKit이란? (0) | 2024.12.16 |