guckmalmensch
guckmalmensch

Reputation: 1181

Switch tab on button press using wrapped UIKit tabview in SwiftUI

I tried to switch to another tab(say, WorkOutView()) when pressing a button on SocialView(). However, I've been using a wrapped struct of UIKit tabview in SwiftUI.

The Wrapper:

struct UIKitStyleTabView: View {
    var viewControllers: [HostingController<AnyView>]
    
    //@State var switchTab = 0

    struct Tab {
        var view: AnyView
        var barItem: UITabBarItem

        init<V: View>(view: V, barItem: UITabBarItem) {
            self.view = AnyView(view)
            self.barItem = barItem
        }
    }

    init(_ tabs: [Tab]) {
        self.viewControllers = tabs.map {
            let host = HostingController(rootView: $0.view)
            host.tabBarItem = $0.barItem
            return host
        }
    }

    var body: some View {
        TabBarController(controllers: viewControllers, switchTab: $switchTab)
            .edgesIgnoringSafeArea(.all)
    }
}

struct TabBarController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    @Binding var switchTav: Int

    func makeUIViewController(context: Context) -> UITabBarController {
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = controllers
        return tabBarController
    }

    func updateUIViewController(_ uiViewController: UITabBarController, context: Context) {
        //uiViewController.selectedIndex = switchTab
    }
}

And I set up like this:

struct MainView : View {
    //@Binding var switchTab: Int
    
    let tabBarSymbolConfig = UIImage.SymbolConfiguration(scale: .medium)
    
    var body : some View {
        
            UIKitStyleTabView([
                UIKitStyleTabView.Tab(view: SocialView(switchTab: $switchTab).accentColor(.white), barItem: UITabBarItem(title: "Social", image: UIImage(systemName: "person.3.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
                ), UIKitStyleTabView.Tab(view: SearchView().accentColor(.white), barItem: UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
                ), UIKitStyleTabView.Tab(view: WorkoutsView().accentColor(.white),barItem: UITabBarItem(title: "Workouts", image: UIImage(systemName: "doc.on.clipboard.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
               ), UIKitStyleTabView.Tab(view: ExercisesView().accentColor(.white), barItem: UITabBarItem(title: "Exercises", image: UIImage(systemName: "sportscourt", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
                ), UIKitStyleTabView.Tab(view: AccountView().accentColor(.white), barItem: UITabBarItem(title: "Me", image: UIImage(systemName: "person.crop.circle", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
                )
            ], switchTab: $switchTab).accentColor(.navIconColor)
        

I tried to insert a state/binding variable everywhere connecting these views, so that I can use it in the function updateUIViewController, but that didn't really work out, because I ended up having to insert a Binding into SceneDelegate.

Any suggestions how to achieve this?

Upvotes: 1

Views: 1400

Answers (1)

Asperi
Asperi

Reputation: 258491

Here is corrected & worked variant (replicated due to many absent components it is, so adapting back is on you).

Tested with Xcode 11.4 / iOS 13.4

demo

Full module code:

struct UIKitStyleTabView: View {
    var viewControllers: [UIHostingController<AnyView>]

    @Binding var switchTab: Int

    struct Tab {
        var view: AnyView
        var barItem: UITabBarItem

        init<V: View>(view: V, barItem: UITabBarItem) {
            self.view = AnyView(view)
            self.barItem = barItem
        }
    }

    init(_ tabs: [Tab], switchTab: Binding<Int>) {
        self.viewControllers = tabs.map {
            let host = UIHostingController(rootView: $0.view)
            host.tabBarItem = $0.barItem
            return host
        }
        self._switchTab = switchTab
    }

    var body: some View {
        TabBarController(controllers: viewControllers, switchTab: $switchTab)
            .edgesIgnoringSafeArea(.all)
    }
}

struct TabBarController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    @Binding var switchTab: Int

    func makeUIViewController(context: Context) -> UITabBarController {
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = controllers
        tabBarController.delegate = context.coordinator
        return tabBarController
    }

    func updateUIViewController(_ uiViewController: UITabBarController, context: Context) {
        uiViewController.selectedIndex = switchTab
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITabBarControllerDelegate {
        let owner: TabBarController
        init(_ owner: TabBarController) {
            self.owner = owner
        }
        func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
            owner.switchTab = tabBarController.selectedIndex
        }
    }
}

struct TabsMainView : View {
    @State var switchTab: Int = 0

    let tabBarSymbolConfig = UIImage.SymbolConfiguration(scale: .medium)

    var body : some View {

        UIKitStyleTabView([
            UIKitStyleTabView.Tab(view: SocialView(switchTab: $switchTab).accentColor(.white), barItem: UITabBarItem(title: "Social", image: UIImage(systemName: "person.3.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
            ), UIKitStyleTabView.Tab(view: Text("SearchView").accentColor(.white), barItem: UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
            ), UIKitStyleTabView.Tab(view: Text("WorkoutsView").accentColor(.white),barItem: UITabBarItem(title: "Workouts", image: UIImage(systemName: "doc.on.clipboard.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
            ), UIKitStyleTabView.Tab(view: Text("ExercisesView").accentColor(.white), barItem: UITabBarItem(title: "Exercises", image: UIImage(systemName: "sportscourt", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
            ), UIKitStyleTabView.Tab(view: Text("AccountView").accentColor(.white), barItem: UITabBarItem(title: "Me", image: UIImage(systemName: "person.crop.circle", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
            )
        ], switchTab: $switchTab)//.accentColor(.navIconColor)
    }
}

struct SocialView: View {
    @Binding var switchTab: Int

    var body: some View {
        Button("Go Search") { self.switchTab = 1 }
    }
}

struct TabsMainView_Previews: PreviewProvider {
    static var previews: some View {
        TabsMainView().colorScheme(.dark)
    }
}

Upvotes: 1

Related Questions