How to close sheet view with buttons - ios

I'm currently running into a problem with a sheet not closing when the close button is pushed or the save button has completed transferring the data from the form to the list in homeview. Below is the code for a custom tab bar that when "New Survey" is pushed it will open the formView. The formView has two buttons closed and save. I would like it that when either one of these buttons are selected that it closes the formView.
struct FormView: View {
#Binding var surveys: [Survey]
#State var customerName = ""
#State var city = ""
#State var state = ""
#State var lineName = ""
#State var date = Date()
#Binding var isPresented: Bool
#State var showingFormView = false
var body: some View {
VStack{
ZStack {
NavigationView {
VStack {
ZStack {
Form {
Section(header: Text("Survey Info").font(.custom("Roboto-Light", size: 24)).foregroundColor(Color(red: 0.00, green: 0.188, blue: 0.42)).offset(x: -20)) { TextField("Customer Name", text: $customerName)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("Customer City", text: $city)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("State", text: $state)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("Line Name", text: $lineName)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
DatePicker("Date", selection: $date, displayedComponents: .date)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
}
}
VStack (alignment: .center){
Spacer()
HStack {
**Button(action: {
self.showingFormView = false
self.isPresented = false
}, label: {
Text("Close")
.font(.custom("Roboto-Light", size: 23))
.foregroundColor(Color.white)
.frame(width: 150.0, height: 50.0, alignment: .center)
.background(Color(hue: 0.001, saturation: 0.768, brightness: 0.975))
.cornerRadius(3)
})
Button(action: {
// Save survey data to UserDefaults or any other means
let survey = Survey(customerName: customerName, city: city, state: state, lineName: lineName, date: date)
self.surveys.append(survey)
// Clear text fields
self.customerName = ""
self.city = ""
self.state = ""
self.lineName = ""
self.date = Date()
// Close FormView
self.showingFormView = false
self.isPresented = false
}, label: {
Text("Save")
.font(.custom("Roboto-Light", size: 23))
.foregroundColor(Color.white)
.frame(width: 150.0, height: 50.0, alignment: .center)
.background(Color(red: 0.451, green: 0.729, blue: 0.251))
.cornerRadius(3)
})**
}
}
}
}
.navigationBarHidden(true)
}
}
}
}
}
Custom Tab Bar
struct CustomTabBar: View {
#Binding var surveys: [Survey]
#Binding var isPresented : Bool
#State var showingFormView = false
var body: some View {
NavigationView{
VStack {
Spacer()
HStack (alignment: .bottom) {
Button {
} label: {
GeometryReader { geo in
VStack (alignment: .center, spacing: 4) {
Image(systemName: "magnifyingglass")
.resizable()
.scaledToFit()
.frame(width: 32, height: 32)
Text("Search")
.font(.custom("Roboto-Light", size: 14))
}
.frame(width: geo.size.width,height: geo.size.height)
}
}
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
**Button(action: {
showingFormView = true
}) {
GeometryReader { geo in
VStack(alignment: .center, spacing: 4) {
Image("PSIcon")
.resizable()
.scaledToFit()
.frame(width: 38, height: 38)
Text("New Survey")
.font(.custom("Roboto-Light", size: 14))
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
}
.frame(width: geo.size.width, height: geo.size.height)
}
}
.sheet(isPresented: $showingFormView) {
FormView(surveys: $surveys, isPresented: $isPresented) }
//code allows you to call up a specific view so long as a var is indicated
**
Button {
} label: {
GeometryReader { geo in
VStack (alignment: .center, spacing: 4) {
Image(systemName: "arrow.up.arrow.down")
.resizable()
.scaledToFit()
.frame(width: 35, height: 35)
Text("Sort")
.font(.custom("Roboto-Light", size: 14))
}
.frame(width: geo.size.width,height: geo.size.height)
}
}
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
}
.frame(height: 50.0)
}
}
}
}
The save button saves and creates a new item in the dynamic list but it does not close the formView. The only way for me to close the view is by swiping down on the screen.

Related

Reload body items once data from service layer loads in swiftUI

