Skip to content

Commit ed3c923

Browse files
authored
[NL-7]: BaseTabBarController 구현 (#34)
1 parent f33373a commit ed3c923

File tree

14 files changed

+367
-14
lines changed

14 files changed

+367
-14
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// BaseTabBarController.swift
3+
// Base
4+
//
5+
// Created by ttozzi on 8/10/25.
6+
//
7+
8+
import Combine
9+
import DesignSystem
10+
import UIKit
11+
import SnapKit
12+
import Then
13+
14+
final class BaseTabBarController: UITabBarController {
15+
16+
private lazy var customTabBar = TabBarView().then {
17+
$0.backgroundColor = STColors.white.color
18+
}
19+
private var cancellables = Set<AnyCancellable>()
20+
21+
override func viewDidLoad() {
22+
super.viewDidLoad()
23+
setupTabBar()
24+
setupBindings()
25+
}
26+
27+
override func viewDidLayoutSubviews() {
28+
super.viewDidLayoutSubviews()
29+
30+
if let window = view.window {
31+
customTabBar.snp.updateConstraints { make in
32+
make.height.equalTo(TabBarView.Constant.tabBarHeight + window.safeAreaInsets.bottom)
33+
}
34+
}
35+
}
36+
37+
private func setupTabBar() {
38+
tabBar.isHidden = true
39+
40+
view.addSubview(customTabBar)
41+
customTabBar.snp.makeConstraints { make in
42+
make.height.equalTo(TabBarView.Constant.tabBarHeight)
43+
make.leading.trailing.bottom.equalToSuperview()
44+
}
45+
}
46+
47+
private func setupBindings() {
48+
customTabBar.selectedIndexSubject
49+
.sink { [weak self] selectedIndex in
50+
self?.selectedIndex = selectedIndex
51+
}
52+
.store(in: &cancellables)
53+
}
54+
}
55+
56+
@available(iOS 17.0, *)
57+
#Preview {
58+
let tabBarController = BaseTabBarController()
59+
return tabBarController
60+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Tab.swift
3+
// Base
4+
//
5+
// Created by ttozzi on 8/15/25.
6+
//
7+
8+
import DesignSystem
9+
import UIKit
10+
11+
enum Tab: CaseIterable {
12+
case home
13+
case fortune
14+
case pastLotto
15+
case my
16+
17+
var title: String {
18+
switch self {
19+
case .home:
20+
return ""
21+
case .fortune:
22+
return "오늘 운세"
23+
case .pastLotto:
24+
return "뭐 나왔지"
25+
case .my:
26+
return "마이"
27+
}
28+
}
29+
30+
var icon: UIImage? {
31+
switch self {
32+
case .home:
33+
return STImages.home.image
34+
case .fortune:
35+
return STImages.clover.image
36+
case .pastLotto:
37+
return STImages.receipt.image
38+
case .my:
39+
return STImages.user.image
40+
}
41+
}
42+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//
2+
// TabBarItem.swift
3+
// Base
4+
//
5+
// Created by ttozzi on 8/14/25.
6+
//
7+
8+
import DesignSystem
9+
import SnapKit
10+
import Then
11+
import UIKit
12+
13+
final class TabBarItem: UIView {
14+
15+
private enum Constant {
16+
static let selectedColor = STColors.primary2.color
17+
static let unselectedColor = STColors.gray5.color
18+
}
19+
20+
private lazy var contentStackView = UIStackView().then {
21+
$0.axis = .vertical
22+
$0.spacing = 4
23+
$0.alignment = .center
24+
}
25+
private lazy var imageView = UIImageView().then {
26+
$0.contentMode = .scaleAspectFit
27+
}
28+
private lazy var titleLabel = UILabel()
29+
var image: UIImage? {
30+
get { imageView.image }
31+
set { imageView.image = newValue?.withRenderingMode(.alwaysTemplate) }
32+
}
33+
var title: String? {
34+
get { titleLabel.text }
35+
set { titleLabel.styledText = newValue }
36+
}
37+
var isSelected: Bool = false {
38+
didSet {
39+
update(selected: isSelected)
40+
}
41+
}
42+
43+
override init(frame: CGRect) {
44+
super.init(frame: frame)
45+
setupUI()
46+
}
47+
48+
required init?(coder: NSCoder) {
49+
fatalError("init(coder:) has not been implemented")
50+
}
51+
52+
private func setupUI() {
53+
addSubview(contentStackView)
54+
contentStackView.snp.makeConstraints { make in
55+
make.centerY.equalToSuperview()
56+
make.leading.greaterThanOrEqualToSuperview()
57+
make.bottom.lessThanOrEqualToSuperview()
58+
make.horizontalEdges.equalToSuperview()
59+
}
60+
contentStackView.addArrangedSubview(imageView)
61+
imageView.snp.makeConstraints { make in
62+
make.size.equalTo(24)
63+
}
64+
contentStackView.addArrangedSubview(titleLabel)
65+
}
66+
67+
private func update(selected: Bool) {
68+
if selected {
69+
imageView.tintColor = Constant.selectedColor
70+
titleLabel.textColor = Constant.selectedColor
71+
titleLabel.style = Typography.Caption_12_B
72+
} else {
73+
imageView.tintColor = Constant.unselectedColor
74+
titleLabel.textColor = Constant.unselectedColor
75+
titleLabel.style = Typography.Caption_12_R
76+
}
77+
}
78+
}
79+
80+
@available(iOS 17.0, *)
81+
#Preview {
82+
let content = UIStackView().then {
83+
$0.axis = .horizontal
84+
}
85+
let selectedItem = TabBarItem()
86+
selectedItem.snp.makeConstraints { make in
87+
make.width.equalTo(88)
88+
make.height.equalTo(72)
89+
}
90+
selectedItem.image = STImages.home.image
91+
selectedItem.title = ""
92+
selectedItem.isSelected = true
93+
content.addArrangedSubview(selectedItem)
94+
95+
let unselectedItem = TabBarItem()
96+
unselectedItem.snp.makeConstraints { make in
97+
make.width.equalTo(88)
98+
make.height.equalTo(72)
99+
}
100+
unselectedItem.image = STImages.home.image
101+
unselectedItem.title = ""
102+
unselectedItem.isSelected = false
103+
content.addArrangedSubview(unselectedItem)
104+
return content
105+
}
106+
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// TabBarView.swift
3+
// Base
4+
//
5+
// Created by ttozzi on 8/14/25.
6+
//
7+
8+
import Combine
9+
import DesignSystem
10+
import Extension
11+
import SnapKit
12+
import Then
13+
import UIKit
14+
15+
final class TabBarView: UIView {
16+
17+
enum Constant {
18+
static let tabBarHeight: CGFloat = 72
19+
}
20+
21+
private lazy var contentStackView = UIStackView().then {
22+
$0.axis = .horizontal
23+
$0.distribution = .fillEqually
24+
$0.alignment = .center
25+
$0.spacing = 5
26+
}
27+
private var tabBarItems: [TabBarItem] = []
28+
private var cancellables = Set<AnyCancellable>()
29+
let selectedIndexSubject = CurrentValueSubject<Int, Never>(.zero)
30+
31+
override init(frame: CGRect) {
32+
super.init(frame: frame)
33+
setupUI()
34+
setupBinding()
35+
}
36+
37+
required init?(coder: NSCoder) {
38+
fatalError("init(coder:) has not been implemented")
39+
}
40+
41+
private func setupUI() {
42+
addSubview(contentStackView)
43+
contentStackView.snp.makeConstraints { make in
44+
make.top.equalToSuperview()
45+
make.horizontalEdges.equalToSuperview().inset(4)
46+
make.height.equalTo(Constant.tabBarHeight)
47+
}
48+
49+
Tab.allCases.enumerated().forEach { index, tab in
50+
let item = TabBarItem()
51+
item.image = tab.icon
52+
item.title = tab.title
53+
item.isSelected = index == selectedIndexSubject.value
54+
contentStackView.addArrangedSubview(item)
55+
tabBarItems.append(item)
56+
}
57+
}
58+
59+
private func setupBinding() {
60+
tabBarItems.enumerated().forEach { index, item in
61+
item.gesturePublisher(gestureRecognizer: UITapGestureRecognizer())
62+
.sink { [weak self] _ in
63+
self?.selectedIndexSubject.send(index)
64+
}
65+
.store(in: &cancellables)
66+
67+
selectedIndexSubject
68+
.map { $0 == index }
69+
.sink { isSelected in
70+
item.isSelected = isSelected
71+
}
72+
.store(in: &cancellables)
73+
}
74+
}
75+
}
76+
77+
@available(iOS 17.0, *)
78+
#Preview {
79+
let tabBarView = TabBarView()
80+
tabBarView.snp.makeConstraints { make in
81+
make.width.equalTo(300)
82+
make.height.equalTo(72)
83+
}
84+
return tabBarView
85+
}
Lines changed: 2 additions & 13 deletions
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "clover.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Lines changed: 14 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "home.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "receipt.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}

0 commit comments

Comments
 (0)