I am having trouble with the reusable aspect of my UITableViewCell. I have different type of data going into the different cells. When the types of data are the same (example: only images) I have no problem because the new image over writes the old one. But when the types of data are the same they both are sticking around.
I'm looking for methods or tools for clearing a cell completely. I have tried many things such as dictionaries to reference the data directly and erase it but that failed because I couldn't consistently tell what data was in what reused cell (especially when scrolling a lot). Others things were
cell.contentView.subviews.removeAll()
threw error, another was
cell.contentView.removeFromSuperView()
but that just made the cell useable and nothing loaded ever.
my code is below, thanks if advance for the help (ignore the date stuff).
if dictionary?["type"]! != nil {
cell.imageView?.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
cell.imageView?.layer.cornerRadius = 1
cell.imageView?.frame = CGRect(x: 0, y: UIScreen.main.bounds.height/2, width: 25, height: 25)
self.tableTextOverLays(i: indexPath.row, type: Int((dictionary?["type"])! as! NSNumber), cell: cell)
if (indexPath.row == tableFeedCount - 1) && lockoutFeed != true && tableFeedCount == self.allDictionary.count && imageFeedCount == imageDictionary.count {
let dictionary = self.allDictionary[indexPath.row]
let date = dictionary?["date"] as! Double
let date2 = date*100000
let date3 = pow(10,16) - date2
let date4 = String(format: "%.0f", date3)
self.setUpFeed(starting: date4, queryLimit: UInt(11))
}
}
else if (dictionary?["type_"] as! String) == "Image" {
print("image feed", indexPath.row)
cell.imageView?.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
cell.imageView?.layer.cornerRadius = 1
cell.imageView?.frame = CGRect(x: 0, y: UIScreen.main.bounds.height/2, width: 25, height: 25)
cell.imageView?.image = imageDictionary[indexPath.row]
if indexPath.row == tableFeedCount - 1 && lockoutFeed != true && tableFeedCount == self.allDictionary.count && imageFeedCount == imageDictionary.count{
let dictionary = self.allDictionary[indexPath.row]
let date = dictionary?["date"] as! Double
let date2 = date*100000
let date3 = pow(10,16) - date2
let date4 = String(format: "%.0f", date3)
setUpFeed(starting: date4, queryLimit: UInt(11))
}
}
else if (dictionary?["type_"] as! String) == "Video" {
print("video feed", indexPath.row)
cell.imageView?.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
let imagerView = UIImageView()
imagerView.layer.cornerRadius = 1
imagerView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
imagerView.image = videoDictionary[indexPath.row]
// let vidUIView = UIView()
// vidUIView.tag = indexPath.row
let asf = UITapGestureRecognizer(target: self, action: #selector(ProfileController.playVid2(gestureRecognizer:)))
asf.numberOfTapsRequired = 2
// imagerView.addGestureRecognizer(asf)
let adsf = UITapGestureRecognizer(target: self, action: #selector(ProfileController.playVid(gestureRecognizer:)))
adsf.numberOfTapsRequired = 1
adsf.require(toFail: asf)
// imagerView.addGestureRecognizer(adsf)
cell.contentView.addGestureRecognizer(asf)
cell.contentView.addGestureRecognizer(adsf)
cell.contentView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
cell.contentView.addSubview(imagerView)
//cell.contentView.addSubview(vidUIView)
if indexPath.row == tableFeedCount - 1 && lockoutFeed != true && tableFeedCount == self.allDictionary.count && videoFeedCount == videoDictionary.count{
let dictionary = self.allDictionary[indexPath.row]
let date = dictionary?["date"] as! Double
let date2 = date*100000
let date3 = pow(10,16) - date2
let date4 = String(format: "%.0f", date3)
setUpFeed(starting: date4, queryLimit: UInt(11))
}
}
It's unclear if that code is in a view controller or in a UITableViewCell subclass. In any case, you will want to use a UITableViewCell subclass for your cells and do cleanup tasks in prepareForReuse:
override func prepareForReuse() {
super.prepareForReuse()
imagerView.image = nil
}
This will involve removing any data from the cell that you don't want carried over into the cell when it is reused. Clearing images from image views, hiding views that won't be shown for all cell types, etc.
Another solution would be to have multiple cell classes and load the appropriate cell class based on the data type it is responsible for displaying. This will simplify some of the code above and may help to prevent reuse, but it will require a significant refactor by the looks of things.
Related
I am getting the widgets/data overlapped in every cell when I scroll down or up my tableview. What I am doing is, I have an array in elements are wCheckinArray = ["Date", "TextFeild", "TextView", "Check", "Sign"]. Now I am checking if there is an element "anyElement" in wCheckinArray then I am making an anyElement in a cell and add as a subview in content view of a cell.
Example:
Under cellForRowAtIndexPath method of UITableView :
let w = wCheckinArray[indexPath.row]
if w.contains(Date) {
// make a date picker and add it as a subview to content view of cell
}
// and so on
Real code:
var Cell : UITableViewCell?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellforrowatindexpathruns")
var instockSignView : Int = 1
Cell = tempCheckinTable.dequeueReusableCell(withIdentifier: "tempCheckinCell")! as UITableViewCell
if wCheckinArray.count == 0 && lCheckinArray.isEmpty {
print("no textFields in Checkin")
}
else
{
print("widget process i in winstock loop")
let w = wCheckinArray[indexPath.row]
if w.contains(wTextField) {
print("textField was placed")
newLabel = UITextField(frame: CGRect(x: 10, y: 10, width: 250, height: 30))
newLabel.textColor = UIColor.gray
newLabel.tag = widgetTagOk
print("You have a instock textLabel with tag \(newLabel.tag)")
if lCheckinArray.isEmpty || idCheckinArray.count != lCheckinArray.count {
newLabel.text = " "
} else {
newLabel.placeholder = lCheckinArray[indexPath.row]
newLabel.layer.cornerRadius = 3
newLabel.layer.borderColor = UIColor.gray.cgColor
newLabel.layer.borderWidth = 1
//instockTextField += 1
widgetTagOk += 1
print("\(wCheckinArray.count)")
Cell?.contentView.addSubview(newLabel)
}
}
if w.contains(wTextView) {
print("textView was placed")
newTextView = UITextView(frame: CGRect(x:10, y: 35, width: 250, height: 50))
newTextView.textAlignment = NSTextAlignment.justified
newTextView.textColor = UIColor.black
newTextView.backgroundColor = UIColor.white
newTextView.font = UIFont.systemFont(ofSize: 15)
newTextView.tag = widgetTagOk //instockTextView
print("You have a instock textView with tag \(newTextView.tag)")
if lCheckinArray.isEmpty || idCheckinArray.count != lCheckinArray.count {
} else {
textViewLabel = UILabel(frame: CGRect(x:10, y: 10, width: 250, height: 20))
textViewLabel.text = "\(lCheckinArray[indexPath.row]):"
textViewLabel.textColor = .black
textViewLabel.tag = widgetTagOk
print("here is the TextView place text \(lCheckinArray[indexPath.row])")
newTextView.layer.cornerRadius = 3
newTextView.layer.borderColor = UIColor.gray.cgColor
newTextView.layer.borderWidth = 1
widgetTagOk += 1 //instockTextView += 1
print("\(wCheckinArray.count)")
Cell?.contentView.addSubview(newTextView)
Cell?.contentView.addSubview(textViewLabel)
}
}
if w.contains(wSign) { //wInstockArray.contains(wSign) {
print("sign was place d")
newSignView = UIView(frame: CGRect(x:10, y: 35, width: 250, height: 50))
newSignView.backgroundColor = UIColor.white
newSignView.tag = instockSignView
print("You have a instock signView with tag \(newSignView.tag)")
if lCheckinArray.isEmpty || idCheckinArray.count != lCheckinArray.count {
print("signview can nit be added as a widget")
} else {
textViewLabel = UILabel(frame: CGRect(x:10, y: 10, width: 250, height: 20))
textViewLabel.text = "\(lCheckinArray[indexPath.row]):"
textViewLabel.textColor = .black
textViewLabel.tag = widgetTagOk
print("here is the TextView place text \(lCheckinArray[indexPath.row])")
newSignView.layer.cornerRadius = 3
newSignView.layer.borderColor = UIColor.gray.cgColor
newSignView.layer.borderWidth = 1
instockSignView += 1
print("\(wCheckinArray.count)")
print("signview has added as a instock widget")
Cell?.contentView.addSubview(newSignView)
Cell?.contentView.addSubview(textViewLabel)
}
}
if w.contains(wDate) { //wInstockArray.contains(wSign) {
print("date was placed")
newDatePicker = UIDatePicker(frame: CGRect(x:10, y: 35, width: 250, height: 50))
newDatePicker.backgroundColor = UIColor.white
newDatePicker.tag = instockSignView
print("You have a checkin date picker with tag \(newSignView.tag)")
if lCheckinArray.isEmpty || idCheckinArray.count != lCheckinArray.count {
print("date picker can nit be added as a widget")
} else {
textViewLabel = UILabel(frame: CGRect(x:10, y: 10, width: 250, height: 20))
textViewLabel.text = "\(lCheckinArray[indexPath.row]):"
textViewLabel.textColor = .black
textViewLabel.tag = widgetTagOk
print("here is the date picker place text \(lCheckinArray[indexPath.row])")
newDatePicker.layer.cornerRadius = 3
newDatePicker.layer.borderColor = UIColor.gray.cgColor
newDatePicker.layer.borderWidth = 1
instockSignView += 1
print("\(wCheckinArray.count)")
print("date picker has added as a checkin widget")
Cell?.contentView.addSubview(newDatePicker)
Cell?.contentView.addSubview(textViewLabel)
}
}
// } //for i in instockarr comment bracket
print("could not add widgets")
print("here is w id \(idCheckinArray),here is w name: \(wCheckinArray), here is w data \(lCheckinArray)")
}
return Cell!
}
This is how tableView looks when I scroll up or down the tableView
What I have Googled
What I have googled is This question which suggests to me that I should check the clear graphics context which is done by default in Xcode
Also searched Here which suggest me to change the cell identifier for every cell, well how can I do? How can I change the cell identifier which I have mentioned in Tableview cell's attributes inspector in Xcode. Is there any solution to reuse this programatically.
Update
The tags of widget like textview.tag or textfield.tag are increasing when I scroll the tableview from bottom to upward here is how I printed the tag of textfield
You better remove your all subviews creating subview.And make sure your y position to every elements is not same
Cell = tempCheckinTable.dequeueReusableCell(withIdentifier: "tempCheckinCell")! as UITableViewCell
for subView in cell.contentView.subviews{
subView .removeFromSuperview()
}
for subviews in self.profilescroll.subviews
{
if ((subviews is UITextField) && (subviews.tag == 0))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
if ((subviews is UITextField) && (subviews.tag == 1))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
}
this is where i put tag to the textfield.
for i in 0 ..< 2
{
btnbudget = UITextField()
btnbudget.frame = CGRect(x: (CGFloat(i) * (((screenWidth - 160)/2) + 40)) + 40, y: 0, width: (screenWidth - 160)/2, height: 50)
btnbudget.font = UIFont(name: "Metropolis-SemiBold", size: 18)
btnbudget.layer.cornerRadius = 10.0
btnbudget.layer.borderWidth = 1.0
btnbudget.layer.borderColor = UIColor(hexString:"4B45A0").cgColor
btnbudget.tag = i
btnbudget.backgroundColor = UIColor(hexString: "#00000000")
btnbudget.text = budgetlist.object(at: i) as? String
btnbudget.textColor = UIColor(hexString:"4B45A0")
btnbudget.textAlignment = .center
btnbudget.delegate = self
budgetbtnview.addSubview(btnbudget)
}
where profilescroll is the view with 3 textfields in which i need to find textfield with tag values ranging from 0 to 1. but for some reason i am not getting the right one hence the keyboard is not returning.
You have assigned tag number to text field than don't need to loop over all subviews.
textField.tag = 101
You can directly use tag number to get view like below...
if let txtField = self.view.viewWithTag(101) as? UITextField {
print(txtField.text)
}
Make sure all the tag you give to view should be unique otherwise you may get unwanted view.
given tag should be unique ...try this..
for i in 100 ..< 102
{
btnbudget = UITextField()
btnbudget.frame = CGRect(x: (CGFloat(i) * (((screenWidth - 160)/2) + 40)) + 40, y: 0, width: (screenWidth - 160)/2, height: 50)
btnbudget.font = UIFont(name: "Metropolis-SemiBold", size: 18)
btnbudget.layer.cornerRadius = 10.0
btnbudget.layer.borderWidth = 1.0
btnbudget.layer.borderColor = UIColor(hexString:"4B45A0").cgColor
btnbudget.tag = i
btnbudget.backgroundColor = UIColor(hexString: "#00000000")
btnbudget.text = budgetlist.object(at: i) as? String
btnbudget.textColor = UIColor(hexString:"4B45A0")
btnbudget.textAlignment = .center
btnbudget.delegate = self
budgetbtnview.addSubview(btnbudget)
}
and ....
for subviews in self.profilescroll.subviews
{
if ((subviews is UITextField) && (subviews.tag == 100))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
if ((subviews is UITextField) && (subviews.tag == 101))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
}
but self.view.viewWithTag(101) approach is more appropriate than this
Let say you have three UITextField and you have set the tags as 0,1,2
Now, you can simply loop through all the Subview of your profilescroll
for case let textField as UITextField in self.profilescroll.subviews {
if textField.tag == 0 {
// do something
return
}
else if textfield.tag == 1 {
// do something
return
}
else if textfield.tag == 2 {
// do something
return
}
}
This will also ignore cases where your subview is not an UITextField thereby saving time.
I have a UITableView with some CustomViews, I am adding everything successfully. But when I scroll the tableview it's shuffle with the the UIImageView those I have added on UIView on runtime.can you please help me out to solve this issue.
Please check my code and give me the right solution.Please prefer only swift3
if(tb_LayerType == "layer"){
var cell = tableView.dequeueReusableCell(withIdentifier: "menuitem1", for: indexPath) as! MenuItemMultipleImageTableViewCell
cell.tag = indexPath.row
print(indexPath.row)
cell.lblProductName.text = listSearch[indexPath.row].tb_product_name
cell.lblProductTopings.text = listSearch[indexPath.row].tb_topping_name
cell.selectionStyle = .none
let productSize = listSearch[indexPath.row].tb_product_size!.components(separatedBy: ",")
let productAmount = listSearch[indexPath.row].tb_product_amount!.components(separatedBy: ",")
// cell.imgView.kf.setImage(with: nil)
if(productSize[0] == "regular"){
cell.lblSize.text = "("+" Alm "+")"
cell.lblPrice.text = productAmount[productSize.index(of: "regular")!]+" kr"
}else if(productSize[0] == "small"){
cell.lblSize.text = "("+" Lille "+")"
cell.lblPrice.text = productAmount[productSize.index(of: "small")!]+" kr"
}else{
cell.lblSize.text = "( "+productSize[0].capitalized+" )"
cell.lblPrice.text = productAmount[0]+" kr"
}
extraTopingImages.removeAll()
extraTopingImages = listSearch[indexPath.row].tb_topping_image!.components(separatedBy: ",")
for name in extraTopingImages {
let image = UIImageView()
self.UrlName = ""
if let range = name.range(of: ".png") {
let firstPart = name[name.startIndex..<range.lowerBound]
self.UrlName = Constants.imageurl+firstPart+".png"
}
if let range = name.range(of: ".png") {
let tagnumber = name[range.upperBound...]
print(tagnumber)
let url = URL(string: UrlName!)
image.kf.setImage(with: url)
image.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
//image.tag = Int(tagnumber)!
image.layer.zPosition = CGFloat(Int(tagnumber)!)
imageArr.append(image)
cell.viewMultipleImage.addSubview(image)
}
}
return cell
}
As you are making cell object with dequeueReusableCell, dequeueReusable means that cell will be gonna reuse again and again. So in any situation if one cell has found if let range = name.range(of: ".png"), all other cell will use same images.
What you should do in this case is, remember to put else closes with if in cellForRow method.
I want to have a tableview that has 2 cells.
One with a label that increases the height and one without that label. The user adds things to this tableview (sort of like a todo list) and if the user selects an option, the label is visible in the tableviewcell of the new item that they added, but if they don't select the option, then they add a tableviewcell without that label. Is there anyway in which I can choose when to display the cell with the label.
I first thought of just making the label invisible if the option isn't selected, however that affect the height of the other label and the constraints.
Would it be possible to create 2 different cells with different identifiers, and call which one to add depending on whether the option is selected.
According to solutions, I tried switching between 2 different cells and this was my code for doing so.
if newMode==true{
let cell = tableView.dequeueReusableCell(withIdentifier: "monthlyCell", for: indexPath) as! MonthlyExpenseTableViewCell
let screenSize: CGRect = UIScreen.main.bounds
cell.dateLabelBackground.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: 31)
cell.dateLabel.text = dateString
cell.expenseName2.text = expense.name
cell.expenseAmount2.text = finalDisplayed
cell.expenseCategory2.text = expense.category
cell.expenseCollection2.text = expense.collection
if (expense.expense) {
cell.expenseAmount2.textColor = UIColor.red
}
else if (expense.income){
cell.expenseAmount2.textColor = UIColor.green
}
if (expense.cash) && (expense.expense){
cell.cashOrCredit.image = #imageLiteral(resourceName: "Cash-Expense Icon")
}
else if (expense.cash) && (expense.income){
cell.cashOrCredit.image = #imageLiteral(resourceName: "Cash-Income Icon")
}
else if (expense.credit) && (expense.income){
cell.cashOrCredit.image = #imageLiteral(resourceName: "Credit-Income Icon")
}
else if (expense.credit) && (expense.income){
cell.cashOrCredit.image = #imageLiteral(resourceName: "Credit-Expense Icon")
}
}
else if newMode==false{
let cell = tableView.dequeueReusableCell(withIdentifier: "monthlyCell2", for: indexPath) as! MonthlyExpenseTableViewCellShort
cell.expenseName3.text = expense.name
cell.expenseAmount3.text = finalDisplayed
cell.expenseCategory3.text = expense.category
cell.expenseCollection3.text = expense.collection
if (expense.expense) {
cell.expenseAmount3.textColor = UIColor.red
}
else if (expense.income){
cell.expenseAmount3.textColor = UIColor(red:0.49, green:0.83, blue:0.13, alpha:1.0)
}
if (expense.cash) && (expense.expense){
cell.cashOrCredit3.image = #imageLiteral(resourceName: "Cash-Expense Icon")
}
else if (expense.cash) && (expense.income){
cell.cashOrCredit3.image = #imageLiteral(resourceName: "Cash-Income Icon")
}
else if (expense.credit) && (expense.income){
cell.cashOrCredit3.image = #imageLiteral(resourceName: "Credit-Income Icon")
}
else if (expense.credit) && (expense.income){
cell.cashOrCredit3.image = #imageLiteral(resourceName: "Credit-Expense Icon")
}
}
return cell
}
The problem with this, is that it only returns on cell at a time for some reason. When I delete the cell, it shows the next one.
Here is the code for me setting the boolean "newMode"
if indexPath.row == 0{
newMode = true
}
if indexPath.row>1{
let previousExpensesData = monthlyExpenses[indexPath.row - 1].modificationDate
let day = Calendar.current.component(.day, from: expense.modificationDate as! Date) // Do not add above 'date' value here, you might get some garbage value. I know the code is redundant. You can adjust that.
let previousDay = Calendar.current.component(.day, from: monthlyExpenses[indexPath.row - 1].modificationDate! as Date)
if day == previousDay {
newMode = false
} else {
newMode = true
}
}
For my heightForRowAtIndexPath, I use this code
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat
{
if newMode==true {
return 95
}
else if newMode==false {
return 64
}
else {
return 0
}
}
And this is the code for numberOfRows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if monthlyExpenses.count == 0{
noExpenseLabel.isHidden = false
}
return monthlyExpenses.count
}
Here are what the 2 cells look like
Actually you will no need of having two different cell. As you just said in your question "I first thought of just making the label invisible if the option isn't selected, however that affect the height of the other label and the constraints."
Just use one cell.
Now to overcome this thing use UIStackView.Put that label in this stackView. Give proper constraints to stackView. And hide/unhide the label according to your option. UIStackView will do the things for you. :)
It will give you the result as you want. For more information about UIStackView, read this awesome tutorial by Raywenderlich.
EDIT: As per your comment
I have created a cell like this..
I managed all subviews of that cell like this..
And I just wrote a line of code in cellForRow
cell.label.isHidden = indexPath.row % 2 == 1
And I'm getting the output this....
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()
}
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
}
#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.
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
}