问题
This is third time I've posted this issues and am yet to get a working response.
So I have a fitness app, the user selected one workout before it been displayed as table view, when a cell is selected I want it to show that cell(containing an exercise) as completed by marking it with a checkmark. this works fine but I am struggling with how to save that check mark when the app is terminated and re launched.
Below I have given an example of one of the workout models and the table view controller.
Please can someone try and solve this!!
Thank you.
Josh
Workout model example -
import Foundation
class The600Workout {
var workoutArray = [
Workout(exercise: "Don't forget to warm up before every workout!", completed: false),
Workout(exercise: "Start with little/ no weight and work your way up", completed: false),
Workout(exercise: "------------------------------------------------------------------", completed: false),
Workout(exercise: "Pull ups | 25 Reps", completed: false),
Workout(exercise: "Lunges | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Calf Raises | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Shoulder press | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Push ups | 50 Reps", completed: false),
Workout(exercise: "Shrugs | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Leg raises | 50 Reps", completed: false),
Workout(exercise: "Bench press | 50 Reps (Low weight)", completed: false),
Workout(exercise: "More Pull ups | 25 Reps", completed: false),
Workout(exercise: "Squats | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Incline Bench press | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Bicep curls | 50 Reps (Low weight)", completed: false),
Workout(exercise: "Tricep pull downs | 50 Reps (Low weight)", completed: false),
]
}
Table View Controller
import UIKit
class workoutTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var workoutTableView: UITableView!
var navTitle: String = ""
var workout = [Workout]()
let tlabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
setWorkout()
workoutTableView.delegate = self
workoutTableView.dataSource = self
tlabel.text = navTitle
tlabel.textAlignment = .center
tlabel.font = UIFont(name: "Arial Rounded MT Bold", size: 30)
tlabel.adjustsFontSizeToFitWidth = true
navigationItem.titleView = tlabel
}
func setWorkout() {
if navTitle == "The 600 Workout" {
workout = The600Workout().workoutArray
}
else if navTitle == "5 Days for Muscle" {
workout = FiveDaysForMuscle().workoutArray
}
else if navTitle == "Marathon Ready" {
workout = MarathonReady().workoutArray
}
else if navTitle == "HIIT @ Home" {
workout = HIITAtHome().workoutArray
}
else if navTitle == "Get Strong" {
workout = GetStrong().workoutArray
}
else if navTitle == "Body Weight Blast" {
workout = BodyWeightBlast().workoutArray
}
else if navTitle == "Bands Pump" {
workout = BandsPump().workoutArray
}
else if navTitle == "Quickie Warm up" {
workout = QuickieWarmUp().workoutArray
}
else if navTitle == "The Best Circuit Workout" {
workout = TheBestCircuit().workoutArray
}
else if navTitle == "The Gym HIIT Workout" {
workout = GymHIIT().workoutArray
}
else if navTitle == "The Ultimate Workout" {
workout = UltimateWorkout().workoutArray
}
else if navTitle == "Warm up For Weights" {
workout = WarmUpForWeights().workoutArray
}
else if navTitle == "6 Day Bro Split" {
workout = SixDayBroSplit().workoutArray
}
else if navTitle == "Explosive Workout" {
workout = ExplosiveWorkout().workoutArray
}
else if navTitle == "Strength Circuit" {
workout = StrengthCircuit().workoutArray
}
else if navTitle == "Killer Circuit" {
workout = KillerCircuit().workoutArray
}
else if navTitle == "Fitness Test" {
workout = FitnessTest().workoutArray
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workout.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
workout[indexPath.row].completed = !workout[indexPath.row].completed
tableView.cellForRow(at: indexPath)?.accessoryType = workout[indexPath.row].completed ? .checkmark : .none
tableView.deselectRow(at: indexPath, animated: false)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prototypeCell", for: indexPath)
cell.textLabel?.text = workout[indexPath.row].exercise
cell.accessoryType = workout[indexPath.row].completed ? .checkmark : .none
cell.layer.borderWidth = 5
cell.layer.cornerRadius = 20
cell.layer.borderColor = #colorLiteral(red: 0, green: 0.3285208941, blue: 0.5748849511, alpha: 1)
cell.textLabel?.textColor = UIColor.black
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = .boldSystemFont(ofSize: 15)
return cell
}
}
回答1:
Save cell's current state(isSelected or not) to UserDefault. After relaunch, select the cell automatically according to UserDefaults data. For ex:
var selectedCell = 0 // For declaration
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
workout[indexPath.row].completed = !workout[indexPath.row].completed
tableView.cellForRow(at: indexPath)?.accessoryType = workout[indexPath.row].completed ? .checkmark : .none
tableView.deselectRow(at: indexPath, animated: false)
self.selectedCell = indexPath.row
UserDefaults.standart.set(self.selectedCell, forKey: "selectedCellIndex")
}
And at cellForRowAt:
if indexPath.row == UserDefaults.standart.object(forKey: selectedCellIndex){
//select this cell and whatever you want
} else {
//Other cells
}
Hope it helps...
回答2:
To be able to save the completed state of each exercise separately you have to refactor the data model.
Create a JSON file for the entire model – named for example
WorkoutData.json– and put it into the application bundle. The file contains all workouts and their exercises and will be copied to theDocumentsfolder on first launch or if you plan a reset function. The structure of the JSON file is[{"name":"The600Workout","exercises": [{"title":"Don't forget to warm up before every workout!", "completed": false}, {"title":"Start with little/ no weight and work your way up", "completed": false}, {"title":"------------------------------------------------------------------", "completed": false}, {"title":"Pull ups | 25 Reps", "completed": false}, {"title":"Lunges | 50 Reps (Low weight)", "completed": false}, {"title":"Calf Raises | 50 Reps (Low weight)", "completed": false}, {"title":"Shoulder press | 50 Reps (Low weight)", "completed": false}, {"title":"Push ups | 50 Reps", "completed": false}, {"title":"Shrugs | 50 Reps (Low weight)", "completed": false}, {"title":"Leg raises | 50 Reps", "completed": false}, {"title":"Bench press | 50 Reps (Low weight)", "completed": false}, {"title":"More Pull ups | 25 Reps", "completed": false}, {"title":"Squats | 50 Reps (Low weight)", "completed": false}, {"title":"Incline Bench press | 50 Reps (Low weight)", "completed": false}, {"title":"Bicep curls | 50 Reps (Low weight)", "completed": false}, {"title":"Tricep pull downs | 50 Reps (Low weight)", "completed": false}] }, {"name":"5 Days for Muscle","exercises": [ ... ]}, {"name": ... [ ... ]}, ... ]Create two structs conforming to
Codablewhich match the JSON datastruct Exercise : Codable { let title : String var completed : Bool } struct Workout : Codable { let name : String let exercises : [Exercise] }In
viewDidLoadget the URL to the file in theDocumentsfolder with a computed propertyvar workoutDataURL : URL { return try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("WorkoutData.json") }and check if the file exists. If not copy the file from the bundle to
Documents.- Create two methods to load and save the data with
JSONDecoder/JSONEncoder - Call the
loadmethod inviewDidLoadand reload the table view - Call the
savemethod whenever the user selects a cell to change thecompletedstate. - Rather than the endless
if - else ifexpression you can simply get the workout withworkout = workoutArray.first{$0.name == navTitle}! - Delete all workout classes. They are not needed anymore.
This is a quite simple solution. A more efficient solution is to use a database like Core data. The benefit is you don't need to keep the entire data model in memory.
来源:https://stackoverflow.com/questions/59816277/saving-bool-tableview-checkmark-3rd-time-lucky