1

I have a UIView within the cell of a UITableView. My cellForRowAtIndexPath rounds that view. When I open the viewcontroller and see the table, all the visible views are rounded. However, when I scroll down, the first new cell to appear does not have a rounded view. How do I fix it?

My cellForRowAtIndex looks like this:

let cell = tableView.dequeueReusableCell(withIdentifier: "RepeatingCell", for: indexPath) as! CustomTableViewCell
cell.delegate = self
tableView.contentInset = UIEdgeInsetsMake(10, 0, 0, 0)

if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
    let task = tableListInfo_Context[(indexPath as NSIndexPath).row                
    let accState = task.value(forKey: "state") as? String
    //Removed assigning labels their text
    let mainView = cell.mainView
    mainView.isHidden = false
    let circleView = cell.viewToRound
    circleView?.isHidden = false
    circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
    circleView?.layer.masksToBounds = true           
    cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
} else {
    //My last cell hides several views to reveal a view under them 
    //If I comment this one line out, I get the not-rounded problem only once and it does not show up again even if I continue scrolling up / down 
    let mainView = cell.mainView
    mainView.isHidden = true
    let circleView = cell.viewToRound
    circleView?.isHidden = true
}

I understand that UITableViews recycle cells and I think that's likely causing me problems. I think it's related to that last cell where I hide views.

In a list of 20 cells, sometimes a non-rounded view shows up as the first to scroll on screen, sometimes the second. As I scroll up/down, randomly a view isn't rounded! I know there's a reason/pattern, but I don't know what.

I also tried adding the rounding code to willDisplayCell:

let circleView = myCell.mainCircle
circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
circleView?.layer.masksToBounds = true

However, that did not solve it. How inconsistent this is is very frustrating. Any help would be appreciated.

FULL CODE AS REQUESTED BY DUNCAN:

  func buildCell_RepeatableCell (indexPath: IndexPath) -> CustomTableViewCell          {
    let cell = tableView.dequeueReusableCell(withIdentifier: "RepeatingCell",     for: indexPath) as! CustomTableViewCell
    cell.delegate = self

    tableToSV_Left.constant = 10
    tableToSV_Right.constant = 10
    tableView.contentInset = UIEdgeInsetsMake(10, 0, 0, 0)

    if (indexPath as NSIndexPath).row < curLifeList_Context.count {
        resetDataCell_ToDefaultLaylout(cell: cell, hideExpand: true)
        let task = curLifeList_Context[(indexPath as NSIndexPath).row]
        let accState = task.value(forKey: "state") as? String
        cell.nameLabel!.text = task.value(forKey: "name") as? String
        cell.descLabel!.text = task.value(forKey: "desc") as? String
        let redTrashIcon = UIImage(named: "trash")
        let maskedIcon = redTrashIcon?.maskWithColor(color: .red)
        cell.row5_LeftBtnImg?.image = maskedIcon
        let doneGreen = UIColor(colorLiteralRed: 83/255, green: 223/255, blue: 56/225, alpha: 0.9)
        let greenCheck = UIImage(named: "check_Green")
        let maskedCheck = greenCheck?.maskWithColor(color: doneGreen)
        cell.row5_RightBtnImg?.image = maskedCheck
        roundQtyCircle(view: cell.mainCircle)
        if accState == "Selected" {
            cell.circleLine.backgroundColor = doneGreen
            cell.mainCircle.borderColor = doneGreen
            cell.hdrCrossOutLine.isHidden = false
            cell.row5_RightBtnImg.alpha = 0.5
        } else {
            cell.circleLine.backgroundColor = UIColor(colorLiteralRed: 211/255, green: 211/255, blue: 211/255, alpha: 0.9)
            cell.mainCircle.borderColor = UIColor(colorLiteralRed: 211/255, green: 211/255, blue: 211/255, alpha: 0.9)
            cell.hdrCrossOutLine.isHidden = true
            cell.row5_RightBtnImg.alpha = 1.0
        }
        cell.dataHdr_Left!.text = doneColon_lczd
        cell.dataHdr_Right!.text = lastDoneColon_lczd
        cell.dataVal_Left!.text = "0"
        cell.dataVal_Right!.text = "-"
        if let lastCompDate = task.value(forKey: "lastCompleted") as? Date {
            cell.dataVal_Left!.text = "\(task.value(forKey: "timesCompleted") as! Int)"
            if NSCalendar.current.isDateInToday(lastCompDate) {
                cell.dataVal_Right!.text = "Today"
                cell.dataBotDtl_Right.text = ""
            } else {
                let secondsUntilChange = (lastCompDate.seconds(from: now)) * -1
                print("Seconds ago \(secondsUntilChange)")
                var timeAgo = getDateString(secondsUntilChange, resetDate: lastCompDate)
                timeAgo.remove(at: timeAgo.startIndex)
                cell.dataVal_Right!.text = timeAgo
            }
        }
        cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
    } else {
        buildAddNew_ForRepeatableCell(cell: cell)
    }

    return cell
   }

  func resetDataCell_ToDefaultLaylout (cell: CustomTableViewCell, hideExpand: Bool) {
    cell.addnewBtn.isHidden = true
    cell.nameLabel.isHidden = false
    cell.dataHdr_Left.isHidden = false
    cell.dataHdr_Right.isHidden = false
    cell.dataTopDtl_Right.isHidden = false
    cell.dataBotDtl_Right.isHidden = false
    cell.mainBox.isHidden = false
}

