๐ŸŽ iOS

์ƒ๋‹จ ํƒญ ํŽ˜์ด์ง• - CollectionView๋ฅผ ํ™œ์šฉํ•œ ์ง์ ‘ ๊ตฌํ˜„

gom1n 2022. 10. 7. 08:27

ํŽ˜์ด์ง•์„ ํ•  ์ˆ˜ ์žˆ๋Š” ํƒญ์„ ๊ตฌํ˜„ํ•  ๋•Œ, ๊ธฐ์กด์—์„œ๋Š” ํƒญ๋งจ(Tabman) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ–ˆ์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์˜ค๋กœ์ง€ ์„ ํƒ / ๋ฏธ์„ ํƒ ์‹œ ์ƒ‰๊น”๋งŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ์•˜๊ณ ,

์ด๋Š” ๋””์ž์ด๋„ˆ๋‹˜์˜ ์š”๊ตฌ์‚ฌํ•ญ๊ณผ๋Š” ๋งž์ง€ ์•Š๊ธฐ์—... ์ง์ ‘ CollectionView๋กœ ์ž‘์—…์„ ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

 

๋””์ž์ด๋„ˆ๋‹˜ ์š”๊ตฌ์‚ฌํ•ญ

'์žฌ์น˜'์™€ '๊ฐ€์น˜' ํด๋ฆญ ์‹œ ๊ธ€์”จ์™€ Indicator๋ฐ” ์ƒ‰์ƒ ๋ณ€๊ฒฝ

๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ์กด์˜ Tabman ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” ์ƒ‰์ƒ ๊ตฌ๋ถ„์„ ๋ชปํ•˜๊ณ , ๋ฐ” ๊ธธ์ด๋„ ์กฐ์ ˆํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ด์Šˆ๊ฐ€ ์žˆ๋‹ค.

 

 

๋จผ์ € ๊ตฌ์กฐ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž.

์ƒ๋‹จ ํƒญ์€ CollectionView, ๊ทธ ๋ฐ‘์˜ Indicator๋ฐ”๋Š” UIView, ๊ทธ ์•„๋ž˜์˜ ํŽ˜์ด์ง•๋˜๋Š” ํŽ˜์ด์ง€๋“ค์€ CollectionView๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

 

์ฝ”๋“œ

 

1. ์ƒ๋‹จํƒญ์„ ๊ตฌ์„ฑํ•˜๋Š” CustomMenuBar ํด๋ž˜์Šค

CollectionView์™€ Indicator UIView๊ฐ€ ๊ฐ™์ด ์ •์˜๋˜์–ด์žˆ๋‹ค.

์ƒ๋‹จ ํƒญ์„ customTabBarCollectionView๋กœ ์ •์˜

๋ฐ”๋ฅผ IndicatorView๋กœ ์ •์˜.

class CustomMenuBar: UIView {
    weak var delegate: CustomMenuBarDelegate?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = .white
        setupCustomTabBar()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    var customTabBarCollectionView: UICollectionView = {
        let collectionViewLayout = UICollectionViewFlowLayout()
        collectionViewLayout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: collectionViewLayout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .white
        return collectionView
    }()
    // ํƒญ ๋ฐ”
    var indicatorView = UIView().then{
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.backgroundColor = .clear
    }
    var indicatorLineView = UIView().then{
        $0.backgroundColor = .zatchPurple
    }
    //MARK: Properties
    var indicatorViewLeadingConstraint:NSLayoutConstraint!
    var indicatorViewWidthConstraint: NSLayoutConstraint!
    //MARK: Setup Views
    func setupCollectionView(){
        customTabBarCollectionView.then{
            $0.delegate = self
            $0.dataSource = self
            $0.showsHorizontalScrollIndicator = false
            $0.register(CustomCell.self, forCellWithReuseIdentifier: CustomCell.reusableIdentifier)
            $0.isScrollEnabled = false
        }
        let indexPath = IndexPath(item: 0, section: 0)
        customTabBarCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
    }
    
    func setupCustomTabBar(){
        setupCollectionView()
        self.addSubview(customTabBarCollectionView)
        customTabBarCollectionView.snp.makeConstraints { make in
            make.leading.trailing.top.equalTo(self)
            make.height.equalTo(54)
        }
        
        self.addSubview(indicatorView)
        indicatorViewWidthConstraint = indicatorView.widthAnchor.constraint(equalToConstant: self.frame.width / 2)
        indicatorViewWidthConstraint.isActive = true
        indicatorView.heightAnchor.constraint(equalToConstant: 1.5).isActive = true
        indicatorViewLeadingConstraint = indicatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor)
        indicatorViewLeadingConstraint.isActive = true
        indicatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        
        indicatorView.addSubview(indicatorLineView)
        indicatorLineView.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.height.equalTo(1.5)
            make.width.equalTo(50)
        }
    }
}

 

CustomMenuBarํด๋ž˜์Šค์—์„œ CollectionView๋ฅผ ์ผ์œผ๋‹ˆ, delegate๋„ ํ•ด์ค€๋‹ค.

์ฒซ๋ฒˆ์งธ ์…€์€ '์žฌ์น˜', ๋‘๋ฒˆ์งธ ์…€์€ '๊ฐ€์น˜'๋กœ ์„ ์–ธ.

