Aller au contenu principal

📑 Hébergement multi-onglets (UIKit et SwiftUI)

Navigation

Vue d'ensemble de la navigation · Catalogue des destinations — chaque écran ouvrable avec sdk.navigate.

Lorsque vous intégrez Nutrition et Entraînement dans votre app — en général sur deux onglets — le SDK fournit un VC hôte léger par module et un coordinateur relié à votre UI d'onglets. Un seul FlutterViewController partagé alimente les deux onglets.

Pourquoi c'est important : le moteur Flutter sur iOS ne peut rendre que dans un FlutterViewController à la fois. Une intégration naïve « deux FlutterVC par moteur » provoque des blocages au changement d'onglet. Le schéma SDK ci-dessous l'évite entièrement.

UIKit (UITabBarController)

import UIKit
import AzeooSDK

final class MainTabBarController: UITabBarController {

/// Holding the coordinator alive keeps the SDK's weak ref valid for the
/// lifetime of this tab bar controller.
private var tabCoordinator: AzeooUITabBarCoordinator?

override func viewDidLoad() {
super.viewDidLoad()
setupTabs()
}

private func setupTabs() {
guard let sdk = AzeooSDK.shared else { return }

let status = UINavigationController(rootViewController: StatusViewController())
status.tabBarItem = UITabBarItem(title: "Status",
image: UIImage(systemName: "info.circle"),
selectedImage: nil)

// Tab hosts — thin VCs that contain the shared Flutter surface.
let nutrition = sdk.tabHost(for: .nutrition)
nutrition.tabBarItem = UITabBarItem(title: "Nutrition",
image: UIImage(systemName: "leaf"),
selectedImage: nil)

let training = sdk.tabHost(for: .training)
training.tabBarItem = UITabBarItem(title: "Training",
image: UIImage(systemName: "dumbbell"),
selectedImage: nil)

viewControllers = [status, nutrition, training]
selectedViewController = status

// Install the coordinator once. From now on, any sdk.navigate(...)
// from anywhere automatically flips this tab bar.
let coordinator = AzeooUITabBarCoordinator(self, mapping: [
.nutrition: 1,
.training: 2,
])
sdk.setModuleContainer(coordinator)
tabCoordinator = coordinator
}

deinit {
AzeooSDK.shared?.setModuleContainer(nil)
}
}

C'est tout le code hôte nécessaire. Depuis n'importe quel view controller vous pouvez faire :

sdk.navigate(to: .nutrition(.plan(id: "abc-123")))
// Tab bar visually switches to Nutrition.
// Flutter switches to nutrition and pushes the plan detail.

SwiftUI (TabView)

import SwiftUI
import AzeooSDK

struct ContentView: View {
@EnvironmentObject var sdkManager: SDKManager

@State private var selectedTab = 0
@State private var tabCoordinator: AzeooSwiftUITabCoordinator<Int>? = nil

var body: some View {
TabView(selection: $selectedTab) {
StatusView()
.tabItem { Label("Status", systemImage: "info.circle.fill") }
.tag(0)

sdkManager.sdk!.modules.nutrition.getView()
.tabItem { Label("Nutrition", systemImage: "leaf.fill") }
.tag(1)

sdkManager.sdk!.modules.training.getView()
.tabItem { Label("Training", systemImage: "dumbbell.fill") }
.tag(2)
}
.onAppear {
guard let sdk = sdkManager.sdk, tabCoordinator == nil else { return }
let coordinator = AzeooSwiftUITabCoordinator<Int>(
selection: $selectedTab,
mapping: [.nutrition: 1, .training: 2]
)
sdk.setModuleContainer(coordinator)
tabCoordinator = coordinator
}
.onDisappear {
sdkManager.sdk?.setModuleContainer(nil)
tabCoordinator = nil
}
}
}

Fonctionnement

sdk.navigate(to: .training(.workouts))


┌──────────────────────────────────────┐
│ AzeooSDK.navigate(to:) │
└─────────┬────────────────┬───────────┘
│ │
coordinator.azeoo_showModule(.training)
│ │
▼ ▼
┌──────────────────────┐ Pigeon to(
│ AzeooUITabBarCoord. │ "training",
│ tabBar.selectedIndex │ "workout-plans",
│ = 2 │ params: nil)
└──────────────────────┘ │

Flutter switches its
internal tab + pushes
the workouts route

L'hôte écrit une ligne au démarrage ; toute navigation ensuite est automatique de bout en bout.

Pourquoi un seul FlutterViewController

Le SDK détient un seul FlutterViewController partagé. Chaque tabHost(for:) renvoie un view controller conteneur léger. Quand un onglet devient visible, le SDK :

  1. Reparente le FlutterVC partagé dans l'hôte d'onglet actif (un déplacement de vue UIKit, sans redémarrage du moteur)
  2. Envoie Pigeon display(module) pour que Flutter affiche le bon contenu d'onglet

AzeooTabHost surcharge shouldAutomaticallyForwardAppearanceMethods à false, donc les changements d'onglet ne déclenchent pas viewWillDisappearviewWillAppear sur le FlutterVC. Le moteur s'attache une fois et reste attaché — pas de flash, pas de blocage.

Autres conteneurs UIKit

Si vous utilisez UINavigationController plutôt qu'une barre d'onglets :

sdk.setModuleContainer(AzeooUINavigationCoordinator(
navController,
hostsByModule: [
.nutrition: nutritionVC,
.training: trainingVC,
],
))

Pour tout le reste (barre latérale, page view, tiroir, personnalisé) : implémentez AzeooModuleContainer directement — une seule méthode. Voir Conteneurs de modules.

Voir aussi