func buildAddNew_ForRepeatableCell (cell: CustomTableViewCell) {
    cell.addnewBtn.isHidden = false
    cell.descLabel.text = addNew_lczd //For editor
    cell.mainBox.isHidden = true
    cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
    cell.container.layoutIfNeeded()
}
Santo
  • 1,611
  • 3
  • 17
  • 37
Dave G
  • 12,042
  • 7
  • 57
  • 83
  • In else block of `cellForRow` method you are hiding `mainView` but you are not unhiding it in the if block. – Adeel Miraj Feb 22 '17 at 13:41
  • @Adeel that was just an error in my removing the text assignment code, I accidentally removed that unhide line. If the mainView wasn't being unhidden, I wouldn't see the non-rounded view at all. – Dave G Feb 22 '17 at 13:56

3 Answers3

0

Instead of setting layer properties of the view in cellForRowAtIndexPath, set them in your CustomTableViewCell class. You can set them in awakeFromNib Method.

In short move your below code to awakeFromNib.

 let circleView = cell.viewToRound
    circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
    circleView?.layer.masksToBounds = true  

The sudo code could be something like this:

awakeFromNib(){
self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
viewToRound?.layer.masksToBounds = true  

}
Vishal Sonawane
  • 2,637
  • 2
  • 16
  • 21
  • I was excited about this idea but it didn't fix the problem. Thanks for the suggestion. – Dave G Feb 22 '17 at 13:18
  • Are there any errors after this approach? can you show how you implemented my solution? – Vishal Sonawane Feb 22 '17 at 13:22
  • override func awakeFromNib() { super.awakeFromNib() self.mainCircle?.layer.cornerRadius = (mainCircle?.frame.size.width)!/2 mainCircle?.layer.masksToBounds = true } – Dave G Feb 22 '17 at 13:23
  • try to use mainCircle?.layer.clipsToBound = true – Vishal Sonawane Feb 22 '17 at 13:28
  • If I do not format the last cell differently, the problem goes away. So I think this can be solved by understanding cell recycling, which I guess I do not. – Dave G Feb 22 '17 at 13:29
  • Just tried it. It's actually mainCircle?.clipsToBound (without the layer). When I scrolled to the bottom, my 2nd to last cell was not rounded. Then when I scrolled back to the top, my top view was not rounded. – Dave G Feb 22 '17 at 13:33
  • Thanks for pointing my typo about clipsToBound. It seems that your problems is due to mismatch in some indexPaths. – Vishal Sonawane Feb 22 '17 at 13:39
0

Try this, it will work.

override func awakeFromNib() {
        super.awakeFromNib()
        self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
        viewToRound?.layer.masksToBounds = true 
    }


override func prepareForReuse() {
        super.prepareForReuse()
        self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
        viewToRound?.layer.masksToBounds = true 
    }
Krunal
  • 77,632
  • 48
  • 245
  • 261
  • Nope. I didn't know prepareForReuse existed. I was pretty excited but that doesn't work either. 90% of the time they are round but still non-round ones appear both when I scroll quickly and slowly. – Dave G Feb 22 '17 at 13:48
  • Can you share me you source code (sample project) will update and revert you with solution, using same methods in my answer – Krunal Feb 22 '17 at 13:54
0

@Adeel already told you the problem and the solution in his comment.

You always need to fully configure your cells in every case. Remember that when you get a recycled cell, it might be in any possible leftover state.

In your case, the key bit is:

if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
  //Configure cells that are not the last cell
} else {
  //Configure cells the last cell
  mainView.isHidden = true
}

You hide mainView in the case where you're configuring the last cell, but don't un-hide it if it's not the last cell. So, if you configure the last cell, scroll it off-screen, then scroll back towards the top of the screen, that recycled cell will get reused for an indexPath other than the last one, and it's mainView will never get un-hidden.

if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
  //Configure cells that are not the last cell
  //-----------------------------
  // This is what you are missing
  mainView.isHidden = false
  //-----------------------------
} else {
  //Configure cells the last cell
  mainView.isHidden = true
}

@VishalSonawane's answer is good, but to make that work you should use 2 different identifiers, one for all cells but the last one, and a different identifier, with a cell pre-configured with mainView hidden, for the last cell. That way you won't get a recycled last cell and try to use it for one of the other positions in your table view.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Hi Duncan, I'm sorry to have wasted your time on that but that was just an error in my removing the text assignment code for my SO post, I accidentally removed that unhide line. It is definitely unhidden. If the mainView wasn't being unhidden, I wouldn't see the non-rounded view at all – Dave G Feb 22 '17 at 13:57
  • We can't debug code we can't see. You should probably show your entire `cellForRow(at:)` method. – Duncan C Feb 22 '17 at 13:59
  • Alright, it's a bit lengthy so I'd tried to not overwhelm everyone but just added it! – Dave G Feb 22 '17 at 14:02