I'm new to the swiftUI, looking for a way to refresh view once service data retrieved. What I want is, once page loads, -or before page loads- I want to call fillUI function so I can retrieve parsed json data from service layer, with that data I want to refresh this code block so my foreach block works with data I have:
VStack(spacing:15 ) {
ScrollView(.horizontal) {
VStack {
ForEach(0..<5) { i in
MeetingCellView(meetingData: meetingListData?.get(at: i) )
.frame(width: 340, height: 150)
.padding(.horizontal)
}.frame(width: 350)
}
}.frame(width: 350)
}
In UIKit, i was able to solve these issues with a way like loading and retrieving all the data first, then showing the view itself with a data. For swiftUI what is the best approach to do that?
Here is the full code of my related view body. I had searched internet but couldn't find the one yet.
import SwiftUI
struct HomeView: View {
#State var username: String = ""
#State var meetingCode: String = ""
#State var data: GetUserDetailModel? = nil
#State var meetingListData: [Datum]? = nil
#State var requestLoaded = false
var body: some View {
ZStack{
VStack {
Image("home.background")
.resizable()
.frame(maxHeight: 338)
Spacer()
ZStack {
VStack {
RoundedRectangle(cornerRadius: 8)
.padding(.top, -20)
.foregroundColor(.white)
}
ScrollView(.vertical, showsIndicators: false)
{
//MARK: - ScrollView Start
VStack {
HStack(spacing: 13.0) {
NavigationLink(destination: CreateMeetingView()) {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color(red: 0.935, green: 0.914, blue: 0.957))
VStack(spacing: 18) {
HStack {
Image("icon.camera")
Spacer()
}
HStack {
Text("New Meeting")
.foregroundColor(Color(red: 0.476, green: 0.228, blue: 0.701))
.font(.custom("inter-semibold", size: 15))
Spacer()
}
}.padding(.horizontal, 15)
}.frame(maxWidth: .infinity, idealHeight: 101)
.padding(.leading, 25)
}
NavigationLink(destination: Text("there"))
{
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color(red: 0.897, green: 0.939, blue: 0.978))
VStack(spacing: 18) {
HStack {
Image("icon.calendar")
Spacer()
}
HStack {
Text("Schedule Now")
.foregroundColor(Color(red: 0.038, green: 0.525, blue: 0.917))
.font(.custom("inter-semibold", size: 15))
Spacer()
}
}.padding(.horizontal, 15)
}.frame(maxWidth: .infinity, idealHeight: 101)
.padding(.trailing, 25)
}
}
}
VStack {
HStack {
Text("Upcoming Meetings")
.font(.custom("inter-semibold", size: 17))
.padding(.leading, 25)
Spacer()
}
ZStack {
VStack(spacing: 30) {
HStack {
Image("upcoming.calendar")
}
HStack {
Text("You do not have a upcoming meeting.")
.font(.custom("inter-regular", size: 15))
.foregroundColor(Color(red: 0.692, green: 0.692, blue: 0.692))
}
}.hidden()
.padding(.vertical, 46)
VStack(spacing:15 ) {
ScrollView(.horizontal) {
VStack {
ForEach(0..<5) { i in
MeetingCellView(meetingData: meetingListData?.get(at: i) )
.frame(width: 340, height: 150)
.padding(.horizontal)
}.frame(width: 350)
}
}.frame(width: 350)
}
}
.padding(.horizontal ,15)
}.padding(.top, 25)
.padding(.bottom, 100)
//MARK: ScrollView End
}
}
}.ignoresSafeArea()
VStack {
HStack {
Image("splash.logo")
.resizable()
.frame(maxWidth: 100, maxHeight: 20)
.padding(.leading, 24)
Spacer()
Image("icon.profile")
.padding(.trailing,25)
}
VStack(spacing: 10.0) {
HStack {
Text("Good Morning,")
.font(.custom("inter-semibold", size: 13))
.foregroundColor(.white)
Spacer()
}
HStack {
Text(username)
.font(.custom("inter-semibold", size: 22))
.foregroundColor(.white)
Spacer()
}
}.padding(.leading, 24)
ZStack {
Color.white
VStack(spacing: 10.0) {
HStack {
Text("Join Meeting")
.font(.custom("inter-semibold", size: 13))
Spacer()
}
HStack {
TextField("Enter meeting code", text: $meetingCode)
.font(.custom("inter-regular", size: 15))
.keyboardType(.numbersAndPunctuation)
}
}.padding(.horizontal, 15)
}
.cornerRadius(6)
.frame(maxWidth: .infinity, maxHeight: 75.0)
.padding(.horizontal, 25.0)
.padding(.top, 30)
Spacer()
}.hiddenNavigationBarStyle()
}
.onAppear(perform: fillUI)
}
func fillUI() {
username = data?.givenName ?? "error"
Network.meetingList {meetings in
//print(meetings)
meetingListData = meetings?.data
requestLoaded = true
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
We use task(priority:_:) for that, .e.g
List {
ForEach(meetings) { meeting in
MeetingView(meeting: meeting)
}
}
.navigationTitle(loaded ? "Meetings" : "Loading...")
.task {
loaded = false
meetings = await Meeting.fetchAll()
loaded = true
}
Also your body is far too large and you have too many #State. Break it up into subviews and try to only have 1 or 2 #State per View.

Why ViewModel function is throwing error in SwiftUI?

I am trying to call ViewModel method in the view but getting error as Thread 1: Fatal error: No ObservableObject of type AuthViewModel found. A View.environmentObject(_:) for AuthViewModel may be missing as an ancestor of this view. I don't have any idea why it is happening.
AuthViewModel
import SwiftUI
class AuthViewModel: ObservableObject {
func login() {
print("Hello")
}
}
My View File
//
// RegistrationView.swift
// TwitterSwiftUI
//
// Created by developer on 11/06/21.
//
import SwiftUI
struct RegistrationView: View {
#State var text = ""
#State var fullName = ""
#State var userName = ""
#State var password = ""
#State var isImagePickerDisplay = false
#State var selectedImage:UIImage?
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#EnvironmentObject var viewModel: AuthViewModel
var body: some View {
ZStack {
Color(#colorLiteral(red: 0.2203325033, green: 0.6315936446, blue: 0.955042541, alpha: 1))
.ignoresSafeArea()
VStack {
Button(action: {
isImagePickerDisplay = true
}, label: {
if selectedImage != nil {
Image(uiImage: selectedImage!)
.resizable()
.scaledToFit()
.frame(width: 140, height: 140)
.padding(.top, 0)
} else {
Image("plus_photo")
.resizable()
.renderingMode(/*#START_MENU_TOKEN#*/.template/*#END_MENU_TOKEN#*/)
.foregroundColor(.white)
.scaledToFit()
.frame(width: 140, height: 140)
.padding(.top, 0)
}
})
VStack {
CustomTextField(text: $fullName, placeholder: Text("Full name"), icon: Image(systemName: "person"))
.foregroundColor(.white)
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
CustomTextField(text: $fullName, placeholder: Text("Email"), icon: Image(systemName: "envelope"))
.foregroundColor(.white)
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
CustomTextField(text: $userName, placeholder: Text("Username"), icon: Image(systemName: "person"))
.foregroundColor(.white)
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
CustomSecureField(password: $password)
.foregroundColor(.white)
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
Spacer()
.frame(height: 30)
GeometryReader { geometry in
Button(action: {
//viewModel.registerUser(email: "userName", password: "password", userName: "userName", fullName: "fullName", profileImage: selectedImage)
viewModel.login()
}, label: {
Text("Sign up")
})
.frame(width: (geometry.size.width*1), height: 50)
.background(Color.white)
.clipShape(Capsule())
.padding(.vertical)
}
}.padding()
Spacer()
HStack {
Text("Already have account? Login in")
.foregroundColor(.white)
}.onTapGesture {
mode.wrappedValue.dismiss()
}.padding(.bottom, 28)
}
}.sheet(isPresented: self.$isImagePickerDisplay) {
ImagePickerViewHelper(selectedImage: self.$selectedImage, sourceType: .photoLibrary)
}
}
}
The error message clearly says that the #EnvironmentObject property wrapper requires the associated object to be added to the environment somewhere on a higher level of the view hierarchy with the environmentObject() modifier.
To access the view model directly you have to use #Stateobject to create an instance of the observed class
#Stateobject var viewModel = AuthViewModel()

SwiftUI - Dismiss a popup and add a button to the current main view , when a button in the popup is clicked

I'm new to swiftUI.
I have a button, on its click I'm displaying a popup view. When close button in the popup is clicked it should dismiss the popupview and add a new button onto the main view. Any help is appreciated. Here's my code
struct SelectLocation: View {
#State private var showPopUp = false
#State private var passCode: String = ""
var body: some View {
ZStack{
VStack{
Button(action: {
withAnimation {
self.showPopUp = true
}
}, label: {
Text("Select Location")
.frame(minWidth: 0, maxWidth: 500)
.padding()
.background(Color.clear)
.foregroundColor(Color.black)
.font(.custom("Open Sans", size: 18))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 2)
)
})
if(self.showPopUp){
popupPassCode()
}
}
}
}
func popupPassCode()-> some View {
VStack{
SecureField("Enter Code", text: $passCode)
.frame(width: 300, height: 50, alignment: .leading)
.cornerRadius(3.0)
.overlay(RoundedRectangle(cornerRadius: 3.0)
.stroke(Color.gray, lineWidth: 1)
)
.padding()
Button(action: {
withAnimation {
self.showPopUp = false
self.showsCorrectOrIncorrect = true
}
}, label: {
Text("Enter")
.frame(width: 150, height: 30)
.padding()
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("Open Sans", size: 28))
})
if showsCorrectOrIncorrect {
btnStart()
}
}
.padding()
.frame(width: 440, height: 300)
.background(Color.white)
.cornerRadius(20)
.shadow(radius: 20 )
}
func btnStart() -> some View {
VStack{
Button(action: {
}, label: {
Text("Start Order")
.frame(width: 150, height: 30)
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("Open Sans", size: 28))
})
}
}
}
When Enter button in the popup is clicked , it should add a new button to the bottom of the main view (means view that has "Select Location" button)
Use a struct instead of function for the start button and pop up and show them when showPopUp and showsCorrectOrIncorrect is true.
struct SelectLocation: View {
#State private var showPopUp = false
#State private var passCode: String = ""
#State var showsCorrectOrIncorrect = false
var body: some View {
ZStack{
VStack{
Button(action: {
withAnimation {
self.showPopUp = true
}
}, label: {
Text("Select Location")
.frame(minWidth: 0, maxWidth: 500)
.padding()
.background(Color.clear)
.foregroundColor(Color.black)
.font(.custom("Open Sans", size: 18))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 2)
)
})
if showPopUp {
PopUpPassCode(passCode: $passCode, showPopUp: $showPopUp, showsCorrectOrIncorrect: $showsCorrectOrIncorrect)
}
if showsCorrectOrIncorrect {
StartButton()
}
}
}
}
}
struct PopUpPassCode: View {
#Binding var passCode: String
#Binding var showPopUp: Bool
#Binding var showsCorrectOrIncorrect: Bool
var body: some View {
VStack{
SecureField("Enter Code", text: $passCode)
.frame(width: 300, height: 50, alignment: .leading)
.cornerRadius(3.0)
.overlay(RoundedRectangle(cornerRadius: 3.0)
.stroke(Color.gray, lineWidth: 1)
)
.padding()
Button(action: {
withAnimation {
self.showPopUp = false
self.showsCorrectOrIncorrect = true
}
}, label: {
Text("Enter")
.frame(width: 150, height: 30)
.padding()
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("Open Sans", size: 28))
})
}
.padding()
.frame(width: 440, height: 300)
.background(Color.white)
.cornerRadius(20)
.shadow(radius: 20 )
}
}
struct StartButton: View {
var body: some View {
VStack{
Button(action: {
}, label: {
Text("Start Order")
.frame(width: 150, height: 30)
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("Open Sans", size: 28))
})
}
}
}

