Trouble using #environmentObject and #StateObject - swiftui

I am creating my first app and I am having trouble using #EnvironmentObject and #StateObject. When I run the simulator it opens my ContentView(). I am trying to get it to where it opens the MainView(), which has a tabView. And now the tabView is not showing my ContentView(). I am assuming I have the #EnvironmentObject and #StateObject in the wrong places. In my ContentView it shows a list and addButton. I am basically trying to have a program that is updated by the user by filling out a form.
Here is the Main Method
import SwiftUI
#main
struct Location_ScoutApp: App {
#StateObject var listViewModels: ListViewModel = ListViewModel()
var body: some Scene {
WindowGroup {
// MainView(){
ContentView()
.environmentObject(listViewModels)
//}
}
}
}
Here is my MainView.
import SwiftUI
struct MainView: View {
var body: some View {
TabView {
MapView()
.tabItem {
Label("Map", systemImage: "map.circle")
}
// this is where i am having trouble.
ContentView()
.tabItem {
Label("Explore", systemImage: "magnifyingglass")
}
ProfileView()
.tabItem {
Label("Profile", systemImage: "person.crop.circle")
}
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}
Here is my ContentView:
import SwiftUI
struct ContentView: View {
#EnvironmentObject var listViewModel: ListViewModel
var body: some View {
List {
ForEach(listViewModel.items) { item in
// this is where i am getting my new error.
ListRowView(item: item)
.onTapGesture {
listViewModel.updateItem(item: item)
}
}
}
.navigationTitle("Explore")
.navigationBarItems(
leading: EditButton(),
trailing:
NavigationLink("Add", destination: addALandmarkForm()))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
.environmentObject(ListViewModel())
}
}

Without a reproducible example, it's hard to debug. This code, however, which is based on yours, works fine. Perhaps you can find the difference between your implementation and mine:
struct Item : Identifiable {
var id = UUID()
var title : String
}
class ListViewModel: ObservableObject {
#Published var items : [Item] = [.init(title: "Test 1"),.init(title: "Test 2")]
}
#main
struct Location_ScoutApp: App {
#StateObject var listViewModels: ListViewModel = ListViewModel()
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(listViewModels)
}
}
}
struct MainView: View {
var body: some View {
TabView {
Text("Map")
.tabItem {
Label("Map", systemImage: "map.circle")
}
ContentView()
.tabItem {
Label("Explore", systemImage: "magnifyingglass")
}
Text("profile")
.tabItem {
Label("Profile", systemImage: "person.crop.circle")
}
}
}
}
struct ContentView: View {
#EnvironmentObject var listViewModel: ListViewModel
var body: some View {
NavigationView {
List {
ForEach(listViewModel.items) { item in
Text(item.title)
}
}
.navigationTitle("Explore")
}
}
}

Related

My environmentObject isn't working.I tap on navigationLink and see nothing in there

