본문 바로가기
Project/UIKit 업비트

[UIKit] TableView 위에 Header를 만들어보자

by iOS_woo 2023. 4. 22.

오늘은 UITableVIew에 Header를 만드는 방법을 기록할거에요.
사진 속에서 프로필 사진, 이름, 전화번호, 유저아이디가 있는 영역이 오늘 작업할 Header에요.

1. ProfileHeader를 만들어주세요.

class ProfileHeader: UITableViewHeaderFooterView {
    // MARK: - Properties
    
    var vm: ProfileHeaderViewModel? {
        didSet { configure() }
    }
    
    private let profileImageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        iv.backgroundColor = .lightGray
        iv.image = UIImage(named: "venom-7")
        return iv
    }()
    
    private let nameLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.boldSystemFont(ofSize: 20)
        label.text = "Woo"
        return label
    }()
    
    private let phoneNumberLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 14)
        label.text = "+82 10 9650 3650"
        label.textColor = .lightGray
        return label
    }()
    
    private let userIdLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 14)
        label.text = "@minwoo_smile"
        label.textColor = .lightGray
        return label
    }()
    
    // MARK: - Lifecycle
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    // MARK: - Helpers
    
    func configureUI() {
        addSubview(profileImageView)
        profileImageView.centerX(inView: self)
        profileImageView.setDimensions(height: 100, width: 100)
        profileImageView.anchor(top: topAnchor, paddingTop: 16)
        profileImageView.layer.cornerRadius = 100 / 2
        
        addSubview(nameLabel)
        nameLabel.centerX(inView: self)
        nameLabel.anchor(top: profileImageView.bottomAnchor, paddingTop: 12)
        
        let stack = UIStackView(arrangedSubviews: [phoneNumberLabel, userIdLabel])
        stack.axis = .horizontal
        stack.spacing = 4
        
        addSubview(stack)
        stack.centerX(inView: self)
        stack.anchor(top: nameLabel.bottomAnchor, paddingTop: 10)
    }
    
    func configure() {
        // 작업 예정..
    }
}

 

2. viewDidLoad에서 register 해주세요.

private let headerIdentifier = "ProfileHeader"

tableView.register(ProfileHeader.self,
                           forHeaderFooterViewReuseIdentifier: headerIdentifier)

 

3. viewForHeaderInSection, heightForHeaderInSection를 설정해주세요.

guard 문의 코드는 테이블 뷰의 0번째 섹션 위에만 헤더를 놓기 위한 코드에요.
적어주지 않으면 모든 섹션 위에 헤더가 생성되어요.

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        guard section == 0 else { return nil }
        
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerIdentifier)
        return header
    }

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        guard section == 0 else { return 0 }
        
        return 200
    }

 

4. ProfileController 전체 코드

import UIKit

private let cellIdentifier = "ProfileCell"
private let headerIdentifier = "ProfileHeader"

class ProfileController: UITableViewController {
    
    // MARK: - Properties
    
    var vm = ProfileViewModel()
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureTableView()
    }
    
    // MARK: - Helpers
    
    func configureTableView() {
        navigationItem.title = "설정"
        view.backgroundColor = .white
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "편집",
                                                            style: .plain,
                                                            target: self,
                                                            action: #selector(handleEditProfile))
        navigationItem.rightBarButtonItem?.tintColor = .systemBlue
        
        tableView = UITableView(frame: tableView.frame, style: .insetGrouped)
        
        tableView.register(ProfileCell.self, forCellReuseIdentifier: cellIdentifier)
        tableView.register(ProfileHeader.self,
                           forHeaderFooterViewReuseIdentifier: headerIdentifier)
        tableView.rowHeight = 50
    }
    
    // MARK: - Actions
    
    @objc func handleEditProfile() {
        print("편집 눌렀습니다.")
    }
}

// MARK: - TableViewDataSource

extension ProfileController {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0:
            return 1
        case 1:
            return 3
        case 2:
            return 2
        default:
            return 0
        }
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ProfileCell
        switch indexPath.section {
        case 0:
            cell.configure(with: vm.tableCellList[0])
        case 1:
            cell.configure(with: vm.tableCellList[indexPath.row + 1])
        case 2:
            cell.configure(with: vm.tableCellList[indexPath.row + 4])
        default:
            break
        }
        return cell
    }
}

// MARK: - TableViewDelegate

extension ProfileController {
    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        guard section == 0 else { return nil }
        
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerIdentifier)
        return header
    }
    
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        guard section == 0 else { return 0 }
        
        return 200
    }
}

 

완성!

 

UICollectionView에서 헤더를 생성하는 것도 비슷하게 할 수 있어요.


다음 글에서는 위의 사진처럼 테이블뷰에 섹션을 구분하는 방법을 기록해볼게요.

홧팅~

댓글