簡體   English   中英

當用戶使用 SwiftUI 進入新視圖時如何刷新核心數據數組?

[英]How to refresh Core Data array when user enters new view with SwiftUI?

我有 3 個視圖。 內容視圖、培訓視圖和培訓列表視圖。 我想列出 Core Data 中的練習,但我也想在不更改數據的情況下進行一些更改。

在內容視圖中; 我正在嘗試使用 CoreData 獲取數據

struct ContentView: View {
    // MARK: - PROPERTY
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Training.timestamp, ascending: false)],
        animation: .default)
    private var trainings: FetchedResults<Training>
    
    @State private var showingAddProgram: Bool = false
    
    // FETCHING DATA
    
    
    // MARK: - FUNCTION
    
    // MARK: - BODY
    
    var body: some View {
        NavigationView {
            Group {
                VStack {
                    HStack {
                        Text("Your Programs")
                        Spacer()
                        Button(action: {
                            self.showingAddProgram.toggle()
                        }) {
                            Image(systemName: "plus")
                        }
                        .sheet(isPresented: $showingAddProgram) {
                            AddProgramView()
                        }
                        
                    } //: HSTACK
                    .padding()
                    List {
                        ForEach(trainings) { training in
                            TrainingListView(training: training)
                        }
                    } //: LIST
                    Spacer()
                } //: VSTACK
            } //: GROUP
            .navigationTitle("Good Morning")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        print("test")
                    }) {
                        Image(systemName: "key")
                    }
                }
            } //: TOOLBAR
            .onAppear() {
                
            }
        } //: NAVIGATION
    }
    
    private func showId(training: Training) {
        guard let id = training.id else { return }
        print(id)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

在培訓視圖中; 我正在以數組列表的形式進行練習,並且正在進入 TrainingListView。

import SwiftUI

struct TrainingView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
    @State var training: Training
    @State var exercises: [Exercise]
    @State var tempExercises: [Exercise] = [Exercise]()
    @State var timeRemaining = 0
    @State var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @State var isTimerOn = false

    var body: some View {
        VStack {
            HStack {
                Text("\(training.name ?? "")")
                Spacer()
                Button(action: {
                    presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Finish")
                }
            }
            .padding()
            
            ZStack {
                Circle()
                    .fill(Color.blue)
                    .frame(width: 250, height: 250)
                Circle()
                    .fill(Color.white)
                    .frame(width: 240, height: 240)
                Text("\(timeRemaining)s")
                    .font(.system(size: 100))
                    .fontWeight(.ultraLight)
                    .onReceive(timer) { _ in
                        if isTimerOn {
                            if timeRemaining > 0 {
                                timeRemaining -= 1
                            } else {
                                isTimerOn.toggle()
                                stopTimer()
                                removeExercise()
                            }
                        }
                    }
            }
            Button(action: {
                startResting()
            }) {
                if isTimerOn {
                    Text("CANCEL")
                } else {
                    Text("GIVE A BREAK")
                }
            }
            Spacer()
            ExerciseListView(exercises: $tempExercises)
        }
        .navigationBarHidden(true)
        .onAppear() {
            updateBigTimer()
        }
    }
    
    private func startResting() {
        tempExercises = exercises
        
        if let currentExercise: Exercise = tempExercises.first {
            timeRemaining = Int(currentExercise.rest)
            startTimer()
            isTimerOn.toggle()
        }
    }
    
    private func removeExercise() {
        if let currentExercise: Exercise = tempExercises.first {
            if Int(currentExercise.rep) == 1 {
                let index = tempExercises.firstIndex(of: currentExercise) ?? 0
                tempExercises.remove(at: index)
            } else if Int(currentExercise.rep) > 1 {
                currentExercise.rep -= 1
                let index = tempExercises.firstIndex(of: currentExercise) ?? 0
                tempExercises.remove(at: index)
                tempExercises.insert(currentExercise, at: index)
            }
            updateBigTimer()
        }
    }
    
    private func updateBigTimer() {
        timeRemaining = Int(tempExercises.first?.rest ?? 0)
    }
    
    private func stopTimer() {
        timer.upstream.connect().cancel()
    }
    
    private func startTimer() {
        timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    }
}

struct TrainingView_Previews: PreviewProvider {
    static var previews: some View {
        TrainingView(training: Training(), exercises: [Exercise]())
    }
}

在培訓列表視圖中; 我列出了所有練習。

struct TrainingListView: View {
    
    @ObservedObject var training: Training
    @Environment(\.managedObjectContext) private var managedObjectContext
    
    var body: some View {
        NavigationLink(destination: TrainingView(training: training, exercises: training.exercises?.toArray() ?? [Exercise]())) {
            HStack {
                Text("\(training.name ?? "")")
                Text("\(training.exercises?.count ?? 0) exercises")
            }
        }
    }
}

另外,我正在添加視頻: https://twitter.com/huseyiniyibas/status/1388571724346793986

我想要做的是,當用戶點擊任何訓練練習列表時,應該刷新。 它應該像一開始一樣再次成為x5。

我很難理解你的問題,但我想我明白了。

我的理解是這樣的:

  1. 您想將代表計數存儲在核心數據中。 (在訓練>練習中)
  2. 當用戶完成練習時,您希望一一倒計時。
  3. 但是您不想更改存儲在核心數據中的原始代表計數。

我沒有運行您的代碼,因為我不想重新創建所有模型和核心數據文件。 我想我已經發現了問題所在。 在這里,我將解釋如何解決它:

核心數據模型是類(引用類型)。 當您傳遞類(就像您在代碼中所做的那樣)並更改它們的屬性時,您會更改原始數據。 在你的情況下,你不想要那個。

(順便說一句,作為引用類型是類的一個非常有用和強大的屬性。結構和枚舉是值類型,即它們在傳遞時被復制。原始數據是不變的。)

您有多種選擇來解決您的問題:

  1. 只需從Exercise生成一個不同的struct (類似於ExerciseDisplay ),然后將ExerciseDisplay傳遞給TrainingView

  2. 您可以編寫Exerciseextension並在將model 傳遞給TrainingView之前“復制”它。 為此,您需要實現NSCopying協議。

extension Exercise: NSCopying {
  
  func copy(with zone: NSZone? = nil) -> Any {
    return Exercise(...)
  }
}

但在此之前,我想您需要將 Codegen 更改為 Manual/None of your entry in your .xcdatamodeld文件。 當您想要手動創建屬性時,這是必需的。 我不完全確定如何為 CoreDate model 實現NSCopying ,但這當然是可行的。


第一種方法更容易但有點丑陋。 第二個更通用和優雅,但也更高級。 只需先嘗試第一種方法,一旦您有信心再嘗試第二種方法。


更新:這簡要介紹了如何實施第一種方法:

struct ExerciseDisplay: Identifiable, Equatable {
    public let id = UUID()
    public let name: String
    public var rep: Int
    public let rest: Int
}

struct TrainingView: View {

    // Other properties and states etc.

    let training: Training
    @State var exercises: [ExerciseDisplay] = []

    init(training: Training) {
        self.training = training
    }

    var body: some View {
        VStack {
        // Views
        }
        .onAppear() {
            let stored: [Exercise] = training.exercises?.toArray() ?? []
            self.exercises = stored.map { ExerciseDisplay(name: $0.name ?? "", rep: Int($0.rep), rest: Int($0.rest)) }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM