user3910594
user3910594

Reputation:

UITableView mixing values because of reusable cells

I have a problem when I scroll in my tableview which contains elements that can be scrolled horizontal it is mixing the values. enter image description here

enter image description here

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    var cell : RoundWorkoutCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as! RoundWorkoutCell

    let tmpCell = cell

    print(tmpCell)

    if(cell == nil)
    {
        cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! RoundWorkoutCell;
    }
    let exerviseName = RoundLabels[indexPath.row]

    if(indexPath.row == 0){

        for countMe in 0..<self.round_1_exercises.count {
            if(countMe<1){
                roundPosition1.append(5)
            }else{
                roundPosition1.append(115+roundPosition1[countMe-1])
            }
            scrollerSize = 115+roundPosition1[countMe]
        }

    }else if(indexPath.row == 1){
        for countMe in 0..<self.round_2_exercises.count {
            if(countMe<1){
                roundPosition2.append(5)
            }else{
                roundPosition2.append(115+roundPosition2[countMe-1])
            }
            scrollerSize = 115+roundPosition2[countMe]
        }
    }else if(indexPath.row == 2){
        for countMe in 0..<self.round_3_exercises.count {
            if(countMe<1){
                roundPosition3.append(5)
            }else{
                roundPosition3.append(115+roundPosition3[countMe-1])
            }
            scrollerSize = 115+roundPosition3[countMe]
        }
    }......
    cell.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(scrollerSize),115)
    cell.RoundExercise_Cell_ScrollView.showsHorizontalScrollIndicator = true
    cell.RoundExercise_Cell_ScrollView.indicatorStyle = .Default


    if(indexPath.row==0){

        for index in 0..<self.round_1_exercises.count {
            print("Round position 1 \(self.roundPosition1[index])")

            var imageView : UIImageView
            imageView  = UIImageView(frame:CGRect(x:roundPosition1[index],y: 5, width:110, height: 110 ))
            imageView.backgroundColor = UIColor.whiteColor()
            cell.RoundExercise_Cell_ScrollView.addSubview(imageView)

            let label1: UILabel = UILabel()
            label1.frame = CGRect(x:roundPosition1[index],y: 5, width:110, height: 20 )
            label1.textColor = UIColor(red:17/255.0, green: 22/255.0, blue: 40/255.0, alpha:1.0)
            label1.textAlignment = NSTextAlignment.Center
            label1.font = UIFont(name: "OpenSans-CondensedLight", size: 14)
            label1.text = exerciseInfo.exercise_name(self.round_1_exercises[index] as! String)
            cell.RoundExercise_Cell_ScrollView.addSubview(label1)

            let frame1 = CGRect(x:roundPosition1[index]+10,y:25, width:90, height: 90 )
            let button = UIButton(frame: frame1)
            button.backgroundColor = UIColor.redColor()
            button.setBackgroundImage(UIImage(named: (self.round_1_exercises[index] as? String)!) as UIImage?, forState: .Normal)
            button.setTitleColor(UIColor.blackColor(), forState: .Normal)
            button.setTitle(self.round_1_exercises[index] as? String, forState: .Normal)
            button.setTitleColor(UIColor(red:0/255,green:0/255,blue:0/255,alpha:0.0), forState: .Normal)
            button.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
            cell.RoundExercise_Cell_ScrollView.addSubview(button)

            let label: UILabel = UILabel()
            label.frame = CGRect(x:roundPosition1[index],y: 115, width:110, height: 20 )
            label.font = UIFont(name: "OpenSans", size: 14)
            label.textColor = UIColor.whiteColor()
            label.textAlignment = NSTextAlignment.Center
            label.text = self.round_1_decoration[index] as? String
            cell.RoundExercise_Cell_ScrollView.addSubview(label)



        }
    }else if(indexPath.row == 1){
        for index in 0..<self.round_2_exercises.count {
             print("Round position 2 \(self.roundPosition2[index])")

            var imageView : UIImageView
            imageView  = UIImageView(frame:CGRect(x:roundPosition2[index],y: 5, width:110, height: 110 ))
            imageView.backgroundColor = UIColor.whiteColor()
            cell.RoundExercise_Cell_ScrollView.addSubview(imageView)

            let label1: UILabel = UILabel()
            label1.frame = CGRect(x:roundPosition2[index],y: 5, width:110, height: 20 )
            label1.textColor = UIColor(red:17/255.0, green: 22/255.0, blue: 40/255.0, alpha:1.0)
            label1.textAlignment = NSTextAlignment.Center
            label1.font = UIFont(name: "OpenSans-CondensedLight", size: 14)
            label1.text = exerciseInfo.exercise_name(self.round_2_exercises[index] as! String)
            cell.RoundExercise_Cell_ScrollView.addSubview(label1)

            let frame1 = CGRect(x:roundPosition2[index]+10,y:25, width:90, height: 90 )
            let button = UIButton(frame: frame1)
            button.backgroundColor = UIColor.redColor()
            button.setBackgroundImage(UIImage(named: (self.round_2_exercises[index] as? String)!) as UIImage?, forState: .Normal)
            button.setTitleColor(UIColor.blackColor(), forState: .Normal)
            button.setTitle(self.round_2_exercises[index] as? String, forState: .Normal)
            button.setTitleColor(UIColor(red:0/255,green:0/255,blue:0/255,alpha:0.0), forState: .Normal)
            button.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
            cell.RoundExercise_Cell_ScrollView.addSubview(button)

            let label: UILabel = UILabel()
            label.frame = CGRect(x:roundPosition2[index],y: 115, width:110, height: 20 )
            label.font = UIFont(name: "OpenSans", size: 14)
            label.textColor = UIColor.whiteColor()
            label.textAlignment = NSTextAlignment.Center
            label.text = self.round_2_decoration[index] as? String
            cell.RoundExercise_Cell_ScrollView.addSubview(label)



        }
    }.....
    cell.RoundExercise_Cell_Label.text = exerviseName

    return cell as RoundWorkoutCell
}

