问题
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 theDocuments
folder 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
Codable
which match the JSON datastruct Exercise : Codable { let title : String var completed : Bool } struct Workout : Codable { let name : String let exercises : [Exercise] }
In
viewDidLoad
get the URL to the file in theDocuments
folder 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
load
method inviewDidLoad
and reload the table view - Call the
save
method whenever the user selects a cell to change thecompleted
state. - Rather than the endless
if - else if
expression 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