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를 다음과 같이 사용할 수 있음을 생각할 수 있습니다.

위에 코드와 같이 ContentView 프로토콜을 준수하는 제너릭 타입으로 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

 

SwiftUITestRepository/TabbarViewExample at main · iOS-Developer-KR/SwiftUITestRepository

Contribute to iOS-Developer-KR/SwiftUITestRepository development by creating an account on GitHub.

github.com

 

 

 

 

'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
ytw_developer