BasicTableViewCell로 음악 재생목록 만들기

고라니·2023년 7월 25일
0

TIL

목록 보기
15/67

UITableView와 UITableViewCell의 Basic Style을 이용하여 멜론의 재생목록 리스트를 간단하게 만들어 보기 (테이블뷰와 관련없는 요소들은 만들지 않음)

UITableView, UITableViewCell의 기본 개념

  • UITableView: iOS에서 목록이나 리스트 형태의 데이터를 표시하기 위한 스크롤 가능한 목록이다. 각 행은 UITableViewCell로 구성된다.

  • UITableViewCell: UITableView의 각 행을 나타내며, 데이터의 한 항목을 표시하는데 사용된다. 텍스트, 이미지 등 다양한 데이터 형식을 포함 한다.


목표

앨범 이미지, 노래 제목, 가수 그리고 우측 버튼으로 이루어진 Cell로 구성된 TableView의 UI를 구현


만들기

스토리보드에 UITableView, UITableViewCell 추가

기존 ViewController 대신 UITableViewController를 새로 만들어도 되지만 기존 ViewController에 UITableView와 UITableViewCell을 추가하였고, UITableViewCell의 Style은 Basic으로 설정했다. 그리고 Cell의 Identifier을 "Cell"로 지정했다.

데이터모델 생성

필요한 데이터를 받을 Track 이라는 구조체를 만들었다.
최대한 간단하게 만들려고 했지만 피쳐링이나 부가 설명이 있는 케이스가 보여서 description을 추가하여 subText에서 처리하게 만들었다.

import UIKit

struct Track {
    let albumImageName: String
    let titleText: String
    let author: String
    let description: String?

    var albumImage: UIImage? {
        return UIImage(named: albumImageName)
    }

    var subText: String {
        return [author, description].compactMap { $0 }.joined(separator: " ")
    }
}

더미데이터 추가

Track을 확장하여 더미데이터를 추가하였다.
최소한 스크롤정도는 가능한 개수로 맞췄다. (노가다 ㅠㅠ)
원래 모두 한줄씩 작성했는데 콤마 기준으로 줄바꿈을 해주면 가독성이 좋다고 하여 수정하였다.

한줄에 너무 많은 코드가 있으면 안좋다?
회사나 팀의 코드스타일에 따라 한줄에 작성하는 경우도 있긴 하지만, 한 라인에 코드가 너무 길면 작업자의 환경에 따라 줄바꿈이 어디서 일어날지 모르기 때문에 가독성이 좋지 않을 수 있다. 특히 한 화면에 두개의 코드를 비교하는 경우 적절한 줄바꿈을 해주면 가독성이 훨씬 좋아진다.

extension Track {
    static let tracks: [Track] = [
        Track(albumImageName: "NewJeans 2nd EP 'Get Up'",
              titleText: "Super Shy",
              author: "NewJeans",
              description: nil),

        Track(albumImageName: "Seven(feat.Latto)- Clean Ver.",
              titleText: "Seven",
              author: "정국",
              description: "(feat.Latto) - Clean Ver."),

        Track(albumImageName: "NewJeans 2nd EP 'Get Up'",
              titleText: "ETA",
              author: "NewJeans",
              description: nil),

        Track(albumImageName: "I feel",
              titleText: "퀸카",
              author: "(여자)아이들",
              description: "(Queencard)"),

        Track(albumImageName: "1집 Alone",
              titleText: "헤어지자 말해요",
              author: "박재정",
              description: nil),

        Track(albumImageName: "I've IVE",
              titleText: "I AM",
              author: "IVE (아이브)",
              description: nil),

        Track(albumImageName: "UNFORGIVEN",
              titleText: "이브, 프시케 그리고 푸른 수염의 아내",
              author: "LE SSERAFIM (르세라핌)",
              description: nil),

        Track(albumImageName: "MY WORLD - The 3rd Mini Album",
              titleText: "Spicy",
              author: "easpa",
              description: nil),

        Track(albumImageName: "Steal The Show (From \"엘리멘탈\")",
              titleText: "Steal The Show",
              author: "Lauv",
              description: "(from \"엘리멘탈\")"),

        Track(albumImageName: "MY WORLD - The 3rd Mini Album",
              titleText: "New Jeans",
              author: "NewJeans",
              description: nil),

        Track(albumImageName: "UNFORGIVEN",
              titleText: "UNFORGIVEN",
              author: "LE SEERAFIM (르세라핌)",
              description: "(feat. Nile Rodgers)"),

        Track(albumImageName: "I've IVE",
              titleText: "Kitsch",
              author: "IVE (아이브)",
              description: nil),

        Track(albumImageName: "NewJeans 1st Ep \"New Jeans\"",
              titleText: "Hype boy",
              author: "NewJeans",
              description: nil)
    ]
}

Image Source 추가

적절한 이미지 찾아서 Assets에 넣어 주었다.

@IBOutlet연결과 그외 전처리

@IBOutlet을 연결해주고 delegate와 dataSource를 self로

