ManagedObject и ObservedObject - PullRequest
       32

ManagedObject и ObservedObject

0 голосов
/ 14 февраля 2020

Я пытаюсь изучить 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)
                }
            }
        }
    }
}

1 Ответ

0 голосов
/ 15 февраля 2020

Мне удалось решить эту проблему, взяв список упражнений, который отображается в подробном представлении «Тренировки», и поместив его в другое подпредставление. Я передал бы дату тренировки (которая является уникальным идентификатором для каждой тренировки) для подпредставления, а затем использовал бы эту дату как NSPredicate в пределах FetchRequest в подпредставлении, чтобы получить обновленный список упражнений, назначенных для тренировки. Это работает, но наверняка кажется, что должен быть более легкий путь.

...