I would like to make a test with SwiftUI binding.
Aim was changing Picker selection with a button and with an other Picker.
In following code ContentView2's Picker selection will be changed with button in ContentView and Picker in ContentView1.
Button can change ContentView2 picker selection but ContentView1 Picker does not.
I can not find the reason.
You can copy paste code to test.
import SwiftUI
struct ContentView: View {
#State private var index1 = 0
#State private var index2 = 1
var body: some View {
GeometryReader { mainView in
HStack {
Button(action: {
if index2 == 0 {
index2 = 1
} else {
index2 = 0
}
}) {
Text("Button")
}
ContentView1(pickerIndex: $index1)
ContentView2(pickerIndex: $index2)
}
}
}
}
struct ContentView1: View {
#State var pickerData = ["Data1", "Data2"]
#Binding var pickerIndex: Int
#State var pickerIndex2 = 0
var customLabel0: some View{
HStack {
VStack(spacing: 10) {
Text("Picker One")
.foregroundColor(.black)
Text(pickerData[pickerIndex])
.multilineTextAlignment(.center)
}
.foregroundColor(.white)
}
.frame(width: (UIScreen.main.bounds.width - (2.5 * 8 )) * 0.6 * 0.5, height: 100)
.background(Color.gray)
.cornerRadius(5)
}
#State private var testIndex = 1
var body: some View {
Menu {
Picker("", selection: self.$pickerIndex) {
ForEach(0..<pickerData.count, id: \.self) {index in
Text(pickerData[index])
}
}
} label: {
customLabel0
}
.onChange(of: pickerIndex, perform: {_ in
ContentView2(pickerIndex: $pickerIndex)
})
}
}
struct ContentView2: View {
#State var pickerData = ["Data1", "Data2"]
#Binding var pickerIndex: Int
var customLabel0: some View{
HStack {
VStack(spacing: 10) {
Text("Picker Two")
.foregroundColor(.black)
Text(pickerData[pickerIndex])
.multilineTextAlignment(.center)
}
.foregroundColor(.white)
}
.frame(width: (UIScreen.main.bounds.width - (2.5 * 8 )) * 0.6 * 0.5, height: 100)
.background(Color.gray)
.cornerRadius(5)
}
var body: some View {
Menu {
Picker("", selection: $pickerIndex) {
ForEach(0..<pickerData.count, id: \.self) {index in
Text(pickerData[index])
}
}
} label: {
customLabel0
}
.onChange(of: pickerIndex, perform: {_ in
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Related
I keep trying to have the navigation links in the surveys list in contentView to open detailView when selected. I keep getting errors Missing argument for parameter 'newsurvey' in call Insert 'newsurvey: <#Survey#>'
Here's the home view
struct ContentView: View {
#State var surveys: [Survey] = []
#State var isPresented = false
#State var selectedTab: Int = 0
#State private var path = [String]()
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
return formatter
}()
var body: some View {
ZStack {
VStack{
NavigationView {
VStack {
Text("")
.frame(height: 0)
.navigationBarTitleDisplayMode(.inline)
//reduces navbartitle height
.toolbar {
ToolbarItem(placement: .principal) {
Image("RR_Logo_Primary_Full_Color")
.resizable()
.frame(width: 275.0, height: 55.0)
}
}
List(surveys) { survey in
NavigationLink(destination: DetailView(newsurvey: survey)) //must be within navigationview
{
HStack{
ZStack {
Rectangle()
.frame(width: 400, height: 100.0)
.foregroundColor(.white)
.cornerRadius(4)
HStack (alignment: .top) {
VStack (alignment: .leading) {
Text(survey.customerName)
.offset(x: -60, y: -2)
.font(.custom("Roboto-Light", size: 24))
Text(survey.lineName)
.offset(x: -60, y: -5)
.font(.custom("Roboto-Light", size: 18))
Text("# of conveyors")
.offset(x: -60)
}
VStack {
Text(dateFormatter.string(from: survey.date))
.font(.custom("Roboto-Light", size: 18))
.offset(x: 60)
}
}
}
}
}
}
.listRowInsets(EdgeInsets())
}
}
.environment(\.defaultMinListHeaderHeight, 1)
CustomTabBar(surveys: $surveys, isPresented: $isPresented)
.frame(height: 60, alignment: .bottom)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I tried the following code below but I keep getting not in scope error.
#State var newsurvey: Survey
#State var surveys: [Survey] = []
#State private var conveyorName = ""
#State private var masterTag = ""
#State private var showAddConveyorSheet = false
#State private var conveyors: [Conveyor] = []
var body: some View {
NavigationView {
VStack {
List {
ForEach(conveyors, id: \.name) { conveyor in
NavigationLink(destination: ConveyorTrack(conveyor: conveyor)) {
Text(conveyor.name)
}
}
}
Button(action: {
self.showAddConveyorSheet = true
}) {
Text("Add Conveyor")
}
}
.navigationBarTitle("Conveyors", displayMode: .inline)
.sheet(isPresented: $showAddConveyorSheet) {
VStack {
HStack {
TextField("Conveyor Name", text: $conveyorName)
TextField("Master Tag", text: $masterTag)
.onTapGesture {
// open camera and scan text here
}
}
Button(action: {
self.conveyors.append(Conveyor(id: self.conveyorName, name: self.conveyorName, masterTag: self.masterTag))
self.conveyorName = ""
self.masterTag = ""
self.showAddConveyorSheet = false
}) {
Text("Save")
}
}
}
}
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(newsurvey: survey)
}
}
I'm building a WatchOS-app (SwiftUI) with multiple pickers, but as soon as I add them to a ScrollView I can no longer simply tap a picker to select it.
When I tap a picker the first picker on the screen gets selected and I have to tap once more to have the right picker selected.
Once I've double tapped the picker I can select other pickers just fine, but as soon as I tap outside to deselect all pickers I have to double tap again.
Sorry if the explanation is a bit fuzzy. This video shows the issue: Video
I'm new to both programming and Swift, so be gentle ;)
import SwiftUI
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ContentView: View {
let paceArray = Array(0...59)
let speedArray = Array(0...99)
#State private var globalSecondsPerKM: Double = 0
#State private var paceMKMHours: Int = 0
#State private var paceMKMMinutes: Int = 0
#State private var paceMKMSeconds: Int = 0
#State private var paceMMHours: Int = 0
#State private var paceMMMinutes: Int = 0
#State private var paceMMSeconds: Int = 0
#State private var speedKMHWhole: Int = 0
#State private var speedKMHDecimal: Int = 0
#FocusState private var paceMKMFocused: Bool
#FocusState private var paceMMFocused: Bool
#FocusState private var speedKMHFocused: Bool
var body: some View {
ScrollView {
VStack {
VStack {
Text("Pace per km")
.font(.headline)
HStack {
Picker(selection: $paceMKMHours, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02dh", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMKMMinutes, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMKMSeconds, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.frame(width: 45)
}
.focused($paceMKMFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
VStack {
Text("Pace per mile")
.font(.headline)
HStack {
Picker(selection: $paceMMHours, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02dh", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMMMinutes, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMMSeconds, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.frame(width: 45)
}
.focused($paceMMFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
VStack {
Text("Speed in km/h")
.font(.headline)
HStack {
Picker(selection: $speedKMHWhole, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02d", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $speedKMHDecimal, label: Text("")) {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: ".%02d", speedArray[index])).tag(index)
}
}
.frame(width: 45)
}
.focused($speedKMHFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
}
}
.labelsHidden()
.font(.system(size: 13))
}
}
This looks like a SwiftUI bug. A possible workaround is setting up a tap gesture on the picker, which triggers a focus change. The initial animation is not perfect, but it looks fine after that.
import SwiftUI
#available(watchOSApplicationExtension 8.0, *)
struct ContentView: View {
let paceArray = Array(0...59)
#State private var paceMKMHours: Int?
#State private var paceMKMMinutes: Int?
#State private var paceMKMSeconds: Int?
#FocusState private var shouldFocusHours: Bool
#FocusState private var shouldFocusMinutes: Bool
#FocusState private var shouldFocusSeconds: Bool
var body: some View {
ScrollView {
VStack {
HStack {
Picker("hours", selection: $paceMKMHours, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dh", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusHours = true
}
.focused($shouldFocusHours)
Picker("minutes", selection: $paceMKMMinutes, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusMinutes = true
}
.focused($shouldFocusMinutes)
Picker("seconds", selection: $paceMKMSeconds, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusSeconds = true
}
.focused($shouldFocusSeconds)
}
.padding(.bottom, 5)
.frame(height: 35)
}
}
.labelsHidden()
.font(.system(size: 13))
}
}
Simple project to be able to understand the concept.
I have two views UpperView and LowerView. The UpperView has a button, when clicked the button calls a ViewModel that fetches data from firebase. My problem is displaying the fetched data in the LowerView. I initialize the ViewModel in the LowerView so that I can access the fetched data through a #Published property but it doesn't work. It's a pretty simple case that I have built in order to understand the concept. Here is the code for UpperView, LowerView and the ViewModel. HomeView is the combination of the UpperView and the LowerView. It feels as if the data is loaded after the LowerView is displayed. All help will be appreciated!!
import Foundation
class MergeViewModel: ObservableObject {
#Published var clients: [Client] = [Client]()
func fetchAllClients() {
COLLECTION_CLIENTS.getDocuments { querySnapshot, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let documents = querySnapshot?.documents else { return }
self.clients = documents.compactMap({ try? $0.data(as: Client.self)})
print(self.clients.count)
}
}
}
import SwiftUI
struct UpperView: View {
#ObservedObject var viewModel = MergeViewModel()
#State var numberOfClients: Int = 0
#State var buttonPressed: Int = 0
#State var clients: [Client] = [Client]()
var body: some View {
ZStack {
Color(.red)
VStack{
Text("This is UPPER VIEW ")
.foregroundColor(.white)
Text("We have \(numberOfClients) of clients!")
Text("Button pressed \(buttonPressed)")
Button(action: {
viewModel.fetchAllClients()
numberOfClients = viewModel.clients.count
buttonPressed += 1
}, label: {
Text("Press")
.frame(width: 100, height: 50)
.background(Color.white.opacity(0.50))
.cornerRadius(10)
})
}
}.ignoresSafeArea()
}
}
struct UpperView_Previews: PreviewProvider {
static var previews: some View {
UpperView()
}
}
import SwiftUI
struct LowerView: View {
#ObservedObject var viewModel = MergeViewModel()
var body: some View {
VStack {
Text("This is LOWER VIEW")
.foregroundColor(.black)
Text("\(viewModel.clients.count)")
.foregroundColor(.black)
List(viewModel.clients) { client in
Text(client.clientName)
.foregroundColor(.black)
}
}
}
}
struct LowerView_Previews: PreviewProvider {
static var previews: some View {
LowerView()
}
}
import SwiftUI
struct HomeView: View {
var body: some View {
NavigationView {
VStack {
UpperView()
LowerView()
Spacer()
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
HStack {
Image("logo_silueta")
.resizable()
.scaledToFit()
.frame(width: 30)
Text("TheJump")
.font(.subheadline)
.foregroundColor(.gray.opacity(0.8))
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
AuthViewModel.shared.signOut()
}, label: {
Text("logout")
})
}
}
}
}
}
Thanks for your input!
Here is how it works correctly:
ViewModel
import Foundation
class MergeViewModel: ObservableObject {
#Published var clients: [Client] = [Client]()
init(){
fetchAllClients()
}
func fetchAllClients() {
COLLECTION_CLIENTS.getDocuments { querySnapshot, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let documents = querySnapshot?.documents else { return }
self.clients = documents.compactMap({ try? $0.data(as: Client.self)})
print(self.clients.count)
}
}
}
UpperView
import SwiftUI
struct UpperView: View {
#ObservedObject var viewModel: MergeViewModel
#State var numberOfClients: Int = 0
#State var buttonPressed: Int = 0
#State var clients: [Client] = [Client]()
var body: some View {
ZStack {
Color(.red)
VStack{
Text("This is UPPER VIEW ")
.foregroundColor(.white)
Text("We have \(numberOfClients) of clients!")
Text("Button pressed \(buttonPressed)")
Button(action: {
viewModel.fetchAllClients()
numberOfClients = viewModel.clients.count
buttonPressed += 1
}, label: {
Text("Press")
.frame(width: 100, height: 50)
.background(Color.white.opacity(0.50))
.cornerRadius(10)
})
}
}.ignoresSafeArea()
}
}
struct UpperView_Previews: PreviewProvider {
static var previews: some View {
UpperView(viewModel: MergeViewModel())
}
}
LowerView
struct LowerView: View {
#ObservedObject var viewModel: MergeViewModel
var body: some View {
VStack {
Text("This is LOWER VIEW")
.foregroundColor(.black)
Text("\(viewModel.clients.count)")
.foregroundColor(.black)
List(viewModel.clients) { client in
Text(client.clientName)
.foregroundColor(.black)
}
}
}
}
struct LowerView_Previews: PreviewProvider {
static var previews: some View {
LowerView(viewModel: MergeViewModel())
}
}
I'm trying to build an demo app by swiftUI that get multi text from user and add them to the list, below , there is an image of app every time user press plus button the AddListView show to the user and there user can add multi text to the List.I have a problem to add them to the list by new switUI data Flow I don't know how to pass data.(I comment more information)
Thanks 🙏
here is my code for AddListView:
import SwiftUI
struct AddListView: View {
#State var numberOfTextFiled = 1
#Binding var showAddListView : Bool
var body: some View {
ZStack {
Title(numberOfTextFiled: $numberOfTextFiled)
VStack {
ScrollView {
ForEach(0 ..< numberOfTextFiled, id: \.self) { item in
PreAddTextField()
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView)
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
AddListView(showAddListView: .constant(false))
}
}
struct PreAddTextField: View {
// I made this standalone struct and use #State to every TextField text be independent
// if i use #Binding to pass data all Texfield have the same text value
#State var textInTextField = ""
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
// What should happen here to add Text to List???
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
#Binding var numberOfTextFiled : Int
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
numberOfTextFiled += 1
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
and for DataModel:
import SwiftUI
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
var textData = [
Text1(text: "SwiftUI"),
Text1(text: "Data flow?"),
]
and finally:
import SwiftUI
struct ListView: View {
#State var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
Because of the multiple-items part of the question, this becomes a lot less trivial. However, using a combination of ObservableObjects and callback functions, definitely doable. Look at the inline comments in the code for explanations about what is going on:
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
//Store the items in an ObservableObject instead of just in #State
class AppState : ObservableObject {
#Published var textData : [Text1] = [.init(text: "Item 1"),.init(text: "Item 2")]
}
//This view model stores data about all of the new items that are going to be added
class AddListViewViewModel : ObservableObject {
#Published var textItemsToAdd : [Text1] = [.init(text: "")] //start with one empty item
//save all of the new items -- don't save anything that is empty
func saveToAppState(appState: AppState) {
appState.textData.append(contentsOf: textItemsToAdd.filter { !$0.text.isEmpty })
}
//these Bindings get used for the TextFields -- they're attached to the item IDs
func bindingForId(id: UUID) -> Binding<String> {
.init { () -> String in
self.textItemsToAdd.first(where: { $0.id == id })?.text ?? ""
} set: { (newValue) in
self.textItemsToAdd = self.textItemsToAdd.map {
guard $0.id == id else {
return $0
}
return .init(id: id, text: newValue)
}
}
}
}
struct AddListView: View {
#Binding var showAddListView : Bool
#ObservedObject var appState : AppState
#StateObject private var viewModel = AddListViewViewModel()
var body: some View {
ZStack {
Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "")) })
VStack {
ScrollView {
ForEach(viewModel.textItemsToAdd, id: \.id) { item in //note this is id: \.id and not \.self
PreAddTextField(textInTextField: viewModel.bindingForId(id: item.id))
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView, save: {
viewModel.saveToAppState(appState: appState)
})
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct PreAddTextField: View {
#Binding var textInTextField : String //this takes a binding to the view model now
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var save : () -> Void //callback function for what happens when "Add" gets pressed
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
save()
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
var addItem : () -> Void //callback function for what happens when the plus button is hit
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
addItem()
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
struct ListView: View {
#StateObject var appState = AppState() //store the AppState here
#State private var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(appState.textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView, appState: appState)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
I am having an issue getting the ContentBodyView to update properly when a button is pressed in the MenuView.
When I print out the #Published currentPage var, it changes on button press, but doesn't appear to get carried across into ContentBodyView. Am I missing something?
I wrote the code based off of this tutorial: https://blckbirds.com/post/how-to-navigate-between-views-in-swiftui-by-using-an-environmentobject/
I've tried doing an .onReceive event on ContentBodyView, but the view still did not change.
App.swift:
#main
struct App: App {
#StateObject var viewRouter = ViewRouter()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(viewRouter)
}
}
}
ContentView:
struct ContentView: View {
// MARK: - PROPERTIES
// for future use...
#State var width = UIScreen.main.bounds.width - 90
// to hide view...
#State var x = -UIScreen.main.bounds.width + 90
#EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
VStack {
ZStack(alignment: Alignment(horizontal: .leading, vertical: .top)) {
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
.shadow(color: Color.black.opacity(x != 0 ? 0.1 : 0), radius: 5, x: 5, y: 0)
.offset(x: x)
.background(Color.black.opacity(x == 0 ? 0.5 : 0).ignoresSafeArea(.all, edges: .vertical).onTapGesture {
// hiding the view when back is pressed...
withAnimation {
x = -width
}
}) //: background
.environmentObject(ViewRouter())
} //: ZSTACK
// adding gesture or drag feature...
.gesture(DragGesture().onChanged({ (value) in
withAnimation {
if value.translation.width > 0 {
// disabling over drag...
if x < 0 {
x = -width + value.translation.width
}
} else {
if x != -width {
x = value.translation.width
}
}
}
}).onEnded({ (value) in
withAnimation {
// checking if half the value of menu is dragged means setting x to 0...
if -x < width / 2 {
x = 0
} else {
x = -width
}
}
})) //: GESTURE
} //: VSTACK
}
}
MenuView:
struct MenuView: View {
// MARK: - PROPERTIES
var edges = UIApplication.shared.windows.first?.safeAreaInsets
// for future use...
#State var width = UIScreen.main.bounds.width - 90
// to hide view...
#Binding var x: CGFloat
#EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
HStack(spacing: 0) {
VStack(alignment: .leading) {
HStack{
Button(action: {
withAnimation {
x = -width
}
}) {
Image(systemName: "xmark")
.resizable()
.frame(width: 18, height: 18)
.padding()
.padding(.top, 25)
.foregroundColor(Color.black)
}
Spacer()
}
ForEach(menuData) { item in
Button(action: {
withAnimation {
if (item.router == "shareables") {
viewRouter.currentPage = .shareables
} else {
viewRouter.currentPage = .home
}
x = -width
}
}) {
Text("\(item.label)")
} //: BUTTON
} //: FOREACH
} //: VSTACK
.padding(.horizontal,20)
// since vertical edges are ignored....
.padding(.top,edges!.top == 0 ? 15 : edges?.top)
.padding(.bottom,edges!.bottom == 0 ? 15 : edges?.bottom)
// default width...
.frame(width: UIScreen.main.bounds.width - 90)
.background(Color.white)
.ignoresSafeArea(.all, edges: .vertical)
Spacer(minLength: 0)
} //: HSTACK
}
}
ContentBodyView:
struct ContentBodyView: View {
// MARK: - PROPERTIES
#EnvironmentObject var viewRouter: ViewRouter
#Binding var x : CGFloat
// MARK: - BODY
var body: some View{
VStack {
switch viewRouter.currentPage {
case .home:
NavigationBarView(x: $x, title: "Home")
Spacer()
HomeView()
.transition(.scale)
case .shareables:
NavigationBarView(x: $x, title: "Shareables")
Spacer()
ShareablesView()
.transition(.scale)
} //: SWITCH
} //: VSTACK
// for drag gesture...
.contentShape(Rectangle())
.background(Color.white)
}
}
ViewRouter:
final class ViewRouter: ObservableObject {
#Published var currentPage: Page = .home
}
enum Page {
case home
case shareables
}
Try using the same ViewRouter instance in all views.
The following code creates new instances of ViewRouter:
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
...
.environmentObject(ViewRouter())
Replace them with:
#EnvironmentObject var viewRouter: ViewRouter
...
.environmentObject(viewRouter)
But in reality you usually need to inject .environmentObject only once per environment, so all these calls may be unnecessary.
Injecting the ViewRouter once in ContentView should be enough (if you're not using sheet etc):
#StateObject var viewRouter = ViewRouter()
...
ContentView().environmentObject(viewRouter)