I'm trying to show an alert with some text when the user taps on an image view. The image view is part of a foreach loop to build the individual list items.
The button is working and it is triggering changes on the Observable object but the alert isPresented is not being triggered... This is so confusing!
The view is nested inside a few views to simplify the code. I've shared code snippets to illustrate my issue. Hopefully that will make sense to someone and a solution can be proposed.
Here's the containing view:
struct AssignmentsBoardView: View {
@ObservedObject var presenter: AssignmentPresenter
@ObservedObject var dogAlert: BookingNotesAlert = BookingNotesAlert()
init(_ presenter: AssignmentPresenter) {
self.presenter = presenter
}
var body: some View {
VStack {
Spacer()
if (presenter.state == ViewState.loading) {
LoadingIndicator(label: "Fetching collections")
} else if (presenter.state == ViewState.complete) {
VStack {
BoardDriversView(drivers: self.presenter.boardDrivers, currentDriver: self.presenter.selectedDriver, onSelected: self.changeDriver(boardDriver:))
AssignmentTypeHeaders(setCollections: {
self.changeAssignmentState(state: AssignmentState.collections)
}, setDropOffs: {
self.changeAssignmentState(state: AssignmentState.dropOffs)
}, currentAssignmentType: self.presenter.assignmentState)
ScrollView {
AssignmentDataView(presenter: self.presenter, dogAlert: self.dogAlert)
}
}.alert(isPresented: self.$dogAlert.showAlert, content: {
Alert(title: Text("Booking Notes"), message: Text(self.dogAlert.getDogNotes()), dismissButton: .default(Text("Ok")))
})
} else {
Text("No Assignments")
}
Spacer()
}
}
private func changeDriver(boardDriver: BoardDriver) {
self.presenter.changeSelectedDriver(driver: boardDriver)
}
private func changeAssignmentState(state: AssignmentState) {
self.presenter.changeAssignment(newState: state)
}
}
struct AssignmentDataView: View {
@ObservedObject var presenter: AssignmentPresenter
var dogAlert: BookingNotesAlert
@ViewBuilder
var body: some View {
if (self.presenter.assignmentState == AssignmentState.collections) {
if (self.presenter.collections.isEmpty) {
Text("No Collections scheduled for this driver")
} else {
ForEach(self.presenter.collections, id: \.id) { listItem in
CollectionListItem(dog: listItem, dogNotesAlert: self.dogAlert)
}
}
} else {
if (self.presenter.dropOffs.isEmpty) {
Text("No Drop Offs scheduled for this driver")
} else {
ForEach(self.presenter.dropOffs, id: \.id) { listItem in
CollectionListItem(dog: listItem, dogNotesAlert: self.dogAlert)
}
}
}
}
}
This is the Observable Object:
class BookingNotesAlert: ObservableObject {
@Published var showAlert = false
var dog: DogCollectionListItem? = nil
func showAlert(dog: DogCollectionListItem) {
self.dog = dog
self.showAlert.toggle()
print("Toggled stuff")
print("Notes \(self.getDogNotes())")
}
func getDogNotes() -> String {
self.dog?.bookingNotes ?? "No notes provided"
}
}
And this is where I am consuming it:
struct CollectionListItemThumbnail: View {
var dog: DogCollectionListItem
var dogNotesAlert: BookingNotesAlert
var body: some View {
Group {
HStack {
if (!dog.bookingNotes.isEmpty) {
Button(action: {
self.dogNotesAlert.showAlert(dog: dog)
print("Tapping")
}, label: {
ImageView(withUrl: dog.thumbnailUrl, 75, highlight: true)
.padding(.trailing, 12)
.padding(.bottom, 12)
.padding(.top, 12)
})
} else {
ImageView(withUrl: dog.thumbnailUrl, 75)
.padding(.trailing, 12)
.padding(.bottom, 12)
.padding(.top, 12)
}
}
}
}
}
Trying Asperi's suggestion:
struct AssignmentsBoardView: View {
@ObservedObject var presenter: AssignmentPresenter
@State var dogAlert: BookingNotesAlert?
init(_ presenter: AssignmentPresenter) {
self.presenter = presenter
}
var body: some View {
VStack {
Spacer()
if (presenter.state == ViewState.loading) {
LoadingIndicator(label: "Fetching collections")
} else if (presenter.state == ViewState.complete) {
VStack {
BoardDriversView(drivers: self.presenter.boardDrivers, currentDriver: self.presenter.selectedDriver, onSelected: self.changeDriver(boardDriver:))
AssignmentTypeHeaders(setCollections: {
self.changeAssignmentState(state: AssignmentState.collections)
}, setDropOffs: {
self.changeAssignmentState(state: AssignmentState.dropOffs)
}, currentAssignmentType: self.presenter.assignmentState)
Button(action: {
let dog = self.presenter.collections[0]
dog.bookingNotes = "Test"
self.dogAlert = BookingNotesAlert(dog: dog)
}, label: {Text("Show booking notes")})
ScrollView {
AssignmentDataView(presenter: self.presenter, dogAlert: self.$dogAlert)
}
}.alert(item: $dogAlert) { dogAlert in
Alert(title: Text("Booking Notes"), message: Text(dogAlert.getDogNotes()), dismissButton: .default(Text("Ok")))
}
} else {
Text("No Assignments")
}
Spacer()
}
}
}
class BookingNotesAlert: Identifiable {
var dog: DogCollectionListItem? = nil
let id: UUID = UUID()
init(dog: DogCollectionListItem) {
self.dog = dog
print("Item Id >> \(id)")
print("Notes >> \(self.getDogNotes())")
}
func getDogNotes() -> String {
self.dog?.bookingNotes ?? "No notes provided"
}
}
This is the console log from the button tap:
Item Id >> 6D695709-2894-4735-8DF6-81E36E1E8F4D
Notes >> Test
Item Id >> FB69ED3A-D462-4C08-8DD5-764C8A54A7B5
Notes >> Test
Item Id >> 484E0B22-F0BD-477A-8BE2-55F626F02609
Notes >> Test
Item Id >> F99E568F-C5BE-4B35-AEDF-E5A4A2700447
Notes >> Test