구현 내용
- 카테고리 내의 부분을 누르면 눌린 카테고리 색은 진하게 변하고, 그 밑에 밑줄이 생긴다.
- 카테고리 부분은 컬렉션 뷰로 구현되어 있다.
구현 방법
1. categoryCell.swift 파일을 생성한다.
import UIKit
class CategoryCell: UICollectionViewCell {
// MARK: - Variables
static let identifier = "CategoryCell"
// MARK: - UI Components
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.backgroundColor = .systemGray
label.layer.cornerRadius = 8
label.layer.masksToBounds = true
label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
return label
}()
private let underlineView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemOrange
return view
}()
// MARK: - Life Cycle
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(underlineView)
configureConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Functions
func configure(with title: String, isSelected: Bool) {
titleLabel.text = title
titleLabel.textColor = isSelected ? .label : .gray
underlineView.isHidden = !isSelected
}
// MARK: - UI Constraints
private func configureConstraints() {
let titleLabelConstraints = [
titleLabel.topAnchor.constraint(equalTo: topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor)
]
let underlineViewConstraints = [
underlineView.leadingAnchor.constraint(equalTo: leadingAnchor),
underlineView.trailingAnchor.constraint(equalTo: trailingAnchor),
underlineView.bottomAnchor.constraint(equalTo: bottomAnchor),
underlineView.heightAnchor.constraint(equalToConstant: 2)
]
NSLayoutConstraint.activate(titleLabelConstraints)
NSLayoutConstraint.activate(underlineViewConstraints)
}
}
2. CategoryMenuViewDelegate.swift 파일 생성한다.
import Foundation
protocol CategoryMenuViewDelegate: AnyObject {
func categoryMenuView(_ categoryMenuView: CategoryMenuCell, didselectedItemAt index: Int)
}
3. CategoryMenuCell.swift 파일 생성한다.
import UIKit
class CategoryMenuCell: UIView {
// MARK: Variables
private let categories = ["Attractions", "Hotels", "Foods", "Shoppings", "Events"]
private var selectedCategoryIndex = 0
weak var delegate: (CategoryMenuViewDelegate)?
// MARK: UI Components
private lazy var categoryCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 15
layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(CategoryCell.self, forCellWithReuseIdentifier: CategoryCell.identifier)
return collectionView
}()
// MARK: Life Cycle
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(categoryCollectionView)
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
configureConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: UI Constraints
private func configureConstraints() {
let categoryCollectionViewConstraints = [
categoryCollectionView.topAnchor.constraint(equalTo: topAnchor),
categoryCollectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
categoryCollectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
categoryCollectionView.bottomAnchor.constraint(equalTo: bottomAnchor)
// categoryCollectionView.heightAnchor.constraint(equalToConstant: 50)
]
NSLayoutConstraint.activate(categoryCollectionViewConstraints)
}
}
// MARK: - Extensions
extension CategoryMenuCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categories.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CategoryCell.identifier, for: indexPath) as! CategoryCell
cell.configure(with: categories[indexPath.item], isSelected: indexPath.item == selectedCategoryIndex)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let category = categories[indexPath.item]
let width = category.size(withAttributes: [.font: UIFont.systemFont(ofSize: 16, weight: .bold)]).width + 20
return CGSize(width: width, height: collectionView.bounds.height - 10)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedCategoryIndex = indexPath.item
collectionView.reloadData()
delegate?.categoryMenuView(self, didselectedItemAt: indexPath.item)
}
}
- extensions 부분은 UICollectionView를 통해 카테고리를 표시하고, 각 카테고리 셀의 크기와 셀 선택 시의 동작을 정의한다.
- 이 함수는 컬렉션 뷰에 표시할 항목의 개수를 반환한다. 여기서는 categories 배열의 개수를 반환하여, 배열에 있는 항목 수만큼 셀을 표시한다.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categories.count
}
- 이 함수는 컬렉션 뷰의 각 셀을 구성한다.
- dequeueReusableCell(withReuseIdentifier:for:)를 사용하여 재사용 가능한 셀을 가져오는데, 여기서는 CategoryCell 타입으로 캐스팅한다.
- cell.configure(with:isSelected:) 메서드를 호출하여 셀을 구성한다. categories[indexPath.item]로 해당 인덱스의 카테고리를 설정하고, indexPath.item == selectedCategoryIndex를 통해 현재 선택된 셀인지 여부를 전달한다.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CategoryCell.identifier, for: indexPath) as! CategoryCell
cell.configure(with: categories[indexPath.item], isSelected: indexPath.item == selectedCategoryIndex)
return cell
}
- 이 함수는 각 셀의 크기를 결정한다.
- categories[indexPath.item]로 현재 인덱스의 카테고리를 가져온다.
- 카테고리 문자열의 폭을 계산하기 위해 size(withAttributes:) 메서드를 사용하고, 여기에 추가적인 여백(20)을 더한다.
- 셀의 높이는 컬렉션 뷰의 높이에서 10을 뺀 값으로 설정한다.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let category = categories[indexPath.item]
let width = category.size(withAttributes: [.font: UIFont.systemFont(ofSize: 16, weight: .bold)]).width + 20
return CGSize(width: width, height: collectionView.bounds.height - 10)
}
- 이 함수는 사용자가 셀을 선택했을 때 호출된다.
- selectedCategoryIndex를 선택된 인덱스로 업데이트한다.
- collectionView.reloadData()를 호출하여 컬렉션 뷰를 다시 로드하고 선택된 셀의 상태를 반영한다.
- delegate?.categoryMenuView(self, didselectedItemAt:)를 호출하여 델리게이트에게 선택된 항목의 인덱스를 알린다.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedCategoryIndex = indexPath.item
collectionView.reloadData()
delegate?.categoryMenuView(self, didselectedItemAt: indexPath.item)
}
4. 포로토콜 관련해서 함수를 구체적으로 선언한다.
class HomeViewController: UIViewController, CategoryMenuViewDelegate {
func categoryMenuView(_ categoryMenuView: CategoryMenuCell, didselectedItemAt index: Int) {
print("index - \(index)")
}
...
'iOS > UIKIT' 카테고리의 다른 글
네비게이션 바 버튼에 이미지의 사이즈를 조절하는 방법 (0) | 2024.07.03 |
---|---|
category 누르면 그에 맞는 데이터를 테이블뷰에 보여주기 (0) | 2024.07.03 |
collectionView, pageControl 사용 (0) | 2024.07.02 |
navigation Title의 위치를 왼쪽으로 옮기는 방법? (0) | 2024.06.30 |
Custom TabBar 설정 (0) | 2024.06.29 |