Stop expanding UITableView from reloading contents incorrectly - Swift 3 - ios

I have a UITableView that expands when a particular cell is tapped. My problem is that on first load or expand of the table the additional cells load the contents correctly but with successive closing and expanding of the table things load incorrectly.
My code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "idCellTextfield" {
cell.photo.image = globalVariable.images2[(indexPath as NSIndexPath).row]
cell.name.text = globalVariable.names2[(indexPath as NSIndexPath).row]
cell.price.text = globalVariable.prices2[(indexPath as NSIndexPath).row]
cell.unitPrice.text = globalVariable.unitPrice2[(indexPath as NSIndexPath).row]
cell.pPrice.text = globalVariable.previousPrice2[(indexPath as NSIndexPath).row]
//This is the portion of code that loads incorrectly
//This code identifies if an item is on special (true or false) and if it is adds a border to the left of the cell, specifies saving amount and changes the colour and style of some labels.
if globalVariable.specialBool2[(indexPath as NSIndexPath).row] == true {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: globalVariable.previousPrice2[(indexPath as NSIndexPath).row])
attributeString.addAttribute(NSStrikethroughStyleAttributeName, value: 2, range: NSMakeRange(0, attributeString.length))
cell.pPrice.attributedText = attributeString;
cell.price.textColor = UIColor(red: 214/255, green: 14/255, blue: 0/255, alpha: 1.0)
cell.layer.addBorderWatchlist(UIRectEdge.left, color: UIColor(red: 214/255, green: 14/255, blue: 0/255, alpha: 1.0), thickness: 6)
cell.savingAmount.text = globalVariable.savingAmount2[(indexPath as NSIndexPath).row]
} else if globalVariable.specialBool2[(indexPath as NSIndexPath).row] == false {
cell.pPrice.text = " "
cell.savingAmount.text = " "
cell.savingLbl.isHidden = true
cell.savingAmount.isHidden = true
}
}
cell.delegate = self
return cell
}
What do I have to change in the relevant portion of code to ensure that the contents reloads correctly each time the table is expanded?

Related

Unable to eliminate last talbeviewcell (when search is empty)

