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
How to Run App in Simulator Xcode 6
Accessing Struct from One Class to Another
Nsurl from String Gets Truncated, Dots on the Uivideoeditorcontroller's Video Path Swift 2 iOS 8
Collection View Layout Bug When Selectitem (Swift 5)
Getting the Time Remaining in the Time Interval of a Timer in Swift
A Method Without Parameters Is Calling for an Argument
How to Convert This Date Format in Swift
Swift - Protocol as Type as Target of Button Action
Thread 1: Signal Sigabrt Error When Using Ibactions
Differencebetween Convenience Init VS Init in Swift, Explicit Examples Better
Swift Switch Char{ Case "\U{E2}:
How to Inject .Environmentobject() in Watchos6
How to Handle Unsafepointer<Unmanaged<Cfarray>>
Swiftui: Unexpected Animation When Using a Non @State Var
Learning Swift: Expressions Are Not Allowed at the Top Level
Cannot Use Unarchivefromfile to Set Gamescene in Spritekit