Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- swift documentation
- HIG
- 리펙토링
- uiscrollview
- combine
- ribs
- SWIFT
- Protocol
- uitableview
- RxCocoa
- collectionview
- Refactoring
- 애니메이션
- MVVM
- UICollectionView
- 리펙터링
- 클린 코드
- tableView
- Xcode
- clean architecture
- UITextView
- ios
- 스위프트
- Observable
- rxswift
- map
- 리팩토링
- Clean Code
- Human interface guide
- swiftUI
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] Custom TabBar 구현 방법 (커스텀 탭바, UIStackView, fillEqually) 본문
UI 컴포넌트 (swift)
[iOS - swift] Custom TabBar 구현 방법 (커스텀 탭바, UIStackView, fillEqually)
jake-kim 2023. 1. 8. 23:22
Custom TabBar 구현 아이디어
- StackView의 distribution을 fillEqually로, alignment를 center로 설정하고 각 버튼을 삽입하여 구현
- distribution을 filleEqually로 설정하면 스택뷰안의 아이템들이 동일한 크기로 배치
Custom TabBar 구현
- 구현에 사용된 extension
- UIControl에 추가한 addAction은 버튼의 이벤트 처리를 할 때, addTarget 방식이 아닌 클로저 처리 방식으로 사용
- UIImage에 추가한 alpha 메소드는 이미지에 alpha값을 부여하는 메소드
// https://ios-development.tistory.com/1237
public extension UIControl {
func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping () -> ()) {
@objc class ClosureSleeve: NSObject {
let closure: () -> ()
init(_ closure: @escaping () -> ()) {
self.closure = closure
}
@objc func invoke() {
closure()
}
}
let sleeve = ClosureSleeve(closure)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
// https://stackoverflow.com/questions/28517866/how-to-set-the-alpha-of-an-uiimage-in-swift-programmatically
extension UIImage {
func alpha(_ value:CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(at: CGPoint.zero, blendMode: .normal, alpha: value)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
- 탭바에 들어가는 항목들을 enum으로 정의하고, normalImage와 selectedImage를 따로 정의
- normalImage: 선택되지 않은 탭
- selectedImage: 선택된 탭
enum TabItem: Int {
case home
case chat
case my
var normalImage: UIImage? {
switch self {
case .home:
return UIImage(systemName: "house")
case .chat:
return UIImage(systemName: "message")
case .my:
return UIImage(systemName: "person.crop.circle")
}
}
var selectedImage: UIImage? {
switch self {
case .home:
return UIImage(systemName: "house.fill")
case .chat:
return UIImage(systemName: "message.fill")
case .my:
return UIImage(systemName: "person.crop.circle.fill")
}
}
}
- class CustomTabBar 선언
final class CustomTabBar: UIView {
}
- stackView와 필요한 프로퍼티 정의
- tabButtons을 전역으로 빼놓는 이유는, updateUI에서 선택된 인덱스값을 바탕으로 setImage해주기 위함
private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.alignment = .center
return stackView
}()
private let tabItems: [TabItem]
private var tabButtons = [UIButton]()
private var selectedIndex = 0 {
didSet { updateUI() }
}
private func updateUI() {
tabItems
.enumerated()
.forEach { i, item in
let isButtonSelected = selectedIndex == i
let image = isButtonSelected ? item.selectedImage : item.normalImage
let selectedButton = tabButtons[i]
selectedButton.setImage(image, for: .normal)
selectedButton.setImage(image?.alpha(0.5), for: .highlighted)
}
}
- 초기화 부분
- tamItems만큼 버튼을 만들고 레이아웃 설정
- 오토레이아웃 설정은 stackView만 하면 되므로, 따로 버튼에 관해 레이아웃 설정을 하지 않아도 되므로 편리 (stackView의 fillEqually가 적용)
init(tabItems: [TabItem]) {
self.tabItems = tabItems
super.init(frame: .zero)
setUp()
}
required init?(coder: NSCoder) {
fatalError()
}
private func setUp() {
defer { updateUI() }
tabItems
.enumerated()
.forEach { i, item in
let button = UIButton()
button.setImage(item.normalImage, for: .normal)
button.setImage(item.normalImage?.alpha(0.5), for: .highlighted)
button.addAction { [weak self] in
self?.selectedIndex = i
}
tabButtons.append(button)
stackView.addArrangedSubview(button)
}
backgroundColor = .systemGray.withAlphaComponent(0.2)
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalTo: leftAnchor),
stackView.rightAnchor.constraint(equalTo: rightAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor),
])
}
사용하는 쪽
- CustomTabBar 인스턴스를 만듣 후 오토 레이아웃을 설정하여 사용
class ViewController: UIViewController {
let tabBarView = CustomTabBar(tabItems: [.home, .chat, .my])
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tabBarView)
tabBarView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tabBarView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
tabBarView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor),
tabBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
tabBarView.heightAnchor.constraint(equalToConstant: 56),
tabBarView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor),
])
}
}
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments