Account Summary Cell

30823 단어 iOSiOS

Adding the table view cell

테이블뷰 셀도 NIB을 이용해서 만들 수 있지만 이번에는 프로그래밍을 이용해서 셀을 만들어보자.

기본 세팅

  1. AccountSummaryCell.swift 파일을 만들고, UITableViewCell을 상속받는 AccountSummaryCell 클래스를 만든다.

  2. 클래스 안에 다음과 같이 코드를 작성한다.

    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 만들기

  1. let typeLabel = UILabel()

  2. cell 설정을 위한 관련 상수들을 초기화한다.

    • static let reuseID = "AccountSummaryCell"
    • static let rowHeight: CGFloat = 100
  3. setup() 메소드에 아래와 같이 코드를 작성한다.

    typeLabel.translatesAutoresizingMaskIntoConstraints = false
    typeLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
    typeLabel.adjustsFontForContentSizeCategory = true
    typeLabel.text = "Account type"
    • adjustsFontForContentSizeCategory : 유저가 디바이스의 폰트 사이즈를 바꾸면 자동으로 label의 폰트 사이즈도 변경되게 해준다.
  4. typeLabel을 상위 view에 추가한다.

    • 여기서는 그냥 addSubview(typeLabel) 로 작성하면 안 된다.
      • 테이블 뷰에서는 컴포넌트들을 배치하고 간격을 설정하기 위해 contentView 에 추가해주어야 한다.
      • contentView는 셀 오브젝트의 view이다.
  5. 오토레이아웃을 설정한다.

    • layout() 메소드 내에 작성
    • topAnchor, leadingAnchor의 constraint 값 설정

테이블 뷰에 AccoutSummaryCell 등록 및 각종 설정하기

  • cell 등록 AccountSummaryViewControllersetupTableView() 메소드에 다음과 같은 코드를 추가한다.
    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
}

AccountSummaryCell 이 내 프로젝트 안에 있다는 게 확실하기 때문에 강제 언래핑을 해준다.

divider 만들기

UIView를 이용해서 간단하게 divider를 만들 수 있다.

  1. underlineView 초기화 후 setup
  2. 오토레이아웃 설정

UIStackView 코드로 구현하기

  1. 필요한 컴포넌트들 세팅

    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)
  2. 오토레이아웃 설정

오른쪽 화살표 이미지 추가하기

  1. let chevronImageView = UIImageView()

  2. 초기 설정

    chevronImageView.translatesAutoresizingMaskIntoConstraints = false
    let chevronImage = UIImage(systemName: "chevron.right")!.withTintColor(appColor, renderingMode: .alwaysOriginal) // SF Symbol의 색상 설정 방법
    chevronImageView.image = chevronImage
    
    contentView.addSubview(chevronImageView)
  3. 오토레이아웃 설정

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을 구현해보자!

  1. AccountSummaryCell에 AccountType 이라는 열거형을 선언해준다.
  2. ViewModel을 다음과 같이 정의해준다.
  3. ViewModel의 데이터를 전달받아 스타일링을 하는 configure 메소드를 작성한다.
  4. AccountSummaryViewController에서 account들을 저장할 accounts 배열을 선언한다.
  5. fetchData() 메소드에서 데이터를 불러와 accounts 에 저장한다.
    • 실제 프로젝트에서는 HTTP 통신을 이용해 서버에서 데이터를 불러올 것이다. 하지만 여기서는 우리가 선언한 ViewModel을 이용해 데이터를 추가한다.

How to leverage enums

  1. AccountSummaryCell에 AccountType 이라는 열거형을 선언해준다.

    	enum AccountType: String {
            case Banking
            case CreditCard
            case Investment
        }
  2. ViewModel을 다음과 같이 정의해준다.

    		struct ViewModel {
            let accountType: AccountType
            let accountName: String
        }

    ViewModel 안에는 필요한 데이터들이 모두 정의가 되어 있다.

    AccountTypeViewModel 모두 외부 파일에 따로 정의할 수도 있지만 AccountSummaryCell 안에서 정의함으로써 이 코드의 독자가 account와 관련이 있는 데이터들이라는 것을 강조했다.

  3. viewModel 변수를 선언한다.

    let viewModel: ViewModel? = nil
  4. 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

  1. AccountSummaryViewController에서 account들을 저장할 accounts 배열을 선언한다.

    var accounts: [AccountSummaryCell.ViewModel] = []
  2. fetchData() 메소드에서 필요한 ViewModel 데이터들을 만들고 accounts 배열에 삽입한다.

  3. tableView의 numberOfRowsInSection 메소드를 수정한다.

  4. 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

위에서 우리는 각 데이터에 맞게 AccountType과 AccountName을 설정해주었다. 하지만 아직 balance 값은 각 데이터에 따라서 변경이 되지 않는다.

Decimal를 Double로 바꿔서 dollarscents 로 분리해서 balanceAmountLabel에 적용해보자.

(참고: 언제 Decimal을 써야 할까)

Adding the decimal

  1. ViewModel 에 Decimal 타입의 balance 변수를 추가한다.
  2. fetchData()의 내용을 수정한다.
  3. Decimal을 Double로 변환한다.
    1. 이를 위해 Utils 디렉토리에 DecimalUtils.swift 파일을 생성한다.

    2. 아래와 같이 코드를 추가한다.

      extension Decimal {
          var doubleValue: Double {
              return NSDecimalNumber(decimal: self).doubleValue
          }
      }
    3. Utils 디렉토리에 CurrencyFormatter.swift 파일을 추가한다.

    4. 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 개발을 익힌 사람들과 비교하지 말자. 꾸준히 공부하고 연습하면 결국 나도 그 천재들처럼 보일 것이다. 겁 먹지 말자!!!!

좋은 웹페이지 즐겨찾기