Custom view inside UITableViewCell changes own position while scrolling

I have a UITableView with UITableViewCell. And in UITableViewCell I have a custom view which draw a circle inside if it needed.

But when I scroll this circles change positions, disappears and so on. Can't understand why so...


You can see what happens after 3 times of scrolling down-up:



So the right positions of circles were missed.


That is my storyboard screenshot for this view:



The class of my custom view which draws the circle:

class SongRatingView: UIView {
    var ratingVal: Float?

    var circleCenter: CGPoint {
        return convertPoint(CGPoint(x: center.x, y: center.y), fromView: superview)
    }

    var circleRadius: CGFloat {
        return min(bounds.size.width, bounds.size.height) / 2 - 1
    }

    var strokeColor: UIColor?

    override func drawRect(rect: CGRect) {
        guard strokeColor != nil else {
            return
        }

        let circlePath = UIBezierPath(arcCenter: circleCenter, radius: circleRadius, startAngle: 0, endAngle: CGFloat(2 * M_PI), clockwise: true)

        strokeColor!.setStroke()
        circlePath.lineWidth = 1
        circlePath.stroke()
    }
}


My cellForRowAtIndexPath method:

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

    let song = songs![indexPath.row]

    cell.songNameLabel.text = song.nameDecoded
    cell.delegateOpt = self
    cell.cellIndexOpt = indexPath.row
    cell.authorNameLabel.text = song.author?.nameDecoded
    cell.songRatingLabel.text = ""
    cell.songRatingView.strokeColor = nil

    if let songRating = song.rating?.floatValue {
        if songRating > 0 {
            cell.songRatingLabel.text = String(songRating / 10)
            cell.songRatingLabel.textColor = UIColor.purpleColor()
            cell.songRatingView.strokeColor = UIColor.purpleColor()
        }
    }

    return cell
}


And finally I have in class SongsTableViewCell:

@IBOutlet weak var songRatingView: SongRatingView!

which connected with storyboard


Update: Don't know why pictures I past ino post does not displayed....


Pictures don't display because the forum software is brain damaged. Apple believes we are little children who can't be trusted with sharp objects so instead of just moderating the forums they disallow all inline images and make it difficult to add external links.


I don't see anything obviously wrong with your code; what is the content mode of the songRatingView? It should be set to "redraw" for a custom view like that.

Thank you very much! Thats so easy! I've just changed content mode to "redraw" and it works!

This was early happyness. Unfortunately, It's not works...

Do you have any code in your SongsTableViewCell subclass? Particularly anything that adds subviews or does layout (manual or modifying constraints)?

Hmmm...

Thats my SongsTableViewCell class:

protocol FavoriteCellDelegate {
    func didClickFavoriteOnCellAtIndex(cellIndex: Int)
}

class SongsTableViewCell: UITableViewCell {

    var delegateOpt: FavoriteCellDelegate?
    var cellIndexOpt: Int?
    var isFavorite: Bool?
  
    @IBOutlet weak var songRatingLabel: UILabel!
    @IBOutlet weak var songNameLabel: UILabel!
    @IBOutlet weak var authorNameLabel: UILabel!
    @IBOutlet weak var toggleFavorite: UIButton!
  
    @IBOutlet weak var songRatingView: SongRatingView!
  
    @IBAction func toggleFavoriteTapped(sender: UIButton) {
        print("Favorite button was clicked")
      
        guard let delegate = delegateOpt else {
            print("Delegate is nil! Return")
            return
        }
      
        guard let cellIndex = cellIndexOpt else {
            print("CellIndex is nil! Return")
            return
        }

        if let favStatus = isFavorite {
            if favStatus {
                isFavorite = false
                sender.setImage(UIImage(named: "favorite"), forState: UIControlState.Normal)
            }
            else {
                isFavorite = true
                sender.setImage(UIImage(named: "favorite_checked"), forState: UIControlState.Normal)
            }
        }
      
        delegate.didClickFavoriteOnCellAtIndex(cellIndex)
    }
  
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    }
}


I do not see anything strange here...

One interesting nuance. My SongRatingView has subview with text (UILabel). And this UILabel displays correctly unlike circle which "jump" after scrolling.


P.S.: I've tried to remove subview (UILabel) at all - does not hove any effect.

I agree that all looks fine. Running out of ideas 😝


When you log out the frame of your circle view, is that consistent? Or does it move around based on your label width or something? Perhaps dumping the constraints using -constraintsAffectingLayoutForAxis would illuminate something.


If you calculate the center just from your own bounds, e.g. CGRectMake(bounds.origin.x + bounds.size.width / 2.0, bounds.origin.y + bounds.size.height / 2.0), instead of based on the frame (which is in superview coordinates) does that make any difference? I'm wondering if your drawRect is being called somewhere in the middle of the layout process where your view has been resized to its final size but not positioned properly yet.

I think your suggestion about the center is correct. If the radius is calculated like this:


min(bounds.size.width, bounds.size.height) / 2 - 1


then the circle is going to be mispositioned if its origin is anywhere except the center of the bounds. There's no point in converting the center from the frame, so the center can be calculated like this:


CGRect (x: bounds.midX, y: bounds.midY)


which is the same value as you suggested, just easier to write. (Actually, in Swift, you can also write bounds.width and bounds.height, leaving out the intermediate 'size.'.) If the view stil bounces around, then logging both the bounds and the "center" property should tell whether the drawing is wrong or the view is moving within its superview.

Custom view inside UITableViewCell changes own position while scrolling
 
 
Q