kdgwill
kdgwill

Reputation: 2199

UICollectionView Refresh Issue

Using UICollectionView I add a Label to a cell as a subView with the scroll direction set to horizontal. Inside the Label I add a button whose background is an image. For some odd reason if I scroll the view in one direction and then come back the buttons image seems to either have left remnants to the right of the button. Either that or their is another button slightly shifted to the right under the initial buttons. I have realized the more I played around with the buttons the further right they shift. Any assistance would be appreciated

The issue does not seem to occur if the label is shorter

enter image description here

 var padding = String(count: 2, repeatedValue: (" " as Character))

    let newLabel = UILabel(frame: CGRectZero)
    newLabel.autoresizingMask =  .FlexibleHeight
    newLabel.backgroundColor = UIColor(red: 56/255, green: 143/255, blue: 212/255, alpha: 1)
    newLabel.text = "\(padding)\(title)\(padding)"
    newLabel.textColor = UIColor.whiteColor()
    let fontName: CFStringRef = "Superclarendon-Regular"
    newLabel.font = CTFontCreateWithName(fontName, 15, nil)
    newLabel.adjustsFontSizeToFitWidth = true
    newLabel.clipsToBounds = true
    newLabel.layer.cornerRadius = 4
    //Fit TO Text
    newLabel.numberOfLines = 1
    newLabel.sizeToFit()
    //Add Button
    if let image = UIImage(named: "Nav_Button_X"){//?//.CGImage
        let button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
        var imageWidth = image.size.width
        var imageHeight = image.size.height
        //Resize label for button
        var oldLabelFrame = newLabel.frame
        var buttonHeight = self.frame.height - self.sectionInsets.top - self.sectionInsets.bottom
        var buttonWidth = newLabel.frame.width + imageWidth
        //newLabel.frame = CGRect(x: oldLabelFrame.origin.x, y: oldLabelFrame.origin.y, width: oldLabelFrame.width, height: bh)
        newLabel.frame.size = CGSize(width: buttonWidth, height: buttonHeight)

        button.frame = CGRectMake(newLabel.frame.width - imageWidth, 0, imageWidth, newLabel.frame.height)
        button.setBackgroundImage(image, forState: UIControlState.Normal)
        newLabel.addSubview(button)
    }
    return newLabel
}

EDIT:

I have tried recreating the view using only a button and NSMutableAttributedString for styling, which may or may not be a better solution shrugs, and the issue persists so it may not be an issue of how I construct the button. Are there suggestions?

I subclass and set up the UICollectionView like so

    let flowLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout();
    flowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal

    super.init(frame: CGRectMake(0, 0, width, height), collectionViewLayout: flowLayout);

    self.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier);

    self.autoresizingMask = UIViewAutoresizing.FlexibleWidth
    self.backgroundColor = UIColor.whiteColor()
    self.bounces = true
    self.layer.cornerRadius = 5
    self.scrollEnabled = true
    self.delegate = self;
    self.dataSource = self;
    self.backgroundColor = UIColor.whiteColor();

and

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

let cell:UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell;
cell.backgroundColor = UIColor.clearColor();
let cellItem = subCategories[indexPath.row]
cell.contentView.addSubview(cellItem)

// Configure the cell
return cell
}

Another interesting trait is that I removed the button from odd numbered labels and found that when I perform the action to get the issue the buttons appear on all of the labels

Upvotes: 1

Views: 3467

Answers (2)

BitParser
BitParser

Reputation: 3968

In my case, I created the view hierarchy in the init method of the cell.

When scrolling slowly, the cells along with the multiline labels would appear perfectly. However, when scrolling too quickly, labels would have the size of the reused cell's label, thus appearing very bad.

I was setting preferredMaxLayoutWidth correctly, but nevertheless the cells would have issues on fast scrolling.

My problem was solved by calling setNeedsLayout inside prepareForReuse:

override func prepareForReuse() {
    setNeedsLayout()
    super.prepareForReuse()
}

Upvotes: 0

lucaslt89
lucaslt89

Reputation: 2451

This looks like your cells are being reused and they show views from old cells.

When you call dequeueReusableCellWithReuseIdentifier, the method returns a cell that is not visible anymore in the UICollectionView, but it doesn't clear it's content.

Let's say you have cells A, B and C visible, you add subviews to all of them (with cell.contentView.addSubview(cellItem)). When you scroll until cell A isn't visible anymore, and you call dequeueReusableCellWithReuseIdentifier to get a new cell (call it D), you'll reuse cell A, so it'll have the subviews you added before to cell A, and the subviews you add now to cell D.

To get rid of this, you have two options:

  1. If you're using a custom UICollectionViewCell, you should override prepareForReuse method. There you should remove all contentView's subviews.
  2. If you're using a UICollectionViewCell, you should remove all contentView's subviews before calling cell.contentView.addSubview(cellItem)

If you want to see if you're adding the button multiple times to reused cells, you can reproduce the issue and use the View Hierarchy inspector:

enter image description here

I created a small project to illustrate the issue of "dirty" cells: https://github.com/lucaslt89/DirtyUICollectionViewCellsExample

Upvotes: 2

Related Questions