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
Viewwilllayoutsubviews in Swift
How to Present a UIcollectionview in Swiftui with UIviewcontrollerrepresentable
Include Dictionary or Array Value in Swift String with Backslash Notation
Negative Arrayslice: Index Is Out of Range
How to Return Subscript in Constant Time
Uitableview Only Displays One Section
Using UIdropinteractiondelegate and Movies
Arrayliteralconvertible: Just a Normal Protocol
Swift Cannot Invoke '*' with an Argument List of Type '(Int, Int)'
How to Convert Custom Object to Data Swift
How to Add a Menu to The Application in The Dock
Avspeechrecognizer: Required Condition Is False: _Recordingtap == Nil Error in Swift3
Combine Sink: Ignore Receivevalue, Only Completion Is Needed
How to Use Passed Parameters in Swift Setmethodcallhandler - Self.Methodname(Result: Result)
Swift Making Copies of Passed Class Instances