- 오토레이아웃을 통해 플로팅? 약간 떠 있는 탭바를 만들어봤다.
CustomTabBar.swift
import UIKit
class CustomTabBar: UIView {
// MARK: UI Components
// 버튼을 담을 스택뷰 생성
private let stackView: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .equalSpacing
stack.alignment = .center
stack.backgroundColor = .systemBackground
stack.layer.cornerRadius = 25
stack.isLayoutMarginsRelativeArrangement = true
stack.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
return stack
}()
// 스택뷰에 들어갈 버튼 생성
private let homeButton = createButton(systemName: "house.fill")
private let searchButton = createButton(systemName: "magnifyingglass.circle.fill")
private let addButton = createButton(systemName: "plus.circle.fill")
private let savedButton = createButton(systemName: "bookmark.fill")
private let profileButton = createButton(systemName: "person.circle.fill")
// 버튼이 눌렸을 때, 이를 확인하기 위한 변수 생성
private var selectedButton: UIButton?
// 데이터 전달을 위한 클로저 생성
var onButtonTapped: ((Int) -> Void)?
// MARK: Life Cycle
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
// Set initial selected button
buttonTapped(homeButton)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
addSubview(stackView)
stackView.addArrangedSubview(homeButton)
stackView.addArrangedSubview(searchButton)
stackView.addArrangedSubview(addButton)
stackView.addArrangedSubview(savedButton)
stackView.addArrangedSubview(profileButton)
homeButton.tag = 0
searchButton.tag = 1
addButton.tag = 2
savedButton.tag = 3
profileButton.tag = 4
[homeButton, searchButton, addButton, savedButton, profileButton].forEach { button in
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
@objc private func buttonTapped(_ sender: UIButton) {
// Deselect the previous selected button
if let selectedButton = selectedButton {
selectedButton.tintColor = .label
}
// Select the new button
sender.tintColor = .systemTeal
selectedButton = sender
// Trigger the callback
onButtonTapped?(sender.tag)
}
// 버튼을 생성하는 함수
private static func createButton(systemName: String) -> UIButton {
let button = UIButton(type: .system)
button.setImage(UIImage(systemName: systemName), for: .normal)
button.tintColor = .label
return button
}
}
buttonTapped 함수
@objc private func buttonTapped(_ sender: UIButton) {
// Deselect the previous selected button
if let selectedButton = selectedButton {
selectedButton.tintColor = .label
}
// Select the new button
sender.tintColor = .systemTeal
selectedButton = sender
// Trigger the callback
onButtonTapped?(sender.tag)
}
- @objc 키워드:
- Objective-C 런타임에 이 메소드를 노출시킵니다.
- UIButton의 target-action 메커니즘을 사용하기 위해 필요합니다.
- private func:
- 이 메소드는 클래스 내부에서만 사용됨을 나타냅니다.
- if let selectedButton = selectedButton { ... }:
- 이전에 선택된 버튼이 있다면 그 버튼의 색상을 기본값(.label)으로 되돌립니다.
- 이는 이전에 선택된 버튼의 하이라이트를 제거하는 역할을 합니다.
- sender.tintColor = .systemTeal:
- 현재 탭된 버튼의 색상을 .systemTeal로 변경합니다.
- 이는 현재 선택된 탭을 시각적으로 표시합니다.
- selectedButton = sender:
- 현재 선택된 버튼을 selectedButton 프로퍼티에 저장합니다.
- 이는 다음 탭 선택 시 이전 선택을 해제하기 위해 사용됩니다.
- onButtonTapped?(sender.tag):
- onButtonTapped 클로저가 설정되어 있다면 호출합니다.
- sender.tag를 인자로 전달하여 어떤 버튼이 탭되었는지 알려줍니다.
- 이 클로저는 일반적으로 CustomTabBarController에서 탭 전환 로직을 처리하는 데 사용됩니다.
CustomTabBarController.swift
import UIKit
class CustomTabBarController: UITabBarController {
private let customTabBar = CustomTabBar()
override func viewDidLoad() {
super.viewDidLoad()
setupCustomTabBar()
setupViewControllers()
view.backgroundColor = .systemTeal
}
private func setupCustomTabBar() {
// 기본 탭바 숨기기
tabBar.isHidden = true
// 커스텀 탭바 추가
view.addSubview(customTabBar)
customTabBar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
customTabBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -15),
customTabBar.heightAnchor.constraint(equalToConstant: 60)
])
// 커스텀 탭바 버튼에 액션 추가
customTabBar.onButtonTapped = { [weak self] index in
self?.selectedIndex = index
}
}
private func setupViewControllers() {
// 각 탭에 해당하는 뷰 컨트롤러 설정
let homeVC = UINavigationController(rootViewController: HomeViewController())
//homeVC.view.backgroundColor = .white
let searchVC = UINavigationController(rootViewController: SearchViewController())
//searchVC.view.backgroundColor = .lightGray
let addPlanVC = UINavigationController(rootViewController: AddPlanViewController())
//addPlanVC.view.backgroundColor = .gray
let bookmarkVC = UINavigationController(rootViewController: BookmarkViewController())
//bookmarkVC.view.backgroundColor = .darkGray
let profileVC = UINavigationController(rootViewController: ProfileViewController())
//profileVC.view.backgroundColor = .black
viewControllers = [homeVC, searchVC, addPlanVC, bookmarkVC, profileVC]
}
}
클로저 호출
// 커스텀 탭바 버튼에 액션 추가
customTabBar.onButtonTapped = { [weak self] index in
self?.selectedIndex = index
}
- customTabBar.onButtonTapped = { ... }:
- 이는 CustomTabBar 클래스의 onButtonTapped 속성에 클로저를 할당하는 것입니다.
- 이 클로저는 탭바의 버튼이 탭될 때마다 호출됩니다.
- [weak self]:
- 이는 클로저 내에서 self에 대한 약한 참조를 생성합니다.
- 메모리 순환 참조(retain cycle)를 방지하기 위해 사용됩니다.
- index in:
- 이는 클로저가 받는 매개변수입니다.
- index는 탭된 버튼의 인덱스(또는 태그)를 나타냅니다.
- self?.selectedIndex = index:
- self는 CustomTabBarController를 가리킵니다.
- selectedIndex는 UITabBarController의 속성입니다.
selectedIndex에 대해 자세히 설명하겠습니다:
- selectedIndex는 UITabBarController의 속성입니다.
- 이 속성은 현재 선택된 탭의 인덱스를 나타냅니다.
- 이 값을 변경하면 해당 인덱스의 뷰 컨트롤러로 화면이 전환됩니다.
- 인덱스는 0부터 시작합니다. (예: 첫 번째 탭은 0, 두 번째 탭은 1, ...)
이 코드의 전체적인 동작은 다음과 같습니다:
- 사용자가 커스텀 탭바의 버튼을 탭합니다.
- CustomTabBar에서 해당 버튼의 인덱스(또는 태그)를 이 클로저로 전달합니다.
- 클로저는 전달받은 인덱스를 UITabBarController의 selectedIndex에 할당합니다.
- 결과적으로, 탭된 버튼에 해당하는 뷰 컨트롤러로 화면이 전환됩니다.
'UIKIT' 카테고리의 다른 글
collectionView, pageControl 사용 (0) | 2024.07.02 |
---|---|
navigation Title의 위치를 왼쪽으로 옮기는 방법? (0) | 2024.06.30 |
컬렉션 뷰 설정 (0) | 2024.06.26 |
카카오톡 로그인 API 구현 (0) | 2024.05.23 |
CollectionView 만들기 (코드로 구현) (1) | 2024.02.25 |