Я пытаюсь изучить SwiftUI и базовые данные (вообще не имею опыта со swift или target C) и прорабатываю их, написав приложение для отслеживания тренировок в тренажерном зале.
У меня есть объект «Тренировка», который имеет отношение «один-много» к объекту «Упражнение».
В моем приложении я начинаю со списка Тренировок в виде навигации, полученного из основных данных. Затем я использую навигационную ссылку, чтобы передать выбранный объект тренировки в подробное представление, которое отображает свойства объекта тренировки. Подробное представление включает название тренировки, дату создания и список упражнений, связанных с этой тренировкой. Список упражнений загружается из свойства отношения объекта Workout.
Далее я использую кнопку для передачи объекта Workout в представление листа, содержащее список упражнений. Список упражнений является запросом выборки объекта Exercise, и рядом с каждым упражнением в списке есть галочка, которая имеет отношение к тренировке. Каждая строка в списке является экземпляром другого представления «строки», а вся строка является кнопкой. Пользователь может нажать на строку, чтобы добавить / удалить ее из тренировки.
Пока что все это прекрасно работает. Моя проблема в том, что представление сведений о тренировке не обновляется, когда я использую представление листа со списком упражнений, чтобы внести изменения в отношение тренировки к объекту упражнения. Базовые данные обновляются, но представление сведений не обновляет sh обновленный объект из базовых данных, когда представление таблицы упражнений закрыто.
Если я go вернусь еще на один шаг к родительскому представлению со списком тренировок и снова выберу тот же объект тренировки, теперь представление подробностей обновится с правильным списком упражнений из основных данных.
Я думаю, это потому, что родительский список тренировок использует запрос выборки для получения объектов тренировки, а все дочерние представления просто передают один экземпляр объекта тренировки, но не отслеживают его на предмет изменений. Когда я возвращаюсь к списку тренировок, я полагаю, что запрос на выборку заменяет экземпляр объекта самыми последними данными.
Я хочу, чтобы представление сведений понимало, когда свойство отношения изменяется в представлении таблицы упражнений, а затем обновите список упражнений, который появляется в этом подробном представлении, с этими изменениями.
Как я могу заставить эту работу? Я пробовал @ObservedObject
на объекте Workout, который проходит через иерархию представления, и я пробовал managedObjectContext.refresh(Workout, mergeChanges: true)
, когда нажимали кнопку отклонения в представлении листа, но ничего.
Любые идеи ?
//
// WorkoutDetail.swift
import SwiftUI
struct WorkoutDetail: View {
@Environment (\.managedObjectContext) var moc
@ObservedObject var workout: Workout
@State private var workoutDate: Date
@State private var exerciseArray: [Exercise]
@State private var workoutName: String
@State private var showingAddScreen = false
var fetchRequest: FetchRequest<Workout>
static let setDateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MMMM d, y, h:mm a"
return formatter
}()
init(workout: Workout) {
self.workout = workout
// Define the initial form field values
_workoutName = .init(initialValue: workout.wrappedWorkoutName)
_workoutDate = .init(initialValue: workout.wrappedWorkoutDate)
_exerciseArray = .init(initialValue: workout.workoutExerciseArray)
let workoutPredicate = NSPredicate(format: "workoutName == %@", workout.workoutName!)
fetchRequest = FetchRequest<Workout>(entity: Workout.entity(), sortDescriptors: [], predicate: workoutPredicate)
}
var body: some View {
Form {
Section(header: Text("Workout Name")) {
TextField("Workout Name", text: $workoutName)
Text("Created \(workoutDate, formatter: Self.setDateFormat)")
Button("Save Changes") {
self.workout.setValue(self.workoutName, forKey: "workoutName")
try? self.moc.save()
}
}
Section(header: Text("Exercises")) {
Button(action: {self.showingAddScreen.toggle()}) {
HStack {
Image(systemName: "pencil")
Text("Add/Remove Exercises")}
}
List{
ForEach(exerciseArray, id:\ .self) { exercise in
VStack(alignment: .leading) {
NavigationLink(destination: ExerciseSet(exerciseSetExercise: exercise, exerciseSetWorkout: self.workout)) {
WorkoutExercise(exercise: exercise)
}
}
}
}
}
}.navigationBarTitle("\(workoutName)")
.sheet(isPresented: $showingAddScreen) {
AddExerciseToWorkout(workout: self.workout).environment(\.managedObjectContext, self.moc)}
}
}
//
// AddExerciseToWorkout.swift
import SwiftUI
struct AddExerciseToWorkout: View {
@ObservedObject var workout: Workout
@State private var exerciseArray: [Exercise]
@State private var workoutName: String
var exerciseNameArray: [String] = []
@State var selectedExercises: [String] = []
@FetchRequest(entity: Type.entity(), sortDescriptors: [NSSortDescriptor(key:"typeName", ascending: true)]) var strengthExercises: FetchedResults<Type>
@Environment(\.presentationMode) var presentationMode
init(workout: Workout) {
self.workout = workout
_exerciseArray = .init(initialValue: workout.workoutExerciseArray)
_workoutName = .init(initialValue: workout.wrappedWorkoutName)
}
var body: some View {
VStack {
Text("\(workoutName) exercises:")
.font(.headline)
.padding()
List{
ForEach(strengthExercises, id:\.self) { strengthExercise in
Section(header: Text(strengthExercise.wrappedTypeName)) {
ForEach(strengthExercise.typeExerciseArray, id: \.self) { exercise in
AddExerciseToWorkoutRow(workout: self.workout, exercise: exercise)
}
}
}
}
Button("Done") {self.presentationMode.wrappedValue.dismiss()
}
}
}
}
//
// AddExerciseToWorkoutRow.swift
import SwiftUI
import CoreData
struct AddExerciseToWorkoutRow: View {
@Environment(\.managedObjectContext) var moc
let exercise: Exercise
@ObservedObject var workout: Workout
@State private var exerciseArray: [Exercise]
@State private var isSelected: Bool = false
init(workout: Workout, exercise: Exercise) {
self.workout = workout
self.exercise = exercise
_exerciseArray = .init(initialValue: workout.workoutExerciseArray)
if exerciseArray.contains(exercise) {
_isSelected = .init(initialValue: true)
} else {
_isSelected = .init(initialValue: false)
}
}
var body: some View {
Button(action: {
if self.exerciseArray.contains(self.exercise) {
self.workout.removeFromWorkoutToExercise(self.exercise)
self.isSelected = false
} else {
self.workout.addToWorkoutToExercise(self.exercise)
self.isSelected = true
}
try? self.moc.save()
self.moc.refresh(self.workout, mergeChanges: true)
self.workout.didChangeValue(forKey: "workoutToExercise")
}) {
HStack {
Text(exercise.wrappedExerciseName)
Spacer()
if self.isSelected {
Image(systemName: "checkmark")
.foregroundColor(.green)
}
}
}
}
}