I use this code to search for all files that are off in "review",
And there is a button to change "review" to "on". When "review" is changed to "on"(because the code only searches for "off"), the tableviewcell will disappear normally.
But every time the last stroke will not disappear(tableview can't find the file), you need to restart the app to clear the tableviewcell,
Why is this?
let db = Firestore.firestore()
db.collectionGroup("TWDBuy").whereField("review", isEqualTo: "off")
.order(by: "date", descending: true)
.addSnapshotListener { (snapshot, error) in
This is tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TWDBuyPGTableViewCell") as! TWDBuyPGTableViewCell
let post = self.postArray[indexPath.row]
let date = post.date
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.timeZone = TimeZone(identifier:"Asia/Taipei")
dateFormatter.locale = Locale.init(identifier: "zh_Hant_TW")
dateFormatter.setLocalizedDateFormatFromTemplate("yyyy-MM-dd HH:mm")
let dateStr = dateFormatter.string(from: date)
if postArray[indexPath.row].review2 == "未確認"{
cell.review2.textColor = UIColor(red: 255.0/255.0, green: 192.0/255.0, blue: 0.0/255.0, alpha: 1)
}else{
cell.review2.textColor = UIColor(red: 55.0/255.0, green: 163.0/255.0, blue: 152.0/255.0, alpha: 1)
}
cell.quantity.text = "數量:"+postArray[indexPath.row].quantity+" USDT"
cell.coin.text = "匯入: TWD "+postArray[indexPath.row].coin
cell.address.text = postArray[indexPath.row].address
cell.network.text = postArray[indexPath.row].network
cell.email.text = postArray[indexPath.row].email
cell.review2.text = postArray[indexPath.row].review2
cell.uid.text = postArray[indexPath.row].uid
cell.useruid = post.uid
cell.useremail = post.email
cell.commentDelegate2 = self
cell.time.text = dateStr
cell.idimage.sd_setImage(with: URL(string: postArray[indexPath.row].ID5))
cell.deleteThisCell2 = { [weak self] in
self!.delete2(didSelectRowAt: indexPath, cell: cell)
}
cell.deleteThisCell3 = { [weak self] in
self!.delete3(didSelectRowAt: indexPath, cell: cell)
}
let settings = Settings.defaultSettings
.with(actionOnTapOverlay: Action.dismissOverlay)
.with(actionOnDoubleTapImageView: Action.zoomIn)
addZoombehavior(for: cell.idimage, settings: settings)
getUserPortfolio(didSelectRowAt: indexPath)
//刷新
self.usertableview.es.addPullToRefresh {
[unowned self] in
/// Do anything you want...
/// ...
/// Stop refresh when your job finished, it will reset refresh footer if completion is true
self.usertableview.es.stopPullToRefresh()
/// Set ignore footer or not
self.usertableview.es.stopPullToRefresh()
}
return cell
}
I have updated the tableview, and there is nothing in the snapshot. If the tableviewcell has a "review" off, modifying each "review" to on will reduce it normally, but the last one will not disappear (firebase "review" is all on)

TableView didSelect row Issue

So the issue is when a cell is tapped, desired data is shown and when again tapped on same cell ( again desired data is shown.)
But when one cell is selected and we again select other cell (then the data is been shown of second tapped cell but the first one is not deselected).
How can I take care of this issue?
var selectedIndex = -1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIView.animate(withDuration: 1) {
self.labelViewHeightConstraint.constant = 60
self.labelLeadingConstraint.constant = 136
self.view.layoutIfNeeded()
}
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
if(selectedIndex == indexPath.row) {
selectedIndex = -1
print("deselect")
UIView.animate(withDuration: 0.4) {
cell.secondView.isHidden = true
cell.firstView.backgroundColor = UIColor(red: 0.8588, green: 0.84705, blue: 0.8745, alpha: 1.0)
}
} else {
cell.secondView.isHidden = false
}
self.expandTableView.beginUpdates()
//self.expandTableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic )
self.expandTableView.endUpdates()
}
You can archive single selection by setting tableView property like belwo
tableView.allowsMultipleSelection = false
This can also be done from Attributes Inspector
Hope this helps
you must disable multiple selection by,
self.tbl.allowsMultipleSelection = false
and enable single selection by,
self.tbl.allowsSelection = true
EDIT:-
if you want to access your old (selected cells), you should make a call like this,
//first assign tag or indexPath in Cell,
cell.tag = indexPath.row
// or
cell.indexPath = indexPath
//then fetch like bellow,
let visibleCell = tableView.visibleCells.filter({$0.tag == self.selectedIndex})
//or
let visibleCell = tableView.visibleCells.filter({$0.indexPath.row == self.selectedIndex})
//if you use ,
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
//then it will get you new cell object.

TableView gets inconsistent behaviour when scrolling down/up

I have a ViewController that has a TableView. The tableView cell has a StackView with two images in it and outside it a label.
In the tableView:cellForRowAtIndexPath I get the cell and my data is inside a arrayList. And this is what I do:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NamesTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NamesTableViewCell
let _name = _Names[indexPath.row]
if indexPath.row % 2 != 0 {
cell.backgroundColor = UIColor(hexString: "ededed")
}
cell.labelName.text = _name.name
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
}
return cell
}
So as you can see I hide the images depending on the gender of the name and also change the background of every other cell.
Now, the behavior I am seeing is:
https://gyazo.com/1b2d39696892b7fb2f15b71696d9a925
The gender is available for every object, I've checked.
What do you guys think? Thanks!
cell.pinkCircleImageView.hidden = false
cell.blueCircleImageView.hidden = false
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
cell.bringSubviewToFront(blueCircleImageView)
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
cell.bringSubviewToFront(pinkCircleImageView)
}
if indexPath.row % 2 == 0
{
cell.backgroundColor=UIColor.whiteColor()
}
else
{
cell.backgroundColor=UIColor(red: 248/255, green: 248/255, blue: 248/255, alpha: 1.0)
}

Swift: Using custom accessoryType for cell freezes UI and 99% CPU Usage

