问题
I'm fairly new to swift and developing, I'm looking for some help with a problem I can't get past.
So essentially I have a bunch of custom class's which detail workouts, I use those workouts to populate the table view to show the user a list of exercises in the chosen particular workout.
I want to be able to place a checkmark next to an exercise within the table view once it has been completed, the issue I am having is the checkmark repeats when I scroll, I have now removed it for new cells but this causes the checkmark to go when I scroll down and then back up, I understand this is because I am reusing the cell. What I can't figure out is how to fix it, I have tried all sorts of solutions and none have worked.
Any advice would be much appreciated!
The code is below, Thank you!
p.s. Just for clarity, the navTitle is getting passed in from the previous VC.
import UIKit
class workoutTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var navTitle: String = ""
var workout = [String]()
let tlabel = UILabel()
@IBOutlet weak var workoutTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
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 tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if navTitle == "The 600 Workout" {
workout = The600Workout().workoutArray
}
if navTitle == "5 Days for Muscle" {
workout = FiveDaysForMuscle().workoutArray
}
if navTitle == "Marathon Ready" {
workout = MarathonReady().workoutArray
}
if navTitle == "HIIT @ Home" {
workout = HIITAtHome().workoutArray
}
if navTitle == "Get Strong" {
workout = GetStrong().workoutArray
}
if navTitle == "Body Weight Blast" {
workout = BodyWeightBlast().workoutArray
}
if navTitle == "Bands Pump" {
workout = BandsPump().workoutArray
}
if navTitle == "Quickie Warm up" {
workout = QuickieWarmUp().workoutArray
}
if navTitle == "The Best Circuit Workout" {
workout = TheBestCircuit().workoutArray
}
if navTitle == "The Gym HIIT Workout" {
workout = GymHIIT().workoutArray
}
if navTitle == "The Ultimate Workout" {
workout = UltimateWorkout().workoutArray
}
if navTitle == "Warm up For Weights" {
workout = WarmUpForWeights().workoutArray
}
if navTitle == "6 Day Bro Split" {
workout = SixDayBroSplit().workoutArray
}
if navTitle == "Explosive Workout" {
workout = ExplosiveWorkout().workoutArray
}
if navTitle == "Strength Circuit" {
workout = StrengthCircuit().workoutArray
}
if navTitle == "Killer Circuit" {
workout = KillerCircuit().workoutArray
}
if navTitle == "Fitness Test" {
workout = FitnessTest().workoutArray
}
return workout.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
} else {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
tableView.deselectRow(at: indexPath, animated: false)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prototypeCell", for: indexPath)
if navTitle == "The 600 Workout" {
workout = The600Workout().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "5 Days for Muscle" {
workout = FiveDaysForMuscle().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Marathon Ready" {
workout = MarathonReady().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "HIIT @ Home" {
workout = HIITAtHome().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Get Strong" {
workout = GetStrong().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Body Weight Blast" {
workout = BodyWeightBlast().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Bands Pump" {
workout = BandsPump().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Quickie Warm up" {
workout = QuickieWarmUp().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "The Best Circuit Workout" {
workout = TheBestCircuit().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "The Gym HIIT Workout" {
workout = GymHIIT().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "The Ultimate Workout" {
workout = UltimateWorkout().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Warm up For Weights" {
workout = WarmUpForWeights().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "6 Day Bro Split" {
workout = SixDayBroSplit().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Explosive Workout" {
workout = ExplosiveWorkout().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Strength Circuit" {
workout = StrengthCircuit().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Killer Circuit" {
workout = KillerCircuit().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
if navTitle == "Fitness Test" {
workout = FitnessTest().workoutArray
cell.textLabel?.text = workout[indexPath.row]
}
cell.accessoryType = .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:
First make a class/struct of Workout with a flag
struct Workout {
let name: String
let isComplete: Bool
}
Make a sample Data Model
var workouts = [
Workout(name: "Squats", isComplete: false),
Workout(name: "Burpees", isComplete: false),
Workout(name: "Crunches", isComplete: true),
Workout(name: "Push Ups", isComplete: false),
Workout(name: "Jumping Jacks", isComplete: true),
Workout(name: "High Knees", isComplete: false),
Workout(name: "Lunges", isComplete: false),
Workout(name: "Plank", isComplete: false),
Workout(name: "Sechigh Knees", isComplete: false),
Workout(name: "Tricep Dips", isComplete: false),
Workout(name: "Mountain Climbers", isComplete: true),
Workout(name: "Wall Sit", isComplete: true),
Workout(name: "Squats 2", isComplete: false),
Workout(name: "Burpees 2", isComplete: false),
Workout(name: "Crunches 2", isComplete: true),
Workout(name: "Push Ups 2", isComplete: false),
Workout(name: "Jumping Jacks 2", isComplete: false),
Workout(name: "High Knees 2", isComplete: false),
Workout(name: "Lunges 2", isComplete: false),
Workout(name: "Plank 2", isComplete: false),
Workout(name: "Sechigh Knees 2", isComplete: true),
Workout(name: "Tricep Dips 2", isComplete: false),
Workout(name: "Mountain Climbers 2", isComplete: false),
Workout(name: "Wall Sit 2", isComplete: false),
]
Make a custom UITableViewCell with a Workout type variable
class CustomCell: UITableViewCell {
var workout: Workout? {
didSet {
guard let workout = workout else { return }
self.textLabel?.text = workout.name
if workout.isComplete {
self.accessoryType = .checkmark
}
else {
self.accessoryType = .none
}
}
}
}
Then in tableView:cellForRowAtIndexPath:
method pass the variable
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
cell.workout = workouts[indexPath.row]
return cell
}
回答2:
You will have to make an array of booleans of size equal to the length of the number of cells in the table view. Then whenever the state changes, you will have to update it.
Eg: If your table view has 5 exercises, then initially all 5 will be false since none of them has been completed.
var completed = [Bool](repeating: false, count: 5)
Then when the user taps on a cell, you will have to update this value as:
completed[indexPath.row] = !completed[indexPath.row]
This way when you are rendering a cell, you can check if the workout was completed or not.
let cell = tableView.dequeueReusableCell(withIdentifier: "prototypeCell", for: indexPath)
cell.accessoryType = completed[indePath.row] ? .checkmark : .none
I will update your code to include this and it should work for your current view controller. I have moved the code where you set the workout array to a separate code so you don't have to do the same work again. This should make your cellForRowAt a lot cleaner.
import UIKit
class workoutTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var navTitle: String = ""
var workout = [String]()
let tlabel = UILabel()
//Keep track of completed state
var completed: [Bool] = []
@IBOutlet weak var workoutTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//Setting the workout array in a separate function so don't have to do the check in number of rows
setWorkout()
//Initializing the completed array
completed = [Bool](repeating: false, count: workout.count)
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
}
if navTitle == "5 Days for Muscle" {
workout = FiveDaysForMuscle().workoutArray
}
if navTitle == "Marathon Ready" {
workout = MarathonReady().workoutArray
}
if navTitle == "HIIT @ Home" {
workout = HIITAtHome().workoutArray
}
if navTitle == "Get Strong" {
workout = GetStrong().workoutArray
}
if navTitle == "Body Weight Blast" {
workout = BodyWeightBlast().workoutArray
}
if navTitle == "Bands Pump" {
workout = BandsPump().workoutArray
}
if navTitle == "Quickie Warm up" {
workout = QuickieWarmUp().workoutArray
}
if navTitle == "The Best Circuit Workout" {
workout = TheBestCircuit().workoutArray
}
if navTitle == "The Gym HIIT Workout" {
workout = GymHIIT().workoutArray
}
if navTitle == "The Ultimate Workout" {
workout = UltimateWorkout().workoutArray
}
if navTitle == "Warm up For Weights" {
workout = WarmUpForWeights().workoutArray
}
if navTitle == "6 Day Bro Split" {
workout = SixDayBroSplit().workoutArray
}
if navTitle == "Explosive Workout" {
workout = ExplosiveWorkout().workoutArray
}
if navTitle == "Strength Circuit" {
workout = StrengthCircuit().workoutArray
}
if navTitle == "Killer Circuit" {
workout = KillerCircuit().workoutArray
}
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) {
//Updating the status of workout for current row in saved list
completed[indexPath.row] = !completed[indexPath.row]
tableView.cellForRow(at: indexPath)?.accessoryType = completed[indexPath.row] ? .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]
cell.accessoryType = completed[indexPath.row] ? .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
}
}
来源:https://stackoverflow.com/questions/59605872/swift-tableview-checkmark-repeating-on-scroll