1

Update: I refactored it a bit to use @ObservableObject, but the issue still persists

In my SwiftUI app, I have a LazyVGrid populated with n cards and one New Event button. When I press the New Event button, I have a custom modal overlaying the View to let the user add a new event. However, after dismissing the view (by tapping a Create Event button), the modal disappears however the new event card isn't added to the LazyVGrid. It only appears once I force quit the app and re-open it.

Here is the main ContentView:

struct ContentView: View {
    @State var progress: Double = 0.0
    @State var editMode: Bool = false
    @State var angle: Double = 0
    @State var showModal: Bool = false

    @ObservedObject var model: Model
            
    var columns: [GridItem] = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
    
    func animate(while condition: Bool) {
        self.angle = condition ? -0.6 : 0
        
        withAnimation(Animation.linear(duration: 0.125).repeatForever(while: condition)) {
            self.angle = 0.6
        }
        
        if !condition {
            self.angle = 0
        }
    }
    
    var body: some View {
        ZStack {
            NavigationView {
                LazyVGrid(columns: columns, spacing: 10) {
                    ForEach(model.events, id: \.self) { event in
                        SmallCardView(event: event, angle: $angle)
                    }
                    
                    if !showModal {
                        AddEventButtonView(
                            editMode: $editMode,
                            showModal: $showModal,
                            namespace: namespace
                        )
                    } else {
                        Spacer().frame(height: 100)
                    }
                }
                .onLongPressGesture {
                    if !self.editMode {
                        self.editMode = true
                        animate(while: self.editMode)
                    }
                }
            }
            
            if self.showModal {
                AddEventView(namespace: namespace, events: self.$model.events, showModal: $showModal)
            }
            
        }
    }
}

This is the View for the Add Event button:

struct AddEventButtonView: View {
    @Binding var editMode: Bool
    @Binding var showModal: Bool
    
    var namespace: Namespace.ID

    var body: some View {
        Image(systemName: "calendar.badge.plus")
            .frame(height: 100)
            .frame(maxWidth: .infinity)
            .opacity(self.editMode ? 0.0 : 1.0)
            .onTapGesture {
                withAnimation {
                    self.showModal = true
                }
            }
    }
}

and this is my modal view:

struct AddEventView: View {
    @State var eventName: String = ""
    @State var endDate = Date()
    @State var gradientIndex: Int = 0
    
    @Binding var showModal: Bool
    @Binding var events: [Event]
    
    var namespace: Namespace.ID
    
    init(namespace: Namespace.ID, events: Binding<[Event]>, showModal: Binding<Bool>) {
        self.namespace = namespace
        self._events = events
        self._showModal = showModal
    }
    
    var body: some View {
        VStack(spacing: 30.0) {
            //...
                        
            Button(action: {
                let event = Event(name: eventName, start: .init(), end: endDate, gradientIndex: gradientIndex)
                
                self.events.append(event)
                
                withAnimation {
                    self.showModal = false
                }
            }) {
                RoundedRectangle(cornerRadius: 13)
                    .frame(maxWidth: .infinity)
                    .frame(height: 42)
                    .overlay(
                        Text("Add Event")
                            .foregroundColor(.white)
                            .bold()
                    )
            }
            .disabled(self.eventName.isEmpty)
        }
    }

}

If anyone has any idea of how to update the LazyVGrid to show the new event card when a new event is added that'd be awesome, thanks!

Here's the problem:

enter image description here

Richard Robinson
  • 867
  • 1
  • 11
  • 38
  • The approach can be the same as in [this solution](https://stackoverflow.com/a/62683038/12299030) for LazyVStack – Asperi Jul 04 '20 at 16:51
  • @Asperi That appears to just have the newly added element scroll to the bottom, whereas I just want the element to appear in the Grid, it doesn't have to scroll at all – Richard Robinson Jul 04 '20 at 16:56

1 Answers1

3

It is not clear how you did set up model, but it should be observed to update view, as (scratchy)

struct ContentView: View {
    @State var progress: Double = 0.0
    @State var editMode: Bool = false
    @State var angle: Double = 0
    @State var showModal: Bool = false

    @ObservedObject var model: Model       // << here !!

...

or use @EnvironmentObject pattern with injection via .environmentObject

or for Swift 2.0 use

@StateObject var model: Model = Model.shared
Asperi
  • 228,894
  • 20
  • 464
  • 690