MainViewController
import UIKit
import SnapKit
class MainViewController: UIViewController {
private let categoryTitleList = [ "추천", "신상품", "베스트", "알뜰쇼핑", "특가/혜택" ]
private lazy var pagingTabBar = PagingTabBar(categoryTitleList: categoryTitleList)
private lazy var pagingView = PagingView(categoryTitleList: categoryTitleList, pagingTabBar: pagingTabBar)
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
}
}
private extension MainViewController {
func setupLayout() {
[
pagingTabBar,
pagingView
].forEach { view.addSubview($0) }
pagingTabBar.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalToSuperview()
make.height.equalTo(pagingTabBar.cellHeight)
}
pagingView.snp.makeConstraints { make in
make.top.equalTo(pagingTabBar.snp.bottom)
make.leading.trailing.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide)
}
}
}
PagingTabBar
import UIKit
import SnapKit
protocol PagingDelegate: AnyObject {
func didTapPagingTabBarCell(scrollTo indexPath: IndexPath)
}
class PagingTabBar: UIView {
var cellHeight: CGFloat { 44.0 }
private var categoryTitleList: [String]
weak var delegate: PagingDelegate?
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let inset: CGFloat = 16.0
layout.itemSize = CGSize(width: (UIScreen.main.bounds.width - inset*2.0)/5.0, height: cellHeight)
layout.sectionInset = UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)
layout.minimumLineSpacing = 0.0
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .systemBackground
collectionView.showsHorizontalScrollIndicator = false
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(PagingTabBarCell.self, forCellWithReuseIdentifier: PagingTabBarCell.identifier)
return collectionView
}()
init(categoryTitleList: [String]) {
self.categoryTitleList = categoryTitleList
super.init(frame: .zero)
setupLayout()
collectionView.selectItem(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: [])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension PagingTabBar: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.didTapPagingTabBarCell(scrollTo: indexPath)
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
extension PagingTabBar: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoryTitleList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PagingTabBarCell.identifier, for: indexPath) as? PagingTabBarCell else { return UICollectionViewCell() }
cell.setupView(title: categoryTitleList[indexPath.row])
return cell
}
}
private extension PagingTabBar {
func setupLayout() {
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
PagingTabBarCell
import UIKit
import SnapKit
class PagingTabBarCell: UICollectionViewCell {
static let identifier = "PagingTabBarCell"
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 15.0, weight: .semibold)
label.textColor = .secondaryLabel
label.textAlignment = .center
return label
}()
private lazy var underline: UIView = {
let view = UIView()
view.backgroundColor = .systemPink
view.alpha = 0.0
return view
}()
override var isSelected: Bool {
didSet {
titleLabel.textColor = isSelected ? .systemPink : .secondaryLabel
underline.alpha = isSelected ? 1.0 : 0.0
}
}
func setupView(title: String) {
setupLayout()
titleLabel.text = title
}
}
private extension PagingTabBarCell {
func setupLayout() {
[
titleLabel,
underline
].forEach { addSubview($0) }
titleLabel.snp.makeConstraints { make in
make.leading.top.trailing.equalToSuperview()
}
underline.snp.makeConstraints { make in
make.height.equalTo(3.0)
make.top.equalTo(titleLabel.snp.bottom).offset(4.0)
make.leading.trailing.bottom.equalToSuperview()
}
}
}
PagingView
import UIKit
import SnapKit
class PagingView: UIView {
private let categoryTitleList: [String]
private let pagingTabBar: PagingTabBar
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.showsHorizontalScrollIndicator = false
collectionView.isPagingEnabled = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(PagingCollectionViewCell.self, forCellWithReuseIdentifier: PagingCollectionViewCell.identifier)
return collectionView
}()
init(categoryTitleList: [String], pagingTabBar: PagingTabBar) {
self.categoryTitleList = categoryTitleList
self.pagingTabBar = pagingTabBar
super.init(frame: .zero)
setupLayout()
pagingTabBar.delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension PagingView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewFrame = collectionView.frame
return CGSize(width: collectionViewFrame.width, height: collectionViewFrame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let indexPath = IndexPath(row: Int(targetContentOffset.pointee.x / UIScreen.main.bounds.width), section: 0)
pagingTabBar.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
}
}
extension PagingView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoryTitleList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PagingCollectionViewCell.identifier, for: indexPath) as? PagingCollectionViewCell else { return UICollectionViewCell() }
cell.setupView(title: categoryTitleList[indexPath.row])
return cell
}
}
extension PagingView: PagingDelegate {
func didTapPagingTabBarCell(scrollTo indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
}
}
private extension PagingView {
func setupLayout() {
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
PagingCollectionViewCell
import UIKit
import SnapKit
class PagingCollectionViewCell: UICollectionViewCell {
static let identifier = "PagingCollectionViewCell"
private lazy var contentLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 36.0, weight: .bold)
label.textAlignment = .center
label.backgroundColor = [.systemOrange, .systemPurple, .systemCyan, .systemMint, .systemBrown, .systemYellow].randomElement()
return label
}()
func setupView(title: String) {
setupLayout()
contentLabel.text = title
}
}
private extension PagingCollectionViewCell {
func setupLayout() {
[
contentLabel
].forEach { addSubview($0) }
contentLabel.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
결과