Change View Swift ui

the swift code below represents two views the first that is displayed is home with the login button and the register button, what I have to do is that when the SIGNUP button is clicked the push to the HomeView view which contains the MapView, practically I have to push to the new view with the click on signup, but with the navigationlink this does not show me the view, how do I solve this?
Home.Swift
import SwiftUI
import MapKit
struct HomeView: View {
#State private var userTrackingMode: MapUserTrackingMode = .follow
#State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: 25.7617,
longitude: 80.1918
),
span: MKCoordinateSpan(
latitudeDelta: 10,
longitudeDelta: 10
)
)
var body: some View {
Map(
coordinateRegion: $region,
interactionModes: MapInteractionModes.all,
showsUserLocation: true,
userTrackingMode: $userTrackingMode
)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
ContectView.Swift
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
LinearGradient(gradient: .init(colors: [Color("Color"), Color("Color1"), Color("Color2")]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)
if UIScreen.main.bounds.height > 800 {
Home()
}
else {
ScrollView(.vertical, showsIndicators: false) {
Home()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home: View {
#State var index = 0
#State var showingDetail = false
#State var isModal: Bool = false
func login() {
}
var body: some View {
VStack {
Image("logo")
.resizable()
.frame(width: 200, height: 180)
HStack {
Button(action: {
withAnimation(.spring(response: 0.8, dampingFraction: 0.5, blendDuration: 0.5)) {
self.index = 0
}
}) {
Text("Login")
.foregroundColor(self.index == 0 ? .black : .white)
.fontWeight(.bold)
.padding(.vertical, 10)
.frame(width: (UIScreen.main.bounds.width - 50) / 2).sheet(isPresented: $isModal, content: {
})
}.background(self.index == 0 ? Color.white : Color.clear)
.clipShape(Capsule())
Button(action: {
withAnimation(.spring(response: 0.8, dampingFraction: 0.5, blendDuration: 0.5)) {
self.index = 1
}
}) {
Text("New User")
.foregroundColor(self.index == 1 ? .black : .white)
.fontWeight(.bold)
.padding(.vertical, 10)
.frame(width: (UIScreen.main.bounds.width - 50) / 2)
}.background(self.index == 1 ? Color.white : Color.clear)
.clipShape(Capsule())
}.background(Color.black.opacity(0.1))
.clipShape(Capsule())
.padding(.top, 25)
if self.index == 0 {
Login(mail: "", pass: "", areYouGoingToSecondView: false)
}
else {
SignUp()
}
if self.index == 0 {
Button(action: {
}) {
Text("Forget Password?")
.foregroundColor(.white)
}
.padding(.top, 20)
}
}
.padding()
}
}
struct Login: View {
#State var mail = ""
#State var pass = ""
#State var areYouGoingToSecondView: Bool = false
var body: some View {
VStack {
VStack {
HStack(spacing: 15) {
Image(systemName: "envelope")
.foregroundColor(.black)
TextField("Enter Email Address", text: self.$mail)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Password", text: self.$pass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
}
.padding(.vertical)
.padding(.horizontal, 20)
.padding(.bottom, 40)
.background(Color.white)
.cornerRadius(10)
.padding(.top, 25)
Button(action: {
print("Prova di stampa")
}) {
Text("LOGIN")
.foregroundColor(.white)
.fontWeight(.bold)
.padding(.vertical)
.frame(width: UIScreen.main.bounds.width - 100)
}.background(
LinearGradient(gradient: .init(colors: [Color("Color2"), Color("Color1"), Color("Color")]), startPoint: .leading, endPoint: .trailing)
)
.cornerRadius(8)
.offset(y: -40)
.padding(.bottom, -40)
.shadow(radius: 15)
}
}
}
struct SignUp: View {
#State var mail = ""
#State var pass = ""
#State var checkpass = ""
#State var repass = ""
#State var name = ""
#State var surname = ""
func signup() -> Bool {
print("Sono in signup")
/*let u = User(email: self.mail, name: self.name, password: self.pass, surname: self.surname)
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
u.register(completion: { result in
print("\n Risultato:")
print(result)
}
)
}
)*/
NavigationLink(destination: HomeView()) {
Text("Show Detail View")
}
return true
}
var body: some View {
VStack {
VStack {
HStack(spacing: 15) {
Image(systemName: "envelope")
.foregroundColor(.black)
TextField("Enter Email Address", text: self.$mail)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "person")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
TextField("Enter Name", text: self.$name)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "person")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
TextField("Enter Surname", text: self.$surname)
}.padding(.vertical, 20)
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Password", text: self.$pass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Re-Enter Password", text: self.$checkpass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
}
.padding(.vertical)
.padding(.horizontal, 20)
.padding(.bottom, 40)
.background(Color.white)
.cornerRadius(10)
.padding(.top, 25)
//Button for signup user
Button(action: {
signup()
}) {
Text("SIGNUP")
.foregroundColor(.white)
.fontWeight(.bold)
.padding(.vertical)
.frame(width: UIScreen.main.bounds.width - 100)
}.background(
LinearGradient(gradient: .init(colors: [Color("Color2"), Color("Color1"), Color("Color")]), startPoint: .leading, endPoint: .trailing)
)
.cornerRadius(8)
.offset(y: -40)
.padding(.bottom, -40)
.shadow(radius: 15)
}
}
}

Removing items from a child view generated by For Each loop causes Fatal Error

I hope you are having a more pleasant evening than mine!
So as I mentioned in the title, my for each loop crashes whenever I try to remove an item from the original list with a binding. I did some research and the problem is that for each generates a view with an id but when you delete the item in your child view it can't find the contents and crashes. Returns 'Thread 1: Fatal error: Index out of range'. I can fix the issue by declaring a #State var instead of #Binding, which really works! However, I have more than a delete button in my child view and if I don't use a binding declaration, changes made don't reflect on the main view. I don't wanna give up on neither the delete button nor buttons. Is there way to keep all of them in my childview?
Mainview declarations;
struct ContentView: View {
#ObservedObject var superReminders = SuperReminders()
#State var superReminder = SuperReminder()}
My list View;
List{
ForEach(superReminders.reminderlist.indices, id: \.self) { index in
NavigationLink(destination: DetailedRemView(superReminder : self.$superReminders.reminderlist[index] ).environmentObject(superReminders)) {
squareImageView(superReminder : self.$superReminders.reminderlist[index]).environmentObject(superReminders).environmentObject(superReminders)
}.listRowBackground(Color.clear)
}.onDelete { indexSet in
superReminders.reminderlist.remove(atOffsets: indexSet)}
}
Childview declarations;
import SwiftUI
struct DetailedRemView: View {
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EE, MMM d, YYYY"
return formatter
}
#State public var showingDetail = false
#Environment(\.colorScheme) var colorScheme: ColorScheme
#State private var deleteReminderAlert = false
#EnvironmentObject var superReminders : SuperReminders
#Environment(\.presentationMode) var presentationMode
#Binding var superReminder : SuperReminder
#State private var showDialog = false
#State var animate = false
var body: some View {
VStack{
HStack(alignment: .center){
Text(superReminder.remdate)
.font(.title)
.multilineTextAlignment(.leading)
.padding(.leading)
.frame(minWidth: 100,maxWidth: .infinity, maxHeight: 50)
Spacer()
Button(action: {
self.showDialog.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue)
.frame(width: 80, height: 35)
HStack{
Text("Edit")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "pencil")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.leading)
.alert(isPresented: $showDialog,
TextAlert(title: "Edit reminder title",
message: "Enter a new title or dissmis.", placeholder: superReminder.remdate,
keyboardType: .default) { result in
if let text = result {
if text != "" {
superReminder.remdate = text }
else{}
} else {
}
})
})
.padding(.leading)
}
.frame(minWidth: 100, maxWidth: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/, maxHeight: 50, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(.vertical, -10)
ZStack(alignment: .topTrailing){
Image(superReminder.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 200,maxHeight: .infinity)
.saturation(superReminder.pastreminder ? 0.1 : 1)
.clipShape(Rectangle())
.cornerRadius(10)
.padding(.all)
.pinchToZoom()
HStack{
Text(superReminder.dateactual, formatter: dateFormatter)
.foregroundColor(.white)
.frame(width: 180, height: 30, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.background( superReminder.pastreminder ? Color.gray : Color.lightlygreen)
.cornerRadius(8)
.animation(/*#START_MENU_TOKEN#*/.easeIn/*#END_MENU_TOKEN#*/)
if superReminder.pastreminder == true {
ZStack{
RoundedRectangle(cornerRadius: 8)
.fill(Color.black)
.frame(width: 30, height: 30)
Image(systemName: "moon.zzz")
.foregroundColor(.white)
}.offset(x: animate ? -3 : 0)
.onAppear(perform: {
shake()
})
}
else{}
}
.zIndex(0)
.offset(x: -10, y: 10)
.padding()
}
.zIndex(1)
.shadow(color: Color.gray.opacity(0.4), radius: 3, x: 1, y: 2)
HStack{
Button(action: {
self.showingDetail.toggle()
}){
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.lightlygreen)
.frame(width: 140, height: 40)
HStack{
Text("Reschedule")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "calendar")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
}
.sheet(isPresented: $showingDetail, content :{
remdatepicker(isPresented: self.$showingDetail, superReminder: $superReminder)})
Button(action: {
if superReminder.pastreminder == true {
superReminder.pastreminder = false
}
else if superReminder.pastreminder == false{
superReminder.pastreminder = true
}
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(superReminder.pastreminder == true ? Color.lightlyblue : Color.gray)
.frame(width: 100, height: 40)
HStack{
Text(superReminder.pastreminder == true ? "Activate" : "Silence")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: superReminder.pastreminder == true ? "checkmark.circle" : "moon.zzz")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
Button(action: {
self.deleteReminderAlert.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color(.red))
.frame(width: 40, height: 40)
HStack{
Image(systemName: "trash")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
}.padding(.bottom, 20)
.alert(isPresented: $deleteReminderAlert){
Alert(
title: Text("Are you sure?"),
message: Text("Do you want to delete this reminder?"),
primaryButton: .destructive(Text("Yes"), action: {
superReminders.remove(superReminder: superReminder)
self.presentationMode.wrappedValue.dismiss()
}),
secondaryButton: .cancel(Text("No"))
)
}
}
}
func shake() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation(Animation.default.repeatCount(6).speed(7)){
animate.toggle()}}}
}
Class and List;
import SwiftUI
struct SuperReminder: Identifiable, Codable, Equatable {
var id = UUID()
var remdate = ""
var dateactual = Date.init()
var image = "New1"
var pastreminder = false
}
class SuperReminders: ObservableObject {
#Published var reminderlist: [SuperReminder]
init() {
self.reminderlist = [
]
}
func add(superReminder: SuperReminder) {
reminderlist.append(superReminder)
}
func remove(superReminder: SuperReminder) {
if let index = reminderlist.firstIndex(of: superReminder) {
reminderlist.remove(at: index)
}
}
}
This answer is similar to
Accessing and manipulating array item in an EnvironmentObject
Loop over superReminders.reminderlist since SuperReminder: Identifiable, Codable, Equatable.
ForEach(superReminders.reminderlist) { superReminder in
NavigationLink(destination: DetailedRemView(superReminders: superReminders,
superReminder: superReminder)) {
-----
}
}
In DetailedRemView, do the following:
struct DetailedRemView: View {
#ObservedObject var superReminders : SuperReminders
var superReminder : SuperReminder
// find index of current superReminder
var indexOfReminder: Int? {
superReminders.reminderlist.firstIndex {$0 == superReminder}
}
var body: some View {
// Unwrap indexOfReminder
if let index = indexOfReminder {
VStack {
------
}
}
}
----
}
Use superReminders.reminderlist[index] in DetailRemView whereever you need to update superReminder.
superReminders.reminderlist[index].pastreminder = false

Resources