//MARK:- UICollectionViewDelegate, DataSource
extension CustomMenuBar: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCell.reusableIdentifier, for: indexPath) as! CustomCell
        cell.index = indexPath.row
        if indexPath.row == 0 {
            cell.label.text = "์žฌ์น˜"
        }
        else {
            cell.label.text = "๊ฐ€์น˜"
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: self.frame.width / 2 , height: 54)
        
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        delegate?.customMenuBar(scrollTo: indexPath.row)
        if indexPath.row == 0 {indicatorLineView.backgroundColor = .zatchPurple}
        else {indicatorLineView.backgroundColor = .zatchDeepYellow}
    }
    
    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        guard let cell = collectionView.cellForItem(at: indexPath) as? CustomCell else {return}
        cell.label.textColor = .black20
    }
}
//MARK:- UICollectionViewDelegateFlowLayout
extension CustomMenuBar: UICollectionViewDelegateFlowLayout {
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

๊ทธ๋ ‡๋‹ค๋ฉด, ์ƒ๋‹จ ํƒญ์— ์“ฐ์ธ CollectionView์…€๋„ ์ •์˜ํ•ด์ค˜์•ผํ•  ๊ฒƒ์ด๋‹ค.

CustomCell ํด๋ž˜์Šค - ๋‚˜๋Š” ์„ ํƒ์ง€์— ๋”ฐ๋ผ์„œ ์ƒ‰์ƒ ๊ตฌ๋ถ„์„ ํ•ด์ฃผ์—ˆ๋Š”๋ฐ, ๊ฐœ์ธ์— ๋”ฐ๋ผ ๊ตฌ๋ถ„ ์•ˆํ•ด์ค˜๋„ ๋œ๋‹ค.

class CustomCell: UICollectionViewCell {
    static let reusableIdentifier = "CustomCell"
    
    var label = UILabel().then{
        $0.text = "Tab"
        $0.textAlignment = .center
        $0.font = UIFont.pretendard(size: 14, family: .Bold)
        $0.textColor = .zatchPurple
        $0.translatesAutoresizingMaskIntoConstraints = false
    }
    var index: Int!
    override var isSelected: Bool {
        didSet{
            print("Changed")
            if index == 0 {self.label.textColor = isSelected ? .zatchPurple : .black20}
            else if index == 1 {self.label.textColor = isSelected ? .zatchDeepYellow : .black20}
        }
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.addSubview(label)
        label.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

 

2. CustomMenuBarDelegate

ํด๋ฆญ ์‹œ ํ™”๋ฉด์„ ์›ํ•˜๋Š” ์ธ๋ฑ์Šค๋กœ ์Šคํฌ๋กคํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ด์ค€๋‹ค.

protocol CustomMenuBarDelegate: class {
    func customMenuBar(scrollTo index: Int)
}
func customMenuBar(scrollTo index: Int) {
    let indexPath = IndexPath(row: index, section: 0)
    self.pageCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}

 

3. ๋ฉ”์ธ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๋Œ์•„๊ฐ€๋ณด์ž.

์ƒ๋‹จํƒญ์˜ ํด๋ฆญ์— ๋”ฐ๋ผ ํŽ˜์ด์ง•๋˜๋Š” pageCollectionView๋ฅผ ์„ ์–ธ.

๊ทธ ์•„๋ž˜์—๋‹ค๊ฐ€ CustomMenuBar ๊ฐ์ฒด๋„ ์‚ด์งœ์ฟต ์„ ์–ธํ•ด์ค€๋‹ค.

var pageCollectionView: UICollectionView = {
    let collectionViewLayout = UICollectionViewFlowLayout()
    collectionViewLayout.scrollDirection = .horizontal
    let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), collectionViewLayout: collectionViewLayout)
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    return collectionView
}()
var customMenuBar = CustomMenuBar()

SetUp ํ•ด์ค€๋‹ค.

//MARK: Setup view
func setupCustomTabBar(){
    self.view.addSubview(customMenuBar)
    customMenuBar.then{
        $0.delegate = self
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.indicatorViewWidthConstraint.constant = self.view.frame.width / 2
    }
    customMenuBar.snp.makeConstraints { make in
        make.leading.trailing.equalToSuperview()
        make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
        make.height.equalTo(50)
    }
}
func setupPageCollectionView(){
    pageCollectionView.then{
        $0.delegate = self
        $0.dataSource = self
        $0.backgroundColor = .gray
        $0.showsHorizontalScrollIndicator = false
        $0.isPagingEnabled = true
        $0.register(PageCell.self, forCellWithReuseIdentifier: PageCell.reusableIdentifier)
    }
    self.view.addSubview(pageCollectionView)
    pageCollectionView.snp.makeConstraints { make in
        make.leading.trailing.equalToSuperview()
        make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom)
        make.top.equalTo(self.customMenuBar.snp.bottom)
    }
}

CollectionView๋ฅผ ์จ์คฌ์œผ๋‹ˆ delegate๋„ ํ•ด์•ผํ•  ๊ฒƒ์ด๋‹ค.

extension TempViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: pageCollectionView.frame.width, height: pageCollectionView.frame.height)
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

 

 

PageCell - ํŽ˜์ด์ง•๋˜๋Š” ํŽ˜์ด์ง€ ์…€๋“ค๋„ ์ •์˜ํ•ด์ค€๋‹ค.

class PageCell: UICollectionViewCell {
    static let reusableIdentifier = "PageCell"

    var label = UILabel().then{
        $0.textColor = .black
        $0.textAlignment = .center
        $0.font = UIFont.systemFont(ofSize: 30, weight: .bold)
        $0.translatesAutoresizingMaskIntoConstraints = false
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.addSubview(label)
        self.backgroundColor = .systemGray6
        
        label.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

๋. ์ง„์งœ ์ฝ”๋“œ๊ฐ€ ์ฐธ ๊ธธ๋‹ค.

์–ด์ฐŒ์ €์ฐŒ ์„ฑ๊ณต์€ ํ–ˆ์ง€๋งŒ ... ๋˜๋„๋ก์ด๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๊ณ  ์‹ถ๋‹ค.