Meditations has a one to many relationship with stages and I have sorted the Meditations based on an order property, which I change by moving them around when I tap edit and this works. Now I want to do the same with the stages in the detail view of the meditations.
I tried to do a new FetchRequest for the DetailView so that I could use the same method as with meditations, but I couldn't make it specific for the particular meditation (and this might not be the best approach if a new FetchRequest is avoidable I think).
I also tried to sort the list items with .sorted, but then it didn't update right away when the order was changed in edit mode and you had to go back and forth to see the changes.
Is there a way to make one of these approaches work? I am also open to other solutions of course.
This is the view for the meditations:
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Meditation.order, ascending: true)])
private var meditations:FetchedResults<Meditation>
@State private var add = false
var body: some View {
NavigationView {
List {
ForEach(meditations){meditation in
NavigationLink(destination: {
DetailView(meditation: meditation)
}, label: {
Text(meditation.title ?? "")
Text("\(meditation.order)")
})
}
.onDelete(perform: deleteMeditation)
.onMove(perform: moveItem)
}
.navigationTitle("Meditations")
.sheet(isPresented: $add){
AddView()
}
.toolbar {
ToolbarItem(placement:.navigationBarTrailing){
EditButton()
}
ToolbarItem(placement:.navigationBarTrailing){
Button(action: {
add.toggle()
}, label: {
Label("Add Meditation",systemImage: "plus")
})
}
}
}
}
private func deleteMeditation(at offset:IndexSet){
withAnimation {
for index in offset{
let meditationToDelete = meditations[index]
do{
viewContext.delete(meditationToDelete)
try viewContext.save()
}catch{
print("Error while deleting Meditation \(error.localizedDescription)")
}
}
}
}
private func moveItem(at sets:IndexSet,destination:Int) {
let itemToMove = sets.first!
print (itemToMove)
print (destination)
if itemToMove < destination {
var startIndex = itemToMove 1
let endIndex = destination - 1
print (startIndex)
print (endIndex)
var startOrder = meditations[itemToMove].order
while startIndex <= endIndex {
meditations[startIndex].order = startOrder
startOrder = startOrder 1
startIndex = startIndex 1
}
meditations[itemToMove].order = startOrder
} else if destination < itemToMove {
var startIndex = destination
let endIndex = itemToMove - 1
var startOrder = meditations[destination].order 1
let newOrder = meditations[destination].order
while startIndex <= endIndex {
meditations[startIndex].order = startOrder
startOrder = startOrder 1
startIndex = startIndex 1
}
meditations[itemToMove].order = newOrder
}
do {
try viewContext.save()
}
catch {
print(error.localizedDescription)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This is the detailview where the stages are shown:
import SwiftUI
struct DetailView: View {
let meditation : Meditation
@Environment(\.managedObjectContext) var viewContext
@Environment(\.dismiss) var dismiss
@State private var addStage = false
@State private var showingDeleteAlert = false
@State private var isPresented = false
var body: some View {
VStack {
Text(meditation.wrappedDetails)
List{
ForEach(meditation.meditationStage){meditationStage in
MeditationStageDetailCell(meditationStage: meditationStage)
}
.onMove(perform: moveStage)
.onDelete(perform: deleteStage)
}
Spacer()
Button ("Begin"){
isPresented.toggle()
}
.fullScreenCover(isPresented: $isPresented) {
MeditatingView(meditation: meditation)
}
Spacer()
}
.navigationTitle(meditation.wrappedTitle)
.navigationBarTitleDisplayMode(.inline)
.alert("Delete Meditation?", isPresented: $showingDeleteAlert) {
Button ("Delete", role: .destructive, action: deleteMeditation)
Button ("Cancel", role: .cancel) {}
} message: {
Text("This action can't be undone")
}
.toolbar{
ToolbarItem(placement:.navigationBarTrailing){
Button {
showingDeleteAlert = true
} label: {
Label ("Delete this Stage", systemImage: "trash")
}
}
ToolbarItem(placement:.navigationBarTrailing){
EditButton()
}
ToolbarItem(placement:.navigationBarTrailing){
Button(action: {
addStage.toggle()
}, label: {
Label("Add Stage",systemImage: "plus")
})
}
}
.sheet(isPresented: $addStage){
AddMeditationStageView(meditation: meditation)
}
}
private func deleteMeditation() {
viewContext.delete(meditation)
try? viewContext.save()
dismiss()
}
private func deleteStage(at offset:IndexSet){
for index in offset{
let stageToDelete = meditation.meditationStage[index]
do{
viewContext.delete(stageToDelete)
meditation.stages -= 1
try viewContext.save()
} catch {
print("Error while deleting Meditation \(error.localizedDescription)")
}
}
}
private func moveStage(at sets:IndexSet,destination:Int) {
let itemToMove = sets.first!
print (itemToMove)
print (destination)
if itemToMove < destination {
var startIndex = itemToMove 1
let endIndex = destination - 1
print (startIndex)
print (endIndex)
var startOrder = meditation.meditationStage[itemToMove].order
print(startOrder)
while startIndex <= endIndex {
meditation.meditationStage[startIndex].order = startOrder
startOrder = startOrder 1
startIndex = startIndex 1
}
meditation.meditationStage[itemToMove].order = startOrder
} else if destination < itemToMove {
var startIndex = destination
let endIndex = itemToMove - 1
var startOrder = meditation.meditationStage[destination].order 1
let newOrder = meditation.meditationStage[destination].order
while startIndex <= endIndex {
meditation.meditationStage[startIndex].order = startOrder
startOrder = startOrder 1
startIndex = startIndex 1
}
meditation.meditationStage[itemToMove].order = newOrder
}
do {
try viewContext.save()
}
catch {
print(error.localizedDescription)
}
}
}
CodePudding user response:
@FetchRequest
private var stages: FetchedResults<MeditationStage>
init(meditation: Meditation) {
let sortDescriptors = [SortDescriptor(\MeditationStage.timestamp]
_stages = FetchRequest(sortDescriptors: sortDescriptors, predicate: NSPredicate(format: "meditation = %@", meditation), animation: .default)
}