class ViewController: UIViewController {
    @IBOutlet weak var musicTableView: UITableView!
    
    let tracks = Track.tracks
    let cellIdentifier: String = "Cell"

    override func viewDidLoad() {
        super.viewDidLoad()
        setDelegates()
    }

    private func setDelegates() {
        musicTableView.delegate = self
        musicTableView.dataSource = self
    }
}

Delegate와 DataSource 프로토콜 채택

필수 요구사항중 첫번째 메서드는 몇개의 행을 생성할지 설정하는 역할을 한다. 더미데이터의 트랙들의 개수만큼 보여주기 위해 tracks.count

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tracks.count
}

두번째 필수 요구메서드는 해당 위치에 나타낼 셀을 반환하는 메서드이다. 이곳에서 데이터 모델로부터 받아온 데이터를 설정한다. tableView.dequeueReusableCell(withIdentifier)에 cellIdentifier 입력하여 셀을 만들어 주고 반환한다.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
    return cell
    }

Cell UI적용

기존 cell.image나 cell.label 등을 통해 cell의 UI를 설정하는 방식은 Deprecated되었다.

대신 더욱 성능이 좋고 유연한 UIListContentConfiguration 사용한다.

defaultContentConfiguration?
iOS 14 부터 도입된 메서드로 기본 컨텐츠 구성 객체를 반환하는데 사용된다.
셀에 표시되는 실제 컨텐츠 들을 정의한다. 이를 사용하면 셀의 UI를 보다 쉽게 구성하고 iOS의 다양한 모드에 대한 대응이 더 간편해진다.

UIListContentConfiguration 사용하여 UI를 설정하였다.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)

    cell.accessoryType = .disclosureIndicator

    var content = cell.defaultContentConfiguration()
    let currentTrack = tracks[indexPath.row]

    content.image = currentTrack.albumImage
    content.text = currentTrack.titleText
    content.secondaryText = currentTrack.subText
    content.textProperties.font = UIFont.boldSystemFont(ofSize: 16)
    cell.contentConfiguration = content
    return cell
}

이정도면 어느정도 구현이 된듯 하다.


문제 확인

해결해야 할 부분이 몇개 보임

  1. 앨범 이미지 사이즈가 통일 되어야 함
  2. 앨범 이미지 모서리에 라운드가 있어야 함
  3. 기본 테이블뷰와는 다르게 행을 구분하는 라인이 없어야 함
  4. 세로점 세 개 버튼 있어야 함
  5. SecondaryText에 투명도가 필요함

해결

이미지 사이즈 통일

imageProperties.maximumSize 코드를 추가하여 최대 사이즈를 50 x 50으로 설정

content.imageProperties.maximumSize = CGSize(width: 50, height: 50)

앨범 이미지 모서리 라운드

imageProperties.cornerRadius를 설정

content.imageProperties.cornerRadius = 5

라인 제거

테이블 뷰의 separatorStyle 을 .none으로 설정

musicTableView.separatorStyle = .none

버튼 추가

accessory 버튼을 추가하기 위해 cell.accessoryType을 설정 한다. 하지만 기본 타입 중 원하는 버튼 스타일이 없었다...
원하는 스타일로 구현하기 위해서는 커스텀 셀을 사용해야 한다. 하지만 기본 셀 스타일로 만들어보는 과정이기 떄문에 아쉽지만 .disclosureIndicator 스타일로 대체하였다.

cell.accessoryType = .disclosureIndicator

SecondaryText 투명도 설정

UIListContentConfiguration의 textProperties는 직접 텍스트 색상의 알파값을 변경하는 방법을 제공하지 않는다고 한다...
다른 방법을 찾아보니 NSAttributedString을 사용하여 투명도 설정이 가능했다.

let secondaryAttributes: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 16),
    .foregroundColor: UIColor.black.withAlphaComponent(0.5)
]

let secondaryAttributedText = NSAttributedString(string: currentTrack.subTitle, attributes: secondaryAttributes)
content.secondaryAttributedText = secondaryAttributedText

NSAttributedString을 사용하여 문제를 해결
NSAttributedString에 대한 이해가 아직은 부족하여 추후에 학습하도록 하겠다.


완성

cell.accessoryType이 조금 아쉽긴 하지만 어느정도 원하는 형태로 구현이 되었다.

마치면서

UITableViewCell의 Basic Style을 활용하여 멜론의 재생목록 UI를 구현해보았다. 기본 스타일을 활용하여 테이블뷰를 구현하는데 있어서 간편한 작업이 가능했지만, 몇 가지의 세부 요소를 표현하는 데 한계가 있었다.
하지만, 이런 제약점들을 발견하면서 표준 UI 요소만으로 수성하는 것의 한계와 이를 해결하기 위한 것들에 대한 개념을 배울 수 있어서 좋았다.
더 유연하고 세부적인 UI를 구현하기 위해서는 커스텀 셀을 사용해야 한다.

profile
🍎 무럭무럭

2개의 댓글

comment-user-thumbnail
2023년 7월 25일

많은 도움이 되었습니다, 감사합니다.

1개의 답글