Same here. The fact that the method and parameter names are prefixed with an underscore suggests that this protocol is not for public consumption. Wouldn't it be nice if someone from Apple development team could respond to this request and explain how to use this protocol or explain why we shouldn't use it. None of the provided styles is a good fit for some situations. The only workaround I can see is to create a custom view from scratch which is a bit of a sledgehammer to cratck this particular nut!
Post
Replies
Boosts
Views
Activity
FIXED IT!!!
I changed the ForEach loop to iterate through the array indices rather than the array itself. The following code now works successfully (using iOS 16 version of NavigationStack/NavigationLink). I assume, but haven't tested, that the same modification in the iOS 15 version would also have worked.
List {
ForEach(0..<vm.allCourses.count, id: \.self) { idx in
if let course = vm.allCourses[idx] {
NavigationLink(value: course.id) {
courseListItem(course)
}
}
}
.onDelete { indices in
coursesToBeDeleted = indices.map{ index in
vm.allCourses[index]
}
showDeleteConfirmationAlert.toggle()
}
}
...
Thanks @DelawareMathGuy for your contribution. :-D
Further code posted in response to @DelawareMathGuy's comment on my original post:
struct CourseEditView: View {
@Environment(\.colorScheme) var colorScheme
var backgroundColor: Color {
Color(uiColor: (colorScheme == .dark ? .systemBackground : .secondarySystemBackground))
}
enum Field: Hashable {
case name
}
@ObservedObject var vm: CourseEditViewModel
@FocusState private var nameFieldFocused: Field?
init(isPreview: Bool, course: Course? = nil) {
self.vm = CourseEditViewModel(manager: isPreview ? PersistenceManager.preview : PersistenceManager.shared, course: course)
}
var body: some View {
VStack {
Form {
HStack {
LabelledTextField(value: $vm.name, label: "Course name")
.focused($nameFieldFocused, equals: .name)
if !vm.isValid {
Image(systemName: "exclamationmark.triangle.fill").foregroundColor(.yellow)
}
}
Section {
List {
ForEach(vm.cards, id: \.teeColour) { card in
NavigationLink(destination: {
CardDetailsView(viewModel: CardDetailsViewModel(manager: vm.persistenceManager, course: vm.course!, card: card))
}, label: {
CardLinkView(card: card)
})
}
// .onDelete(perform: deleteCards)
}
} header: {
HStack {
if vm.isValid {
Text("CARDS")
Spacer()
NavigationLink {
CardDetailsView(viewModel: CardDetailsViewModel(manager: vm.persistenceManager, course: vm.course!))
} label: {
HStack(spacing: 4) {
Image(systemName: "plus")
.font(.title2)
Text("Add card")
}
.foregroundColor(.accentColor)
}
}
}
}
}
.padding(.top)
.navigationTitle("Add/edit Course")
.onAppear {
if !vm.isValid {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
nameFieldFocused = .name
}
}
vm.refresh()
}
}
.background(backgroundColor)
}
}
class CourseEditViewModel: ObservableObject {
var course: Course?
@Published var name: String = "" {
didSet {
isValid = !name.isEmpty
if isValid {
saveChanges()
}
}
}
@Published var isValid: Bool = false
var cards: [Card] {
course?.courseCards.sorted(by: { $0.teeColourOrder < $1.teeColourOrder }) ?? []
}
private (set) var persistenceManager: PersistenceManager
private var isInitialising: Bool = true
init(manager: PersistenceManager, course: Course? = nil) {
self.persistenceManager = manager
if course != nil {
self.course = course
self.name = course?.name ?? "Unknown"
}
isInitialising = false
}
func refresh() {
objectWillChange.send()
}
func saveChanges() {
guard !isInitialising else { return }
var saved = false
if let course = self.course {
course.name = name
saved = persistenceManager.applyChanges()
} else {
let course = persistenceManager.courseRepository.new { course in
course.id = UUID()
course.name = self.name
}
saved = persistenceManager.applyChanges()
if saved {
self.course = course
}
}
if !saved {
persistenceManager.rollback()
}
}
}
Further to my post above, I tried a simpler view to test the delete logic which worked fine with no errors. This would suggest that the problem is somewhere in the original view coding.
import SwiftUI
struct CourseListForDeleteTestView: View {
@ObservedObject var vm: CourseListViewModel
@State private var showDeleteConfirmationAlert = false
init(isPreview: Bool = false) {
self.vm = CourseListViewModel(manager: isPreview ? PersistenceManager.preview : PersistenceManager.shared)
}
var body: some View {
VStack {
List {
ForEach(vm.allCourses) { course in
Text(course.name)
}
.onDelete { indices in
if let idx = indices.first {
if vm.deleteCourses(courses: [vm.allCourses[idx]]) {
showDeleteConfirmationAlert.toggle()
vm.refresh()
}
}
}
}
}
.alert("Course deleted", isPresented: $showDeleteConfirmationAlert) {
Button(role: .cancel) {
} label: {
Text("OK")
}
}
}
}
Your wish has been granted!
Text("Tap Me!").gesture(
TapGesture(count: 2)
.onEnded({ print("Tapped Twice!”)})
.exclusively(before:
TapGesture()
.onEnded({print("Tapped Once!”) })
)
)