Any idea why the preview doesn't work here??
When I use the Text field, the preview comes up with a following error:
MessageSendFailure: Message send failure for update
==================================
| MessageError: Connection interrupted
import SwiftUI
import UIKit
struct ContentView: View {
#EnvironmentObject var userSettings: UserSettings
#State var loginmode = false
#State private var showingAlert = false
#State var selectedTab = "Home"
#State var showMenu = false
#State var visible = false
#State var color = Color.black.opacity(0.7)
var body: some View {
TextField("Enter your email address", text: $userSettings.email)
}
}
struct RegisterView: View {
var body: some View {
Text("Hello World")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Content of another file:
import Foundation
import SwiftUI
import Combine
class UserSettings: ObservableObject
{
#Published var email = String()
}
The content of main file is as below:
import SwiftUI
#main
struct kokoburraApp: App {
#StateObject var userSettings = UserSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(userSettings)
}
}
}
Previews do not work without the necessary environment objects.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(UserSettings())
}
}
Related
my code works on the simulator here (it works here)
#main
struct sideMenu3App: App {
let persistenceController = PersistenceController.shared
#StateObject var viewRouter = ViewRouter()
var body: some Scene {
WindowGroup {
let context = persistenceController.container.viewContext
let dateHolder = DateHolder(context)
MotherView(viewRouter: viewRouter)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(dateHolder)
}
}}
on my PreviewProvider
struct MotherView_Previews: PreviewProvider {
//#StateObject var viewRouter: ViewRouter
static var previews: some View {
MotherView(viewRouter: ViewRouter()).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}}
the app crashes on the preview
I originally posted another question asking this in the context of a project I was trying to develop, but I can't even get it to work in a vacuum so I figured I'd start with the basics. As the title suggests, my EnvironmentObjects don't update the UI as they should; in the following code, the user enters text on the ContentView and should be able to see that text in the next screen SecondView.
EDITED:
import SwiftUI
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())
}
}
However, the SecondView doesn't show the text that the user has written, but the default value of name (blank). What am I doing wrong here?
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())
}
}
The following code produces the runtime error: #EnvironmentObject error: may be missing as an ancestor of this view. The tState in the environment is an #ObservedObject.
struct TEditorView: View {
#EnvironmentObject private var tState: TState
#State var name = ""
init() {
self._name = State(initialValue: tState.name)
}
var body: some View {
...
}
}
XCode 12.0.1
iOS 14
The answer is that an Environment Object apparently cannot be accessed in an init() function. However, an ObservedObject can be. So I changed the code to this and it works. To make it easy I turned TState into a singleton that I could access anywhere. This could probably replace the use of #EnvironmentObject in many situations.
struct TEditorView: View {
#ObservedObject private var tState = TState.shared
//#EnvironmentObject private var tState: TState
#State var name = ""
init() {
self._name = State(initialValue: tState.name)
}
var body: some View {
...
}
}
A different approach here could be to inject the initial TState value in the constructor and do-away with the #EnvironmentObject completely. Then from the parent view you can use the #EnvironmentObject value when creating the view.
struct TEditorView: View {
#State var name = ""
init(tState: TState) {
self._name = State(initialValue: tState.name)
}
var body: some View {
...
}
}
struct ContentView: View {
#EnvironmentObject private var tState: TState
var body: some View {
TEditorView(state: tState)
}
}
Or use a #Binding instead of #State if the name value is meant to be two-way.
In general I'd also question why you need the #EnvironmentObject in the constructor. The idea is with a #EnvironmentObject is that it's represented the same in all views, so you should only need it body.
If you need any data transformations it should be done in the object model itself, not the view.
The #State should be set as private and per the documentation should only be accessed in the View body.
https://developer.apple.com/documentation/swiftui/state
An #EnvironmentObject should be set using the ContentView().environmentObject(YourObservableObject)
https://developer.apple.com/documentation/combine/observableobject
https://developer.apple.com/documentation/swiftui/stateobject
Below is some Sample code
import SwiftUI
class SampleOO: ObservableObject {
#Published var name: String = "init name"
}
//ParentView
struct OOSample: View {
//The first version of an #EnvironmentObject is an #ObservedObject or #StateObject
//https://developer.apple.com/tutorials/swiftui/handling-user-input
#ObservedObject var sampleOO: SampleOO = SampleOO()
var body: some View {
VStack{
Button("change-name", action: {
self.sampleOO.name = "OOSample"
})
Text("OOSample = " + sampleOO.name)
//Doing this should fix your error code with no other workarounds
ChildEO().environmentObject(sampleOO)
SimpleChild(name: sampleOO.name)
}
}
}
//Can Display and Change name
struct ChildEO: View {
#EnvironmentObject var sampleOO: SampleOO
var body: some View {
VStack{
//Can change name
Button("ChildEO change-name", action: {
self.sampleOO.name = "ChildEO"
})
Text("ChildEO = " + sampleOO.name)
}
}
}
//Can only display name
struct SimpleChild: View {
var name: String
var body: some View {
VStack{
//Cannot change name
Button("SimpleChild - change-name", action: {
print("Can't change name")
//self.name = "SimpleChild"
})
Text("SimpleChild = " + name)
}
}
}
struct OOSample_Previews: PreviewProvider {
static var previews: some View {
OOSample()
}
}
I am learning Modals in SwiftUI and the code is below:
ContentView.swift:
import SwiftUI
struct ContentView: View {
#State private var showingAddUser = false
var body: some View {
return VStack {
Text("Modal View")
}.onTapGesture {
self.showingAddUser.toggle()
print(self.showingAddUser) //for console
}
.sheet(isPresented: $showingAddUser) {
Addview(isPresented: self.$showingAddUser)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
AddView.swift:
import SwiftUI
struct Addview: View {
#Binding var isPresented: Bool
var body: some View {
Button("Dismiss") {
self.isPresented = false
}
}
}
struct Addview_Previews: PreviewProvider {
static var previews: some View {
Addview(isPresented: .constant(false))
}
}
When I try to run the code for the first time and check the print output in console, boolean value changes to true however if I initialise #State variable showingAddUser with true the console output is unchanged that is it remains true. Should't toggle() flip the boolean value to false?
Is this toggle() different from Toggle switch from a concept point of view?
The toggle() is a mutating function on value type Bool. If you set the initial value of showingAddUser as true it will display the AddUser View when launched initially and it's not if set to false, that's the difference.
Toggle is a SwiftUI View. It can be used as any other View in SwiftUI body, like this:
struct ContentView: View {
#State var bool: Bool
var body: some View {
Toggle(isOn: $bool) {
Text("Hello world!")
}
}
}
There is no need for isPresented Boolean in Add View
Try This
ContentView.swift
import SwiftUI
struct ContentView: View {
#State private var showingAddUser = false
var body: some View {
return VStack {
Text("Modal View")
}.onTapGesture {
self.showingAddUser = true
print(self.showingAddUser) //for console
}
.sheet(isPresented: $showingAddUser) {
Addview()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
AddView.swift
import SwiftUI
struct AddView: View {
var body: some View {
Button(action:
// Do Your Things
) {
Text("MyButton")
}
}
}
struct Addview_Previews: PreviewProvider {
static var previews: some View {
Addview()
}
I am practicing trying to switch views using observable objects in SwiftUI, but my code isn't working. I know I can do this with #State, but I would like to get this working with observable objects. When I click on the image in the content view, the image doesn't change. Can someone help me?
ContentView.swift
import SwiftUI
struct ContentView: View {
#ObservedObject var viewRouter: ViewRouter
var body: some View {
VStack {
Button(action: {self.viewRouter.currentPage = "Page2"}) {
Image(viewRouter.currentPage)
}
if viewRouter.currentPage == "Page1" {
Page1(viewRouter: viewRouter)
}else if viewRouter.currentPage == "Page2" {
Page2(viewRouter: viewRouter)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(viewRouter: ViewRouter())
}
}
ViewRouter.swift
import Foundation
import SwiftUI
import Combine
class ViewRouter: ObservableObject {
let objectWillChange = PassthroughSubject<ViewRouter, Never>()
#Published var currentPage: String = "Page1"
}
Page1.swift
import SwiftUI
struct Page1: View {
#ObservedObject var viewRouter:ViewRouter
var body: some View {
VStack {
Image("ET-LondonBridge")
}
}
}
struct Page1_Previews: PreviewProvider {
static var previews: some View {
Page1(viewRouter: ViewRouter())
}
}
Page2.swift
import SwiftUI
struct Page2: View {
#ObservedObject var viewRouter:ViewRouter
var body: some View {
VStack {
Image("BigBen")
.aspectRatio(contentMode: .fit)
}
}
}
struct Page2_Previews: PreviewProvider {
static var previews: some View {
Page2(viewRouter: ViewRouter())
}
}
You don't need this line to make all things work. Just comment out this line
//let objectWillChange = PassthroughSubject<ViewRouter, Never>()