My environmentObject isn't working.I tap on navigationLink and see nothing in there.
I change note but it does not get updated.I made viewModel and share data from it everywhere I need it
I made the second TextEditor to do changes to my notes, but I cannot see changes.I just want to write smith and data should be updated
So how can I fix that?
import SwiftUI
#main
struct WhatToDoAppApp: App {
#StateObject private var vm = NoteViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(vm)
}
}
}
//ContentView.swift
import SwiftUI
struct ContentView: View {
#EnvironmentObject var vm: NoteViewModel
#State private var showSheet = false
#State private var searchText = ""
var body: some View {
NavigationView {
List {
ForEach(vm.notes) { item in
NavigationLink(destination: NoteDetailView()) {
Text(item.task)
.lineLimit(1)
}
}
.onDelete(perform: vm.deleteTask)
.onMove(perform: vm.moveTask)
}
.searchable(text: $searchText) {
if !searchResult.isEmpty {
ForEach(searchResult) { item in
NavigationLink(destination: NoteDetailView()) {
Text(item.task)
.lineLimit(1)
}
}
}
}
.navigationBarTitle("Notes")
.safeAreaInset(edge: .bottom) {
Color.clear
.frame(maxHeight: 40)
.background(.gray.opacity(0.7))
HStack {
Spacer(minLength: 160)
Text("\(vm.notes.count) notes")
.foregroundColor(.black.opacity(0.3))
Spacer()
Button {
showSheet = true
} label: {
Image(systemName: "square")
.font(.largeTitle)
.padding(.trailing)
}
}
}
.sheet(isPresented: $showSheet) {
NoteView()
}
}
}
var searchResult: [ToDoItem] {
guard !searchText.isEmpty else { return vm.notes }
return vm.notes.filter { $0.task.contains(searchText) }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.preferredColorScheme(.dark)
ContentView()
.preferredColorScheme(.light)
}
.environmentObject(NoteViewModel())
}
}
//NoteDetailView.swift
import SwiftUI
struct NoteDetailView: View {
#EnvironmentObject var vm: NoteViewModel
var body: some View {
VStack {
TextEditor(text: $vm.text)
Spacer()
}
}
}
struct NotedetailView_Previews: PreviewProvider {
static var previews: some View {
NoteDetailView().environmentObject(NoteViewModel())
}
}
//NoteView.swift
import SwiftUI
struct NoteView: View {
// #State private var text = ""
#EnvironmentObject var vm: NoteViewModel
#Environment(\.dismiss) var dismiss
var body: some View {
NavigationView {
VStack {
TextEditor(text: $vm.text)
}
.padding()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
addTask()
dismiss()
vm.text = ""
}, label: {
Text("Done")
.font(.system(size: 25))
.foregroundColor(.accentColor)
})
}
}
}
}
func addTask() {
vm.add(ToDoItem(task: vm.text))
}
}
struct NoteView_Previews: PreviewProvider {
static var previews: some View {
NoteView()
.environmentObject(NoteViewModel())
}
}
import Foundation
struct ToDoItem: Identifiable, Codable {
var id = UUID()
var task : String
}
class NoteViewModel: ObservableObject {
#Published var notes = [ToDoItem]()
#Published var text = ""
let saveKey = "SavedKey"
init() {
if let data = UserDefaults.standard.data(forKey: saveKey) {
if let decoded = try? JSONDecoder().decode([ToDoItem].self, from: data) {
notes = decoded
return
}
}
notes = []
}
private func save() {
if let encoded = try? JSONEncoder().encode(notes) {
UserDefaults.standard.set(encoded, forKey: saveKey)
}
}
func add(_ note: ToDoItem) {
notes.append(note)
save()
}
func deleteTask(indexSet: IndexSet) {
indexSet.forEach { index in
self.notes.remove(at: index)
save()
}
}
}
The detail view should be a #Binding, and you can use the array that you have in the viewModel as an Bindable List here the fixes:
List {
ForEach($vm.notes) { $item in
NavigationLink(item.task, destination: NoteDetailView(note: $item))
}
The detail view should look like this:
struct NoteDetailView: View {
#Binding var note: ToDoItem
#EnvironmentObject var vm: NoteViewModel
var body: some View {
VStack {
TextEditor(text: $note.task)
Spacer()
}
.onDisappear {
vm.save()
}
}}
This way every time the user updates and closes the modal, the list will be saved.

Why do I exit my TabView when navigating to a child view?

Question. Why do I exit my TabView when navigating to a view that is a child of a TabView? I hope I am being clear, but the code below is ready to be copy and pasted and notice how when I navigate to UnrelatedView I exit my tabView...
Additional context: The root view ContentView in this case is embedded inside of a NavigationView in the App.swift file
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
ViewA()
.tabItem {
Text("A")
}
ViewB()
.tabItem {
Text("B")
}
ViewC()
.tabItem {
Text("C")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ViewA: View {
var body: some View {
NavigationLink(destination: UnrelatedView()) {
Text("ViewA")
}
}
}
struct ViewB: View {
var body: some View {
Text("ViewB")
}
}
struct ViewC: View {
var body: some View {
Text("ViewC")
}
}
struct UnrelatedView: View {
var body: some View {
NavigationView {
Text("Unrelated View")
}
}
}
You need to wrap your navigation in NavigationViews.
struct ViewA: View {
var body: some View {
NavigationView {
NavigationLink(destination: UnrelatedView()) {
Text("ViewA")
}
}
}
}

How to get a value as to which view is selected in TabView?

I'm currently developing an application using SwiftUI.
This app has 2 Views controlled a Tab View.
I want to get a value as to which view is selected in TabView.
Is there any way to do that?
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
FirstView()
.tabItem {
Text("First")
}.tag(1)
SecondView()
.tabItem {
Text("Second")
}.tag(2)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
FirstView.swift
import SwiftUI
struct FirstView: View {
var body: some View {
Text("FirstView")
}
}
struct FirstView_Previews: PreviewProvider {
static var previews: some View {
FirstView()
}
}
SecondView.swift
import SwiftUI
struct SecondView: View {
var body: some View {
Text("SecondView")
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView()
}
}
Xcode: Version 11.7
Swift: Swift 5
You need to use selection, like below
Note: selection should be same type as used for tags (and use corresponding values from tags to select specific tab programmatically)
struct ContentView: View {
#State private var selectedTab = 1 // default selection
var body: some View {
TabView(selection: $selectedTab) { // << here !!
FirstView()
.tabItem {
Text("First")
}.tag(1)
SecondView()
.tabItem {
Text("Second")
}.tag(2)
}
}
}

HStack NavigationLink in Form tappable area small

I would like to be able to tap the entire row in this form to navigate to the next view; what am I doing wrong? It only allows me to navigate if I tap the symbol at the end.
This code should compile and run in your simulator.
Thanks.
import SwiftUI
struct SwiftUIView: View {
let selectedTags = ["A", "B", "C"]
var body: some View {
NavigationView {
Form {
NavigationLink(destination: DetailView()) {
ScrollView(.horizontal) {
HStack {
ForEach(selectedTags, id: \.self) { tag in
Text(tag)
}
}
}
}
}
}
}
}
struct DetailView: View {
var body: some View {
Text("Welcome")
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView()
}
}
copy - paste - run
import SwiftUI
struct ContentView: View {
let selectedTags = ["A", "B", "C"]
#State var active = false
var body: some View {
NavigationView {
Form {
NavigationLink( destination: DetailView(), isActive: $active) {
ScrollView(.horizontal) {
HStack {
ForEach(selectedTags, id: \.self) { tag in
Text(tag)
}
}
}.onTapGesture {
self.active.toggle()
}
}
}
}
}
}
struct DetailView: View {
var body: some View {
Text("Welcome")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

After using navigationLink to some views, how to go back to the first view in tab view?

There are some view with in my tabview, For example, if I use navigationLink to TeamDetail3, can I use a button and return to the TeamListView(first view)? I don't know if #Environment(.presentationMode) or other approach can do the effect like this. Any comments would be appreciated.
struct MainView: View {
#State var selectedtab:Int = 1
#Binding var isNavigationBarHidden : Bool
var body: some View {
TabView (selection: $selectedtab){
UserInformationView()
.tabItem {
Image(systemName: "person.fill")
Text("1")
}.tag(0)
.navigationBarTitle("1")
.navigationBarHidden(true)
TeamListView()
.tabItem {
Image(systemName: "person.3.fill")
Text("2")
}.tag(1)
.navigationBarTitle("2")
.navigationBarHidden(true)
}
.navigationBarTitle("main")
.onAppear(perform: {
self.isNavigationBarHidden = true
})
}
}
My TeamListView and their subview like this:
struct TeamListView: View {
#EnvironmentObject var userToken : UserToken
var body: some View {
NavigationView {
NavigationLink(destination: TeamDetail1()) {
Text("go to next page")
}
}
}
}
struct TeamDetail1: View {
#EnvironmentObject var userToken : UserToken
var body: some View {
NavigationLink(destination: TeamDetail2()) {
Text("go to next page")
}
}
}
}
struct TeamDetail2: View {
#EnvironmentObject var userToken : UserToken
var body: some View {
NavigationLink(destination: TeamDetail3()) {
Text("go to next page")
}
}
}
}
struct TeamDetail3: View {
#EnvironmentObject var userToken : UserToken
var body: some View {
Button(action:{}) {
Text("return to first page")
}
}
}
}