I'm trying to use a custom accessory type (UIImage) for my tableview cell which has a expand/collapse functionality. When user taps a cell the row expands or collpases if parent is tapped again.
The imageview I'm using to set the accessory type is below:
var expandIcon : UIImageView?
expandIcon = UIImageView(frame:CGRectMake(0, 0, 16, 16))
expandIcon!.image = UIImage(named:"expand")
Following code is when user taps a row which if its parent it should epxand or if its already expanded it will collapse.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellData = dataForCellAtRowIndex[indexPath.section]!.rows[indexPath.row]
var cell:UITableViewCell!
if isParentCell(indexPath.row) == true {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = "test" + String(indexPath.row)
cell.detailTextLabel?.text = "detail"
cell.backgroundColor = UIColor.whiteColor()
cell.accessoryView = expandIcon
}else{
cell = tableView.dequeueReusableCellWithIdentifier("childCell", forIndexPath: indexPath)
cell.backgroundColor = UIColor.lightGrayColor()
cell.textLabel?.text = "child name"
cell.detailTextLabel?.text = "child detail"
cell.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
The bit that is causing the problem is cell.accessoryView = expandAccessory which causes the UI to freeze and cpu usage goes to 99% as reported by xcode. if i remove cell.accessoryView = expandIcon everything is fine! Why is this happening?
You should implement a function to return an expandIcon and then call it in place of expandIcon variable.
func expandImageView() -> UIImageView {
let expandIcon = UIImageView(frame:CGRectMake(0, 0, 16, 16))
expandIcon.image = UIImage(named:"expand")
return expandIcon
}
cell.accessoryView = expandImageView()
The fix for this as #rmaddy mentioned was the reuse of UIImage view which was causing the UI freeze. Creating a new UIImage for each cell fixed my problem so instead of:
if isParentCell(indexPath.row) == true {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = "test" + String(indexPath.row)
cell.detailTextLabel?.text = "detail"
cell.backgroundColor = UIColor.whiteColor()
cell.accessoryView = expandIcon
}
i had to :
if isParentCell(indexPath.row) == true {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = "test" + String(indexPath.row)
cell.detailTextLabel?.text = "detail"
cell.backgroundColor = UIColor.whiteColor()
//put the below two lines in a func and return a UIImage
let expandIcon = UIImageView(frame:CGRectMake(0, 0, 16, 16))
expandIcon.image = UIImage(named:"expand")
cell.accessoryView = expandIcon
}

UITableViewCell not removed from the UITableView when using reloaddata

I'm using a tableview to display some categories in a left menu. When you select it, the data in the cells changes to courses ('Cursussen') within that category.
Each cell contains an UIImageView and an UILabel.
I noticed a while ago that when you select a course in the left menu, the label will change to that of a category. That wasn't a big issue back then, but now that I'm working to disable certain courses if they are not available it suddenly became a big issue. To indicate a course that's not available, I'm setting label.enabled = false, which works fine, however I also need to prevent the user from tapping on it and navigating to a course that's not available. To do that, I'm using tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) where I check whether the UILabel is enabled. If it's disabled the App won't navigate to the course.
Back to the issue, tapping on a course that is unavailable (which is displaying the correct image and label) will trigger the didSelectRowAtIndexPath delegate, but when I dequeue the Cell and check whether the UILabel in it is disabled it so happens to be enabled instead and furthermore the label.text does not equal the value I see in the App.
State before selecting a row:
State after selecting a row:
cellForRowAtIndexPath method:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("LeftCell", forIndexPath: indexPath)
let label = cell.contentView.subviews[0] as! UILabel
let image = cell.contentView.subviews[1] as! UIImageView
if(selectedCategory.ID > 0 || searchCursusses.count > 0) {
//Category is selected, load course into cell
var cursus : Cursus
if(selectedCategory.ID > 0) {
cursus = cursusses[indexPath.row] as Cursus
} else {
cursus = searchCursusses[indexPath.row] as Cursus
}
image.image = self.chooseImage(false, name: cursus.category!.Name)
label.numberOfLines = 0
label.text = cursus.name
label.lineBreakMode = .ByTruncatingTail
if(defaults.boolForKey("offline-mode")) {
let realm = try! Realm()
let videos = realm.objects(DownloadedVideo).filter("video.cursus.ID = %#", cursus.ID)
if(videos.count > 0) {
label.enabled = true
} else {
label.enabled = false
}
} else {
label.textColor = UIColor.blackColor()
}
cell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.setSelected(false, animated: false)
} else {
let category = categories[indexPath.row] as Category
image.image = self.chooseImage(false, name: category.Name)
label.numberOfLines = 0
label.text = category.Name
label.lineBreakMode = .ByTruncatingTail
label.textColor = UIColor.blackColor()
cell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
return cell
}
didSelectRowAtIndexPath method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier( "LeftCell", forIndexPath: indexPath)
cell.selectionStyle = .None
if(selectedCategory.ID > 0 || searchCursusses.count > 0) {
let label = cell.contentView.subviews[0] as! UILabel
if(!label.enabled) {
return
}
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewControllerWithIdentifier("CursusView") as! CursusViewController
if(selectedCategory.ID > 0) {
resultViewController.loadCursus(self.cursusses[indexPath.row], completionHandler: {
self.presentViewController(resultViewController, animated:true, completion:nil)
})
} else {
resultViewController.loadCursus(self.searchCursusses[indexPath.row], completionHandler: {
self.presentViewController(resultViewController, animated:true, completion:nil)
})
}
} else {
let category = categories[indexPath.row] as Category
cell.setSelected(true, animated: false)
let label = cell.contentView.subviews[0] as! UILabel
let image = cell.contentView.subviews[1] as! UIImageView
if(label.textColor == UIColor.lightGrayColor()) {
return
} else {
image.image = self.chooseImage(true, name: category.Name)
label.numberOfLines = 0
label.text = category.Name
label.textColor = UIColor.whiteColor()
label.lineBreakMode = .ByTruncatingTail
cell.backgroundColor = UIColor(red: 45.0/255.0, green: 145.0/255.0, blue: 220.0/255.0, alpha: 1.0)
self.selectedCategory = category
self.topBarTitle.text = category.Name
let realm = try! Realm()
let cursusses = realm.objects(Cursus).filter("category.ID = %#", selectedCategory.ID)
for cursus in cursusses {
self.cursusses.append(cursus)
}
let title = self.leftMenuNav.subviews[0] as! UILabel
let titleImg = self.leftMenuNav.subviews[1] as! UIImageView
titleImg.image = UIImage(named: "back-icon")
title.text = "Cursussen"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.slideInFromRight(0.5)
self.tableView.reloadData()
self.collectionView.crossFade(0.3)
self.collectionView.reloadData()
})
}
}
}
It seems the old cells are not properly cleaned up after calling reloaddata, causing multiple cells to be at the same IndexPath.
I'm at a loss here, please help!
The problem is the line
let cell = tableView.dequeueReusableCellWithIdentifier( "LeftCell", forIndexPath: indexPath)
in the didSelectRowAtIndexPath function because it is creating a new cell (or trying to reuse one) and corrupting the table view cell cache.
You should be getting the existing cell instead using the cellForRowAtIndexPath function.
You have a problem with your approach: didSelectRowAtIndexPath is supposed to look at the data in the model, not in the view. Your code is trying, incorrectly, to access the cell and examine its labels etc. Instead, your method should be accessing the same underlying data source that has been used to make the labels in the first place.
In other words, instead of writing
let label = cell.contentView.subviews[0] as! UILabel
and then examining the enabled/disabled status of the label
you should write
cursus = cursusses[indexPath.row] as Cursus
and examine the availability of the cursus.
One general rule of thumb is that you should get very suspicious when you see code accessing components of UITableViewCell outside cellForRowAtIndexPath. Testing the state of a label is nearly universally an indication that the code is incorrect.
Man you are doing that wrong. Only tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell can dequeue cells and setup its contents.
To prevent selection you should react on func tableView(_ tableView: UITableView,
willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath and return nil to indicate that you don't want to select anything.
Main problem is that you are writing to complex methods. It is hard to figure out what are you doing and what is you intention. (will/did)SelectRowAtIndexPath should invoke only one/tow some simple methods, for example: perform a segue or load some data.

Resources