How to Inject .Environmentobject() in Watchos6

How to inject .environmentObject() in watchOS6

The workaround from the link uses AnyView, which is a very bad idea. It has been explained in several other questions and tweets from Apple engineers, that AnyView should only be used on leaf views, as there is a heavy performance hit otherwise.

As for the second option (put the environmentObject inside ContentView), it works fine. Here you have an example:

class UserData: ObservableObject {
@Published var show: Bool = true
}

struct ContentView: View {
@State var model = UserData()

var body: some View {
SubView().environmentObject(model)
}
}

struct SubView: View {
@EnvironmentObject var model: UserData

var body: some View {
VStack {
Text("Tap Me!").onTapGesture {
self.model.show.toggle()
}

if self.model.show {
Text("Hello World")
}
}
}
}

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()))
}
}

How to create Generic if @EnvironmentObject?

The EnvironmentObject must be ObservableObject, so here is fix

struct ContentView<T: ObservableObject>: View {
@EnvironmentObject var mockFoobar: T

// .. other code here

Update: added demo with introduced model protocol

protocol Foobaring {
var param: Bool { get set }
func start()
}

class Foobar: ObservableObject, Foobaring {
@Published var param: Bool = false
func start() {
self.param = true
}
}

struct MyFoobarView<T: ObservableObject & Foobaring>: View {
@EnvironmentObject var foobar: T

var body: some View {
VStack {
Text("Hello Foobar")
}
.onAppear {
self.foobar.start()
}
}
}

struct MyFoobarView_Previews: PreviewProvider {
static var previews: some View {
let foobar: Foobar = Foobar()
return MyFoobarView<Foobar>()
.environmentObject(foobar)
}
}

EnvironmentObject refresh/hand over problems using NavigationLink

One of the solutions is to create a variable controlling whether to display a navigation stack.

class AppStateControl: ObservableObject {
...
@Published var isDetailActive = false // <- add this
}

Then you can use this variable to control the first NavigationLink by setting isActive parameter. Also you need to add .isDetailLink(false) to all subsequent links.

First link in stack:

NavigationLink(destination: DetailView1().environmentObject(appStateControl), isActive: self.$appStateControl.isDetailActive) {
Text("Show Detail View 1")
}
.isDetailLink(false)

All other links:

NavigationLink(destination: DetailView2().environmentObject(appStateControl)) {
Text("Show Detail View 2")
}
.isDetailLink(false)

Then just set isDetailActive to false to pop all your NavigationLinks and return to the main view:

Button(action: {
self.appStateControl.callView = "AppMain"
self.appStateControl.isDetailActive = false // <- add this
}) {
Text("Go to main App")
}

SwiftUI re-initialize EnvironmentObject?

A possible solution is to introduce explicit method in GlobalClass to reset it to initial state and use that method and in init and externally, like

class GlobalClass: ObservableObject {
@Published var value: Int = 1

init() {
self.reset()
}

func reset() {
self.value = 1
// do other activity if needed
}
}

struct reinitenviron: View{
@EnvironmentObject private var globalObj: GlobalClass
var body: some View{
Text("refresh").onTapGesture {
globalObj.reset() // << here
}
}
}

How to bind an EnvironmentObject variable to a slider

I found an answer here, the trick is to move the $ and not use self:

struct ExampleView : View {

@EnvironmentObject var externalData : ExternalData

var body: some View {
Slider(value: $externalData.sliderVal, from: 1, through: 100, by: 1)
}

}

final class ExternalData : BindableObject {

let didChange = PassthroughSubject<ExternalData, Never>()

var sliderVal : Double = 5.0 {
didSet {
didChange.send(self)
}
}
}

Trouble getting EnvironmentObject to update the UI

class NameClass: ObservableObject {
@Published var name = ""
}

struct ContentView: View {
@StateObject var myName = NameClass()

var body: some View {
NavigationView {
VStack {
TextField("Type", text: $myName.name)
NavigationLink(destination: SecondView()) {
Text("View2")
}
}
}.environmentObject(myName)
}
}

struct SecondView: View {
@EnvironmentObject var myself: NameClass
var body: some View {
Text("\(myself.name)")
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(NameClass())
}
}

Change between views on SwiftUI within NavigationView

I use a similar technic to show a login view and then the content view after the user logged in. Here's a way you can achieve that:

Create an ObservableObject

class pageSettings: ObservableObject {
@Published var currentPage: String = "splash"
}

Modify your SceneDelegate

Add following line after var window: UIWindow? :

var pageSettings = PageSettings()

Then replace the call of UIHostingController:

window.rootViewController = UIHostingController(rootView: contentView)

by this call:

window.rootViewController = UIHostingController(rootView: contentView.environmentObject(pageSettings))

Use your ObservableObject

Inside your MotherView, add this property:

@EnvironmentObject var pageSettings: PageSettings

Replace your test by this:

if pageSettings.currentPage == "splash" 

Inside your SplashScreen

You also need to call the EnvironmentObject. You can then update the value of currentPage like this (it will instantly switch view):

pageSettings.currentPage = "content"

You can use your environment object to pass data between all your views.

How do you know when @Published var changes inside a @EnvironmentObject?

You can use .onReceive, like

struct OtherView: View {
@EnvironmentObject var globalVariables : GlobalVariables

var body: some View {
Text("some view")
.onReceive(globalVariables.$searchText) { newValue in
// do anything needed here
}
}
}


Related Topics



Leave a reply



Submit