Account Summary Cell
Adding the table view cell
테이블뷰 셀도 NIB을 이용해서 만들 수 있지만 이번에는 프로그래밍을 이용해서 셀을 만들어보자.
기본 세팅
-
AccountSummaryCell.swift
파일을 만들고, UITableViewCell을 상속받는AccountSummaryCell
클래스를 만든다. -
클래스 안에 다음과 같이 코드를 작성한다.
class AccountSummaryCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setup() layout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension AccountSummaryCell { private func setup() { } private func layout() { } }
Doing the layout
typeLabel 만들기
-
let typeLabel = UILabel()
-
cell 설정을 위한 관련 상수들을 초기화한다.
static let reuseID = "AccountSummaryCell"
static let rowHeight: CGFloat = 100
-
setup() 메소드에 아래와 같이 코드를 작성한다.
typeLabel.translatesAutoresizingMaskIntoConstraints = false
typeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
typeLabel.adjustsFontForContentSizeCategory = true
typeLabel.text = "Account type"
- adjustsFontForContentSizeCategory : 유저가 디바이스의 폰트 사이즈를 바꾸면 자동으로 label의 폰트 사이즈도 변경되게 해준다.
-
typeLabel을 상위 view에 추가한다.
- 여기서는 그냥
addSubview(typeLabel)
로 작성하면 안 된다.
- 테이블 뷰에서는 컴포넌트들을 배치하고 간격을 설정하기 위해
contentView
에 추가해주어야 한다.
- contentView는 셀 오브젝트의 view이다.
-
오토레이아웃을 설정한다.
- layout() 메소드 내에 작성
- topAnchor, leadingAnchor의 constraint 값 설정
테이블 뷰에 AccoutSummaryCell 등록 및 각종 설정하기
- cell 등록
AccountSummaryViewController
의 setupTableView()
메소드에 다음과 같은 코드를 추가한다.tableView.register(AccountSummaryCell.self, forCellReuseIdentifier: AccountSummaryCell.reuseID)
- 셀 높이 및 footer 설정하기
tableView.rowHeight = AccountSummaryCell.rowHeight
tableView.tableFooterView = UIView()
AccountSummaryCell dequeue하기
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AccountSummaryCell.reuseID, for: indexPath) as! AccountSummaryCell
return cell
}
let typeLabel = UILabel()
cell 설정을 위한 관련 상수들을 초기화한다.
static let reuseID = "AccountSummaryCell"
static let rowHeight: CGFloat = 100
setup() 메소드에 아래와 같이 코드를 작성한다.
typeLabel.translatesAutoresizingMaskIntoConstraints = false
typeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
typeLabel.adjustsFontForContentSizeCategory = true
typeLabel.text = "Account type"
- adjustsFontForContentSizeCategory : 유저가 디바이스의 폰트 사이즈를 바꾸면 자동으로 label의 폰트 사이즈도 변경되게 해준다.
typeLabel을 상위 view에 추가한다.
- 여기서는 그냥
addSubview(typeLabel)
로 작성하면 안 된다.- 테이블 뷰에서는 컴포넌트들을 배치하고 간격을 설정하기 위해
contentView
에 추가해주어야 한다. - contentView는 셀 오브젝트의 view이다.
- 테이블 뷰에서는 컴포넌트들을 배치하고 간격을 설정하기 위해
오토레이아웃을 설정한다.
- layout() 메소드 내에 작성
- topAnchor, leadingAnchor의 constraint 값 설정
AccountSummaryViewController
의 setupTableView()
메소드에 다음과 같은 코드를 추가한다.tableView.register(AccountSummaryCell.self, forCellReuseIdentifier: AccountSummaryCell.reuseID)
tableView.rowHeight = AccountSummaryCell.rowHeight
tableView.tableFooterView = UIView()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AccountSummaryCell.reuseID, for: indexPath) as! AccountSummaryCell
return cell
}
AccountSummaryCell
이 내 프로젝트 안에 있다는 게 확실하기 때문에 강제 언래핑을 해준다.
divider 만들기
UIView를 이용해서 간단하게 divider를 만들 수 있다.
- underlineView 초기화 후 setup
- 오토레이아웃 설정
UIStackView 코드로 구현하기
-
필요한 컴포넌트들 세팅
let balanceStackView = UIStackView() let balanceLabel = UILabel() let balanceAmountLabel = UILabel()
balanceStackView.translatesAutoresizingMaskIntoConstraints = false balanceStackView.axis = .vertical balanceStackView.spacing = 0 balanceLabel.translatesAutoresizingMaskIntoConstraints = false balanceLabel.font = UIFont.preferredFont(forTextStyle: .body) balanceLabel.textAlignment = .right balanceLabel.text = "Some balance" balanceAmountLabel.translatesAutoresizingMaskIntoConstraints = false balanceAmountLabel.textAlignment = .right balanceAmountLabel.text = "$929,466.63" balanceStackView.addArrangedSubview(balanceLabel) balanceStackView.addArrangedSubview(balanceAmountLabel) contentView.addSubview(balanceStackView)
-
오토레이아웃 설정
오른쪽 화살표 이미지 추가하기
-
let chevronImageView = UIImageView()
-
초기 설정
chevronImageView.translatesAutoresizingMaskIntoConstraints = false let chevronImage = UIImage(systemName: "chevron.right")!.withTintColor(appColor, renderingMode: .alwaysOriginal) // SF Symbol의 색상 설정 방법 chevronImageView.image = chevronImage contentView.addSubview(chevronImageView)
-
오토레이아웃 설정
How to make things pretty with NSAttributedString
NSAttributedString은 iOS에서의 특별한 String이다. 문자열이 어떻게 보여질지에 관한 프로퍼티를 가지고 있다.
사용 가능한 예시
- Paragraph
- Bolding
- 특정 부분만 굵게 보이도록 하기
- Image
- 문자열과 이미지를 함께 배치할 수 있다.
- Kerning
- 버튼 안에 있는 텍스트에 간격 주기
- BaselineOffset
NSAttributedString 설정하기
AccountSummaryCell
에 아래와 같은 함수를 추가한다.
private func makeFormattedBalance(dollars: String, cents: String) -> NSMutableAttributedString {
let dollarSignAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.preferredFont(forTextStyle: .callout), .baselineOffset: 8]
let dollarAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.preferredFont(forTextStyle: .title1)]
let centAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.preferredFont(forTextStyle: .footnote), .baselineOffset: 8]
let rootString = NSMutableAttributedString(string: "$", attributes: dollarSignAttributes)
let dollarString = NSAttributedString(string: dollars, attributes: dollarAttributes)
let centString = NSAttributedString(string: cents, attributes: centAttributes)
rootString.append(dollarString)
rootString.append(centString)
return rootString
}
- NSAttributedString.Key : attributed string 내의 텍스트에 적용하고자 하는 attribute
- cell에 있는 balance(ex. $929,486.63)를 dollars(929,486)와 cents(63)으로 나눈다.
- 다른 스타일을 적용하기 위함
- dollarSignAttributes : 8 포인트 위에 $ 표시를 배치한다.
- dollarAttributes : 폰트 크기 설정
- centAttributes : 폰트 크기 설정, 8 포인트 위에 배치
UILabel에 NSAttributedString 적용하기
balanceAmountLabel.attributedText = makeFormattedBalance(dollars: "929,466", cents: "23")
layout() 메소드 내에 위와 같은 코드를 추가한다.
→ UILabel의 텍스트를 text
프로퍼티에 입력하지 않고 attributedText
라는 특별한 프로퍼티에 입력해주었다.
How to save your work incrementally
git add -p
위 명령어를 사용하면 전체 파일을 한 번에 add 하지 않고 변경된 부분을 확인하면서 원하는 부분만 add 할 수 있다.
Meet the View Model
ViewModel을 사용하면 각기 다른 조건에서 cell을 재사용할 수 있다.
ViewModel 사용하는 법
ViewModel을 사용하기 위해서 먼저 ViewModel에서 표현하기 위해 어떤 데이터를 가져와야 하는지를 알아야 한다.
AccountSummaryCell에서는 세 가지의 데이터가 필요하다.
- accountType
- accountName
- balance
ViewModel에서 위 세 가지 데이터를 cell로 전달하고, 뷰 컨트롤러에서 account들을 ViewModel의 배열로 표현할 것이다.
ViewModel의 장점
- Clean seperation UI/Data
- Makes setup easy
- Enables reuse and configuration
- Helps with unit testing
ViewModel을 구현해보자!
- AccountSummaryCell에
AccountType
이라는 열거형을 선언해준다. - ViewModel을 다음과 같이 정의해준다.
- ViewModel의 데이터를 전달받아 스타일링을 하는 configure 메소드를 작성한다.
- AccountSummaryViewController에서 account들을 저장할
accounts
배열을 선언한다. fetchData()
메소드에서 데이터를 불러와accounts
에 저장한다.- 실제 프로젝트에서는 HTTP 통신을 이용해 서버에서 데이터를 불러올 것이다. 하지만 여기서는 우리가 선언한 ViewModel을 이용해 데이터를 추가한다.
How to leverage enums
-
AccountSummaryCell에 AccountType
이라는 열거형을 선언해준다.
enum AccountType: String {
case Banking
case CreditCard
case Investment
}
-
ViewModel을 다음과 같이 정의해준다.
struct ViewModel {
let accountType: AccountType
let accountName: String
}
ViewModel
안에는 필요한 데이터들이 모두 정의가 되어 있다.
AccountType
과 ViewModel
모두 외부 파일에 따로 정의할 수도 있지만 AccountSummaryCell 안에서 정의함으로써 이 코드의 독자가 account와 관련이 있는 데이터들이라는 것을 강조했다.
-
viewModel
변수를 선언한다.
let viewModel: ViewModel? = nil
-
configure 메소드를 추가한다.
extension AccountSummaryCell {
func configure(with vm: ViewModel) {
typeLabel.text = vm.accountType.rawValue
typeLabel.text = vm.accountName
switch vm.accountType {
case .Banking:
underlineView.backgroundColor = appColor
balanceLabel.text = "Current Balance"
case .CreditCard:
underlineView.backgroundColor = .systemOrange
balanceLabel.text = "Balance"
case .Investment:
underlineView.backgroundColor = .systemPurple
balanceLabel.text = "Value"
}
}
}
fetch data
-
AccountSummaryViewController에서 account들을 저장할 accounts
배열을 선언한다.
var accounts: [AccountSummaryCell.ViewModel] = []
-
fetchData() 메소드에서 필요한 ViewModel 데이터들을 만들고 accounts
배열에 삽입한다.
-
tableView의 numberOfRowsInSection
메소드를 수정한다.
-
tableView의 cellForRowAt
메소드를 수정한다.
guard !accounts.isEmpty else { return UITableViewCell() }
let cell = tableView.dequeueReusableCell(withIdentifier: AccountSummaryCell.reuseID, for: indexPath) as! AccountSummaryCell
let account = accounts[indexPath.row]
cell.configure(with: account)
indexPath에 해당하는 account 데이터에 맞게 셀을 스타일링하는 코드가 추가되었다.
Dealing with the decimal
AccountSummaryCell에 AccountType
이라는 열거형을 선언해준다.
enum AccountType: String {
case Banking
case CreditCard
case Investment
}
ViewModel을 다음과 같이 정의해준다.
struct ViewModel {
let accountType: AccountType
let accountName: String
}
ViewModel
안에는 필요한 데이터들이 모두 정의가 되어 있다.
AccountType
과 ViewModel
모두 외부 파일에 따로 정의할 수도 있지만 AccountSummaryCell 안에서 정의함으로써 이 코드의 독자가 account와 관련이 있는 데이터들이라는 것을 강조했다.
viewModel
변수를 선언한다.
let viewModel: ViewModel? = nil
configure 메소드를 추가한다.
extension AccountSummaryCell {
func configure(with vm: ViewModel) {
typeLabel.text = vm.accountType.rawValue
typeLabel.text = vm.accountName
switch vm.accountType {
case .Banking:
underlineView.backgroundColor = appColor
balanceLabel.text = "Current Balance"
case .CreditCard:
underlineView.backgroundColor = .systemOrange
balanceLabel.text = "Balance"
case .Investment:
underlineView.backgroundColor = .systemPurple
balanceLabel.text = "Value"
}
}
}
AccountSummaryViewController에서 account들을 저장할 accounts
배열을 선언한다.
var accounts: [AccountSummaryCell.ViewModel] = []
fetchData() 메소드에서 필요한 ViewModel 데이터들을 만들고 accounts
배열에 삽입한다.
tableView의 numberOfRowsInSection
메소드를 수정한다.
tableView의 cellForRowAt
메소드를 수정한다.
guard !accounts.isEmpty else { return UITableViewCell() }
let cell = tableView.dequeueReusableCell(withIdentifier: AccountSummaryCell.reuseID, for: indexPath) as! AccountSummaryCell
let account = accounts[indexPath.row]
cell.configure(with: account)
indexPath에 해당하는 account 데이터에 맞게 셀을 스타일링하는 코드가 추가되었다.
위에서 우리는 각 데이터에 맞게 AccountType과 AccountName을 설정해주었다. 하지만 아직 balance 값은 각 데이터에 따라서 변경이 되지 않는다.
Decimal를 Double로 바꿔서 dollars
와 cents
로 분리해서 balanceAmountLabel
에 적용해보자.
(참고: 언제 Decimal을 써야 할까)
Adding the decimal
ViewModel
에 Decimal 타입의balance
변수를 추가한다.- fetchData()의 내용을 수정한다.
- Decimal을 Double로 변환한다.
-
이를 위해 Utils 디렉토리에
DecimalUtils.swift
파일을 생성한다. -
아래와 같이 코드를 추가한다.
extension Decimal { var doubleValue: Double { return NSDecimalNumber(decimal: self).doubleValue } }
-
Utils 디렉토리에
CurrencyFormatter.swift
파일을 추가한다. -
ViewModel
에 아래와 같은 코드를 추가한다.var balanceAsAttrubutedString: NSAttributedString { return CurrencyFormatter().makeAttributedCurrency(balance) }
-
코드를 작성하고 실행을 했더니 balance가 이상하게 표시가 되었다.
S$는 대체 어디서 튀어나온 걸까.. 하고 한참을 삽질해보았더니 알고보니 “US$XXX.XXX”와 같은 형식으로 설정이 되어 있는 것이었다.
공식 문서를 살펴보니 currency가 locale에 따라 다른 형식으로 출력이 되길래 formatter의 Locale을 en_US
로 설정해주었더니 US 문구가 없어지고 잘 출력이 되었다.
선생님의 팁...😇
Ignore the geniuses.
iOS 개발은 어렵다. 빠르게 iOS 개발을 익힌 사람들과 비교하지 말자. 꾸준히 공부하고 연습하면 결국 나도 그 천재들처럼 보일 것이다. 겁 먹지 말자!!!!
Author And Source
이 문제에 관하여(Account Summary Cell), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@240-coding/Account-Summary-Cell저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)