Using Multiple Hosting Controllers in Swiftui on Watchos

How does SwiftUI pass EnvironmentObjects between HostingController?

Here is possible approach

class AppState: ObservableObject {
static let shared = AppState() // shared instance

@Published var setting: String = "some"
}

class HostingController: WKHostingController<AnyView> {
override var body: AnyView {
let contentView = ContentView()
.environmentObject(AppState.shared) // << inject !!
return AnyView(contentView)
}
}

class HostingController2: WKHostingController<AnyView> {
override var body: AnyView {
let contentView = ContentView2()
.environmentObject(AppState.shared) // << inject !!
return AnyView(contentView)
}
}

Is page-based navigation possible in a SwiftUI app on WatchOS?

If you are using the new SwiftUI App Lifecycle and can't access Storyboards (or just need a solution for App Delegate Cycle without using Storyboards), you can use the new Page View style in SwiftUI 2:

WindowGroup {
TabView {
Page1View()
Page2View()
}
.tabViewStyle(PageTabViewStyle())
}

Issues implementing navigation from a main controller to page based controllers in WatchOS using SwiftUI

There is a logic error in your design. The reloadRootPageController function should be called in the top WKHostingController but "HC3".

 class HostingController: WKHostingController<ContentView> {

override func awake(withContext context: Any?) {
super.awake(withContext: context)
WKInterfaceController.reloadRootPageControllers(withNames: ["HC2", "HC3", "HC4"], contexts: [context] , orientation: WKPageOrientation.horizontal, pageIndex: 1)
}

override var body: ContentView {
return ContentView()
}
}

If reloadRootPageControllers is called in HC3, the strange situation is what you met.

Otherwise, you have to add a conditional_once in your HC3 setting.

class HC3: WKHostingController<CV> {

static var runOnce: Bool = true

override func awake(withContext context: Any?) {
super.awake(withContext: context)

if HC3.runOnce { HC3.runOnce.toggle()
WKInterfaceController.reloadRootPageControllers(withNames: ["HC2", "HC3", "HC4"], contexts: [context] , orientation: WKPageOrientation.horizontal, pageIndex: 1)
}

}

override var body: CV {
return CV()
}
}

How does the inbuilt Workout app on Apple Watch navigate from main table row to a set of page-based controllers?

OBJC:

+ (void)reloadRootPageControllersWithNames:(NSArray<NSString *> *)names 
contexts:(NSArray *)contexts
orientation:(WKPageOrientation)orientation
pageIndex:(NSInteger)pageIndex;

SWIFT:

class func reloadRootPageControllers(withNames names: [String], 
contexts: [Any]?,
orientation: WKPageOrientation,
pageIndex: Int)

Starting View on WatchOS

Just happened to open SO today and found this question.

If I understand correctly (based on the wwdc vid you shared), what you want to do is to open a child view in of your main view.

Let's set up an example based on what I assume you want to do. There is an entry point, which I will name HomeView, hosting your navigation view as well as the navigation links. You will also have two more views (just for illustration purposes), will I'll call AView and BView.

If I create this new app (StraightToDetailsApp), the main entry point should look like:

import SwiftUI

@main
struct StraightToTheDetailsApp: App {
var body: some Scene {
WindowGroup {
Text("Hello world!")
}
}
}

Let's add the three new views we need:

import SwiftUI

@main
struct StraightToTheDetailsApp: App {
var body: some Scene {
WindowGroup {
// replaced ContentView with HomeView
HomeView()
}
}
}

struct HomeView: View {
var body: some Scene {
NavigationView {
ScrollView {
// specified the navigation hierarchy from home.
NavigationLink("Hello A!!!", destination: AView())
NavigationLink("Greetings B!!!", destination: BView())
}
}
}
}

// Just made two simple views.
struct AView: View {
var body: some Scene {
VStack {
Text("Hello from A")
}.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .center)
.background(Color.blue)
}
}

struct BView: View {
var body: some Scene {
VStack {
Text("Hello from B")
}.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .center)
.background(Color.blue)
}
}

If you launch your app now, you will see home with two buttons, tapping on it triggers the navigation in the app.

As you want to force the app to navigate directly to one of the items, you will need to provide identifiers you can use to a bound variable, triggering the navigation automatically. What does that mean?

// let's create accessors to those links that you can reference from 
// somewhere in your code.
// Perhaps, you have a view model maintaining this state.
enum Details {
case a
case b
}

// And let's adapt home view, with the appropriate state tracking
// the "tag"

struct HomeView: View {
@State var selectedTag: Details? = nil

var body: some View {
NavigationView {
ScrollView {
NavigationLink(
"Hello A!!!",
destination: AView(),
tag: Details.a,
selection: self.$selected)

NavigationLink(
"Greetings B!!!",
destination: BView(),
tag: Details.b,
selection: self.$selected)
}
}
}
}

If you launch your app, it will be pretty much as before, but now you can provide a value to that state variable called selectedTag and make it go wherever you want.

struct HomeView: View {
@State var selectedTag: Details? = .a

var body: some View {
// ...
}
}

Launching the app now, the app will display AView with HomeView stacked in the navigation.

Using environmentObject in watchOS

You can use type erasure, AnyView in the case of SwiftUI View.

I would refactor WKHostingController to return AnyView.

This seems to compile fine on my end.

class HostingController : WKHostingController<AnyView> {
override var body: AnyView {
return AnyView(ContentView().environmentObject(DataModel()))
}
}


Related Topics



Leave a reply



Submit