SwiftUI 试图从列表中删除对象但无法在不可变值上使用变异成员:是一个“让”常量
Posted
技术标签:
【中文标题】SwiftUI 试图从列表中删除对象但无法在不可变值上使用变异成员:是一个“让”常量【英文标题】:SwiftUI trying to remove object from a list but getting Cannot use mutating member on immutable value: is a 'let' constant 【发布时间】:2021-01-29 22:21:58 【问题描述】:我将整个视图发布到Github Gist,因为我认为您需要查看所有内容。问题是在滑动删除时,我正在删除 HealthKit 锻炼,但我无法删除列表中的行。第 132 行是我试图从列表中删除锻炼的地方。取消注释,编译器说“不能在不可变值上使用变异成员:'workoutMonth' 是一个 'let' 常量”,但它是 var
及其 @State
?
import SwiftUI
import HealthKit
struct MonthsWorkoutsListView: View
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var trackerDataStore: TrackerDataStore
@State var workoutMonths = [WorkoutMonth]()
@State private var showingDeleteFailOrSuccessAlert = false
@State private var deleteFailOrSuccessAlert = Alert(title: Text(""))
var body: some View
VStack
if workoutMonths.count == 0
Text("No workouts logged yet. Open the Athlytic App on your Apple Watch to start and save a new workout.")
.multilineTextAlignment(.center)
.padding(.horizontal)
else
List
ForEach(workoutMonths) workoutMonth in
Section(header: Text(getFormattedYearMonth(workoutMonth: workoutMonth)))
ForEach(workoutMonth.trackerWorkouts) workout in
NavigationButton(
action:
trackerDataStore.selectedWorkout = workout //this line is needed so that the image at the top of WorkoutDetailHeaderCard gets updated, otherwise the view loads before the async called and you end up with the image of the last selected workout
trackerDataStore.loadDataForSelectedWorkout(selectedWorkoutToBeUpdated: workout)
,
destination:
WorkoutDetailView().environmentObject(trackerDataStore)
,
workoutRow: WorkoutRow(trackerWorkout: workout)
)
.onDelete (offSets) in
deleteWorkout(offSets: offSets, workoutMonth: workoutMonth)
.alert(isPresented: $showingDeleteFailOrSuccessAlert )
deleteFailOrSuccessAlert
.navigationBarTitle(Text("Workouts"))
.onAppear
workoutMonths = buildWorkoutsIntoYearMonths(workouts: trackerDataStore.workouts)
func buildWorkoutsIntoYearMonths(workouts: [TrackerWorkout]) -> [WorkoutMonth]
var data = [YearMonth: [TrackerWorkout]]()
var tempWorkoutMonths = [WorkoutMonth]()
for workout in workouts
//print("workout UUID = \(workout.uuid) for workout dated \(workout.startDate)")
let yearMonth = YearMonth(date: workout.startDate)
//Build a year month with an empty array of hockeyTracker workouts
var yearMonthWorkouts = data[yearMonth, default: [TrackerWorkout]()]
yearMonthWorkouts.append(workout)
data[yearMonth] = yearMonthWorkouts
//Order YearMonths into newest > oldest
let sortedDataKeys = data.keys.sorted(by: >)
//Build yearMonths into Struct which is needed for SwiftUI
for yearMonth in sortedDataKeys
let newYearMonth = WorkoutMonth(yearMonth: yearMonth, trackerWorkouts: data[yearMonth]!)
tempWorkoutMonths.append(newYearMonth)
return tempWorkoutMonths
func getFormattedYearMonth(workoutMonth: WorkoutMonth) -> String
var monthAsString = ""
switch (workoutMonth.yearMonth.month)
case 1: monthAsString = "January"
case 2: monthAsString = "February"
case 3: monthAsString = "March"
case 4: monthAsString = "April"
case 5: monthAsString = "May"
case 6: monthAsString = "June"
case 7: monthAsString = "July"
case 8: monthAsString = "August"
case 9: monthAsString = "September"
case 10: monthAsString = "October"
case 11: monthAsString = "November"
case 12: monthAsString = "December"
default:
print("Default")
let yearAsString = (workoutMonth.yearMonth.year.description)
let yearWithoutFirst2Char = yearAsString.dropFirst(2)
let formattedSection = "\(monthAsString) '\(yearWithoutFirst2Char)"
return formattedSection
private func deleteWorkout(offSets: IndexSet, workoutMonth: WorkoutMonth)
for offset in offSets
if let unwrappedHKWorkout = workoutMonth.trackerWorkouts[offset].hkWorkout
guard unwrappedHKWorkout.sourceRevision.source.name == "Athlytic" else
print("sourceName isn't Athlytic it is \(unwrappedHKWorkout.sourceRevision.source.name)")
showingDeleteFailOrSuccessAlert.toggle()
deleteFailOrSuccessAlert = Alert(title: Text("Delete Error"), message: Text("Apple only permits us to delete a workout that was logged with the Athlytic Watch App. To delete this workout you need to delete it in the Apple Health or Fitness App."), dismissButton: .default(Text("ok")))
return
HKHealthStore().delete(unwrappedHKWorkout) (success, error) in
DispatchQueue.main.async
if success
print("success workout deleted")
//workoutMonth.trackerWorkouts.remove(at: offset)
showingDeleteFailOrSuccessAlert.toggle()
deleteFailOrSuccessAlert = Alert(title: Text("workout successfully deleted"))
else if let unwrappedError = error
print("error could not delete workout because \(unwrappedError)")
showingDeleteFailOrSuccessAlert.toggle()
deleteFailOrSuccessAlert = Alert(title: Text("Delete Error"), message: Text("We were unable to delete this workout due to \(unwrappedError.localizedDescription)"), dismissButton: .default(Text("ok")))
//struct MonthsWorkoutsList_Previews: PreviewProvider
// static var previews: some View
// MonthsWorkoutsListView()
//
//
struct YearMonth: Comparable, Hashable
let year: Int
let month: Int
init(year: Int, month: Int)
self.year = year
self.month = month
init(date: Date)
let comps = Calendar.current.dateComponents([.year, .month], from: date)
self.year = comps.year!
self.month = comps.month!
static func < (lhs: YearMonth, rhs: YearMonth) -> Bool
if lhs.year != rhs.year
return lhs.year < rhs.year
else
return lhs.month < rhs.month
struct WorkoutMonth: Identifiable
var id = UUID()
var yearMonth: YearMonth
var trackerWorkouts: [TrackerWorkout]
【问题讨论】:
请不要发布指向您的代码的链接。我已更新您的问题以包含您的代码。 【参考方案1】:您正在尝试修改 workoutMonth
,这是一个传入函数的参数,使其默认为 let
变量。
你需要做更多这样的事情(请注意,这是未经测试的,可能不精确,因为你没有提供一个简单的项目来玩,我只是从要点中阅读,但它应该至少让你开始):
self.workoutMonths = self.workoutMonths.map wm in
if wm.id == workoutMonth.id
var copiedMonth = wm
copiedMonth.trackerWorkouts.remove(at: offset)
return copiedMonth
else
return wm
【讨论】:
以上是关于SwiftUI 试图从列表中删除对象但无法在不可变值上使用变异成员:是一个“让”常量的主要内容,如果未能解决你的问题,请参考以下文章
无法在 SwiftUI 中使用 ForEach 和 CoreData 将数据正确传递给模态演示
在 PySpotify 中,试图在不知道曲目在播放列表中的位置的情况下从播放列表中删除曲目