Here is the custom cell class

class RoundWorkoutCell: UITableViewCell {

@IBOutlet var RoundExercise_Cell_Label: UILabel!
@IBOutlet var RoundExercise_Cell_ScrollView: UIScrollView!


override func awakeFromNib() {
    super.awakeFromNib()

    // Initialization code
    self.RoundExercise_Cell_Label.text = ""
    self.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(0),115)
}

I have placed this thread before but no one answered.I need to fix this.I have finished my whole app and this is left.I know that i have to use somehow prepareForReuse() but am not sure how,or if i could disable this reusable cells. Thanks

Upvotes: 2

Views: 2262

Answers (4)

saxahan
saxahan

Reputation: 21

@Swinny89 was at right point thank you for that, but I can add more detail about prepareForReuse method. If you remove all your reusing cell data in that method, the performance will reduce. On the other hand, you can hide your variables in prepareForReuse method, and make them visible in your cell init method.

For example as the way I did:

override func prepareForReuse() {

    if(commentLabels != nil) {
        for item in commentLabels {
            item.hidden = true
        }
    }

    super.prepareForReuse()
}

In other method I make the visible with if check:

    if(commentLabels == nil) {
        // creating new labels and adding to commentLabels

    } else { // it means reuse cell data and it has been hidden
        // hidden = false
        for item in commentLabels {
            item.hidden = false
        }

    }

Upvotes: 1

user3910594
user3910594

Reputation:

class RoundWorkoutCell: UITableViewCell {

@IBOutlet var RoundExercise_Cell_Label: UILabel!
@IBOutlet var RoundExercise_Cell_ScrollView: UIScrollView!





override func awakeFromNib() {
    super.awakeFromNib()

    // Initialization code
    self.RoundExercise_Cell_Label.text = ""
    self.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(0),115)
}

override func prepareForReuse() {
    //myOutletName = myNilValue

    self.RoundExercise_Cell_Label.text = ""
    self.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(0),115)

    for subview in RoundExercise_Cell_ScrollView.subviews {
            print(subview)
            subview.removeFromSuperview()
    }

    super.prepareForReuse()
}

}

Upvotes: 1

Swinny89
Swinny89

Reputation: 7373

The cells are reused to save on memory, this means that you need to recycle them properly to stop old data from being shown. You can do this by adding the prepareForReuse() function into your tableviewCell. In here you will need to set image outlets to be empty i.e by setting it to UIImage(). You will need to do the same with all outlets. This will ensure that old data that is not relevant will not be shown.

Example:

override func prepareForReuse() {
    //myOutletName = myNilValue 
    super.prepareForReuse()
}

Upvotes: 2

redent84
redent84

Reputation: 19249

If you don't want to reuse cells, you can simply remove the call to dequeueReusableCellWithIdentifier. Try replacing these lines:

var cell : RoundWorkoutCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as! RoundWorkoutCell

let tmpCell = cell

print(tmpCell)

if(cell == nil)
{
    cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! RoundWorkoutCell;
}

With this:

cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! RoundWorkoutCell;

This approach doesn't reuse cells and will be less performant and will leak memory.


A better approach would be to construct the cells once and then reuse them, instead of rebuilding them in cellForRowAtIndexPath method. For example, if you design the cells in the NIB (as you are doing now), there's no need to add buttons and labels again by code, you can simply change the contents and hide those that are not needed.

Then, on prepareForReuse (or even at the beginning of cellForRowAtIndexPath), you reset the content of the row to the initial state.

Upvotes: 1

Related Questions