iOS 개발일기

[Swift] Custom PageViewController 본문

iOS/Swift

[Swift] Custom PageViewController

맨날 까먹으니 적어두자 2022. 3. 22. 16:15

기존 PageViewController를 사용한 방법이 아닌 

UICollectionView를 이용하여 PageViewController를 구현해보았습니다.

import UIKit

class PageViewController: UIViewController {

    var indicatorConstraint: NSLayoutConstraint?
    
    var indicatorView: UIView!
    var naviCollection: UICollectionView!
    var pageCollection: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad() 
        
        setupControls() 
        setupLayout()
        
        initData()
    }
    
    private func setupControls() {
        indicatorView = UIView()
        indicatorView.backgroundColor = .black
        
        naviCollection = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .horizontal
            
            let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
            cv.backgroundColor = .white
            cv.showsHorizontalScrollIndicator = false
            
            cv.delegate = self
            cv.dataSource = self
            
            cv.register(NaviCell.self, forCellWithReuseIdentifier: NaviCell.cellId)
            return cv
        }()
        
        pageCollection = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .horizontal
            
            let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
            cv.backgroundColor = .white
            cv.isPagingEnabled = true
            cv.showsHorizontalScrollIndicator = false
            
            cv.delegate = self
            cv.dataSource = self
            
            cv.register(PageCell.self, forCellWithReuseIdentifier: PageCell.cellId)
            return cv
        }()
    }
    
    private func setupLayout() {
        view.backgroundColor = .white
        
        view.addSubview(naviCollection)
        view.addSubview(indicatorView)
        view.addSubview(pageCollection)
        
        naviCollection.translatesAutoresizingMaskIntoConstraints = false
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        pageCollection.translatesAutoresizingMaskIntoConstraints = false
        
        naviCollection.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        naviCollection.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        naviCollection.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        naviCollection.heightAnchor.constraint(equalToConstant: 60).isActive = true
        
        indicatorView.topAnchor.constraint(equalTo: naviCollection.bottomAnchor).isActive = true
        indicatorView.widthAnchor.constraint(equalToConstant: view.frame.width / 4).isActive = true
        indicatorView.heightAnchor.constraint(equalToConstant: 3).isActive = true
        
        indicatorConstraint = indicatorView.leftAnchor.constraint(equalTo: view.leftAnchor)
        indicatorConstraint?.isActive = true
        
        pageCollection.topAnchor.constraint(equalTo: indicatorView.bottomAnchor).isActive = true
        pageCollection.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        pageCollection.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        pageCollection.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    }
    
    private func initData() {
        let indexPath = IndexPath(item: 0, section: 0)
        naviCollection.selectItem(at: indexPath, animated: false, scrollPosition: [])
    }

}
extension PageViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, 
                        numberOfItemsInSection section: Int) -> Int {
        return 4
    }
    
    func collectionView(_ collectionView: UICollectionView, 
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        switch collectionView {
        case naviCollection:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NaviCell.cellId, for: indexPath) as! NaviCell
            
            cell.index = indexPath.item
            return cell
        case pageCollection:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PageCell.cellId, for: indexPath) as! PageCell
            
            cell.index = indexPath.item
            return cell
        default:
            return UICollectionViewCell()
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, 
                        didSelectItemAt indexPath: IndexPath) {
        guard collectionView == naviCollection else {return}
        naviCollection.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
        
        pageCollection.reloadData()
        pageCollection.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    }
    
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        switch collectionView {
        case naviCollection:
            return CGSize(width: collectionView.frame.width / 4, height: 60)
        case pageCollection:
            return collectionView.frame.size
        default:
            return .zero
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, 
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        indicatorConstraint?.constant = scrollView.contentOffset.x / 4
    }
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, 
                                   withVelocity velocity: CGPoint, 
                                   targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let item = Int(targetContentOffset.pointee.x / scrollView.frame.width)
        let indexPath = IndexPath(item: item, section: 0)
        naviCollection.selectItem(at: indexPath, animated: true, scrollPosition: [])
    }
}

 

 

각 CollectionView에 사용되는 Cell입니다.

import UIKit

class NaviCell: UICollectionViewCell {
    
    static let cellId = "NaviCellId"
    
    var index: Int? {
        willSet {
            titleLabel.text = nil
        }
        
        didSet {
            guard let index = index else {return}
            titleLabel.text = String(index + 1) + "번 탭"
        }
    }
    
    var titleLabel: UILabel!
    
    override var isSelected: Bool {
        didSet {
            titleLabel.textColor = isSelected ? .black : .lightGray
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupControls()
        setupLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupControls() {
        titleLabel = {
            let l = UILabel()
            l.textAlignment = .center
            l.textColor = .lightGray
            l.font = .boldSystemFont(ofSize: 20)
            return l
        }()
    }
    
    private func setupLayout() {
        addSubview(titleLabel)
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
    }
}​
import UIKit

class PageCell: UICollectionViewCell {

    static let cellId = "PageCellId"
    
    var index: Int? {
        willSet {
            titleLabel.text = nil
        }
        
        didSet {
            guard let index = index else {return}
            titleLabel.text = String(index + 1) + "번 셀"
        }
    }
    
    var titleLabel: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupControls()
        setupLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupControls() {
        titleLabel = {
            let l = UILabel()
            l.textColor = .black
            l.textAlignment = .center
            l.font = .boldSystemFont(ofSize: 40)
            return l
        }()
    }
    
    private func setupLayout() {
        addSubview(titleLabel)
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
    }
}

 

 

 

참고

 

[ios] 커스텀 탭바(TabBar) 만들기

[ios] 커스텀 탭바(TabBar) 만들기 안녕하세요. 오늘은 기본 TabBar가 아닌 커스텀 TabBar를 만들어보려 합니다. 안드로이드에서와는 다르게 iOS에는 TabBar는 하단에 고정되어있고 이는 변경할 수 없습니

baked-corn.tistory.com