TabView를 커스텀화하여 만들 수 있습니다
TabView 분석
다음은 TabView을 커스텀화하기 위해서는 TabView가 어떻게 구성되어 있는지 확인합니다.
확인해보면 TabView는 2개의 Generic 타입의 값을 가지는 struct 입니다. TabView에서 SelectionValue는 Hashable를 준수하며 Content는 View를 준수하는 것을 확인할 수 있습니다.
추가로 TabView를 만들게 된다면 init() 메서드가 실행되는데 selection은 Binding 프로퍼티여야하며 content는 @ViewBuilder로 뷰를 만드는것으로 확인할 수 있습니다.
Enum 정의하기
반복적으로 사용할 코드를 Enum을 사용하여 만들어줍니다.
enum TabBarItem: Hashable {
case home, favorite, profile
var iconName: String {
switch self {
case .home: return "house"
case .favorite: return "heart"
case .profile: return "person"
}
}
var title: String {
switch self {
case .home: return "Home"
case .favorite: return "Favorites"
case .profile: return "Profile"
}
}
var color: Color {
switch self {
case .home: return Color.red
case .favorite: return Color.blue
case .profile: return Color.green
}
}
}
CustomTabBarContainerView
위에 분석 결과 커스텀화하여 만든 TabView를 다음과 같이 사용할 수 있음을 생각할 수 있습니다.
위에 코드와 같이 Content는 View 프로토콜을 준수하는 제너릭 타입으로 View를 준수하는 모든 콘텐츠를 사용할 수 있음을 의미합니다.
추가로 위와 같이 init() 메서드를 완성시켜 TabBarView를 담는 컨테이너를 완성시킵니다. body에서 content와 사전에 정의한 뷰를 사용하여 사용자한테 커스텀 뷰를 보여줍니다.
struct CustomTabBarContainerView<Content:View>: View {
@Binding var selection: TabBarItem
let content: Content
@State private var tabs: [TabBarItem] = []
init(selection: Binding<TabBarItem>, @ViewBuilder content: () -> Content) {
self._selection = selection
self.content = content()
}
var body: some View {
VStack(spacing: 0) {
ZStack {
content
}
CustomTabBarView(tabs: tabs, selection: $selection)
}
.onPreferenceChange(TabBarItemsPreferenceKey.self, perform: { value in
self.tabs = value
})
}
}
CustomTabBarView
다음은 커스텀화한 커스텀 TabBarView 입니다.
struct CustomTabBarView: View {
let tabs: [TabBarItem]
@Binding var selection: TabBarItem
var body: some View {
HStack {
ForEach(tabs, id: \.self) { tab in
tabView(tab: tab)
.onTapGesture {
switchToTab(tab: tab)
}
}
}
.padding(5)
.background(Color.white.ignoresSafeArea(edges: .bottom))
}
}
#Preview {
VStack {
let tab: TabBarItem = .home
Spacer()
CustomTabBarView(tabs: [
.home, .favorite, .profile
], selection: .constant(tab))
}
}
extension CustomTabBarView {
private func tabView(tab: TabBarItem) -> some View {
VStack {
Image(systemName: tab.iconName)
.font(.subheadline)
Text(tab.title)
.font(.system(size: 10, weight: .semibold, design: .rounded))
}
.foregroundStyle(selection == tab ? tab.color : Color.gray)
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
.background(selection == tab ? tab.color.opacity(0.3) : Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
private func switchToTab(tab: TabBarItem) {
withAnimation(.easeInOut) {
selection = tab
}
}
}
PreferenceKey 사용하기
PreferenceKey는 모든 뷰에서 접근할 수 있도록 Key, Value로 값을 정의하여 사용하는 get, set 가능한 프로토콜입니다.
PreferenceKey를 사용하여 원하는 뷰만 보여주게끔 opacity를 사용하여 설정한 tab과 selection이 같을 경우 사용자한테 보여주게끔 합니다.
struct TabBarItemsPreferenceKey: PreferenceKey {
static var defaultValue: [TabBarItem] = []
static func reduce(value: inout [TabBarItem], nextValue: () -> [TabBarItem]) {
value += nextValue()
}
}
struct TabBarItemViewModifier: ViewModifier {
let tab: TabBarItem
@Binding var selection: TabBarItem
func body(content: Content) -> some View {
content
.opacity(selection == tab ? 1.0 : 0.0)
.preference(key: TabBarItemsPreferenceKey.self, value: [tab])
}
}
extension View {
func tabBarItem(tab: TabBarItem, selection: Binding<TabBarItem>) -> some View {
modifier(TabBarItemViewModifier(tab: tab, selection: selection))
}
}
다음과 같이 tabBarItem 메서드가 사용됩니다.
CustomTabBarContainerView(selection: $tabSelection) {
Color.blue
.tabBarItem(tab: .home, selection: $tabSelection)
Color.red
.tabBarItem(tab: .favorite, selection: $tabSelection)
Color.green
.tabBarItem(tab: .profile, selection: $tabSelection)
}
커스텀 TabView 사용전
struct ContentView: View {
@State private var selection: String = "home"
var body: some View {
TabView(selection: $selection,
content: {
Color.red
.tabItem {
Image(systemName: "house")
Text("Home")
}
Color.blue
.tabItem {
Image(systemName: "heart")
Text("Favorite")
}
Color.orange
.tabItem {
Image(systemName: "person")
Text("Profile")
}
})
}
}
커스텀 TabView 사용 후
struct ContentView: View {
@State private var selection: String = "home"
@State private var tabSelection: TabBarItem = .home
var body: some View {
CustomTabBarContainerView(selection: $tabSelection) {
Color.blue
.tabBarItem(tab: .home, selection: $tabSelection)
Color.red
.tabBarItem(tab: .favorite, selection: $tabSelection)
Color.green
.tabBarItem(tab: .profile, selection: $tabSelection)
}
}
}
결과
https://github.com/iOS-Developer-KR/SwiftUITestRepository/tree/main/TabbarViewExample
'SwiftUI' 카테고리의 다른 글
UIViewRepresentable SwiftUI에서 UIKit 사용하기 (0) | 2024.04.24 |
---|---|
사용자한테 보여줄 색깔 선택하기 (0) | 2024.04.23 |
aligmentGuide (0) | 2024.04.21 |
Combine 이용해서 API로 JSON 다운받기 (0) | 2024.04.21 |
싱글톤 (1) | 2024.04.19 |