CollectionView cell inside UITableView is repeating - ios

I have one table view in which every cell contains one collection view!
1) display only two cell in collectionview as my issue is when we scroll the tableview, collectoinview cell content is repeated in many other cell of tableview
myCode:
let tags = Tblarr[indexPath.row]["hashtags"] as! [String]
if tags.count != 0
{
for var i in 0 ... tags.count - 1
{
if i < 2
{
cell.tagsView.addTag(tags[i])
cell.collectionView.isHidden = false
}
}
if tags.count > 2
{
cell.lblCount.isHidden = false
cell.lblCount.text = "+\(tags.count - 2)"
}
else
{
cell.lblCount.isHidden = true
}
}
else{
cell.lblCount.isHidden = true
cell.collectionView.isHidden = true
}
cell.fillCollectionView(with: cell.tagsView.tags)
cell.widthConstraint.constant = cell.collectionView.contentSize.width
here I get only fixed width of collectionview without setting width according to content size & collectionview cells are repeated in other cell of tableview, even if cell is one , it will show 2 cell(when scrolled)
when load for the first time
when tableview is scrolled
//for storing array in collectionview from tableview
func fillCollectionView(with array: [String]) {
self.collectionArr = array
self.collectionView.reloadData()
}

reload collectionView inside tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
cell.fillCollectionView.reloadData()
}
func fillCollectionView(with array: [String]) {
self.collectionArr.removeAll
self.collectionArr = array
self.collectionView.reloadData()
}

I don't know what fillCollectionView does but it looks like you didn't consider that the table view cells are reused. So every time you call fillCollectionView you have to reset the content of your cell or the content or your collection view.

Related

After tableView scrolled data puts in cells in wrong order

in my View:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TransactionTableCell", for: indexPath) as! TransactionTableCell
let newItem = getTransactionsInSection(section: sectionHeader[indexPath.section])[indexPath.row]
cell.configure(item: newItem)
}
in my TransactionTableCell
func configure(item: TransactionModel) {
guard let withdrawalBonuses = item.withdrawalBonuses,
withdrawalBonuses < 0,
let accruedBonuses = item.accruedBonuses,
accruedBonuses > 0 else {
configureWithOneOperation(item)//shows one line of operation
return
}
//show 2 lines of operations
firstOperationAmountLabel.text = "+\(Int(accruedBonuses))"
secondOperationAmountLabel.text = "\(Int(withdrawalBonuses))"
}
When I scroll the cell , second operation line is appears in wrong cells where its shouldn't be, even If I reload my table , that also has this problem.
You should use prepareForReuse() method
Simply just clear data of your labels:
override func prepareForReuse() {
super.prepareForReuse()
firstOperationAmountLabel.text = nil
secondOperationAmountLabel.text = nil
}
There are few things to check here.
Make sure you reset all fields before configure a new cell.
If you have created a cell using xib or storyboard, make sure you haven't filled labels with static text.
Is your guard statements passing for every item?
Else block for guard configures cell with a single operation, Is it handling all ui elements in cell?

How to reload selected cell in particular section for expand/collapse in tableview Swift

I am doing expand/collapse tableview cells feature in my iOS app. I have multiple sections. And each section has multiple cells. By default, cell height is 100, once user taps on cell, I am increasing height to 200.
So, Based on Bool value, I am changing it. But, While scrolling tableview, It is interchanging the expanded/collapse cells in between sections.
Like if I tap on first section first cell, It is expanding, but after scrolling tableview, Second section first cell also expanding.
My Requirement is, If user tap on particular cell, that cell only should expand/collapse. User can manually expand and close. User can expand multiple cells.
So, I have tried to store Indexpath row and Section.
var expandedIndexSet : IndexSet = []
var expandedIndexSection : IndexSet = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"cellIdentifier", for:
indexPath) as! MyTableViewCell
if expandedIndexSet.contains(indexPath.row) && expandedIndexSection.contains(indexPath.section) { // expanded true
cell.height = 200
//some other data loading here
}
else { //expanded false
cell.height = 100
}
}
#IBAction moreButtonTapped(_ sender: Any) {
if(expandedIndexSet.contains(indexPath.row)) && expandedIndexSection.contains(indexPath.section){
expandedIndexSet.remove(indexPath.row)
expandedIndexSection.remove(indexPath.section)
} else {
expandedIndexSet.insert(indexPath.row)
expandedIndexSection.insert(indexPath.section)
}
entriesTableView.beginUpdates()
entriesTableView.reloadRows(at: [indexPath], with: .none)
entriesTableView.endUpdates()
}
Anyone can give better approach than this?
If you store section and row independently in separate arrays, your algorithm will fail.
The reason is that both are dependent:
Think of three expanded cells (row:1, section:1), (row:2, section:1), (row:3, section:2)
Now what happens for the cell (row:3, section:1)?
The row-array contains the value "3", and the section-array contains value "1", therefore it will be considered as expanded.
Therefore, you need to store the index path as a whole - see the sample code:
var expanded:[IndexPath] = []
expanded.append(IndexPath(row:1, section:1))
expanded.append(IndexPath(row:2, section:1))
expanded.append(IndexPath(row:3, section:2))
let checkPath = IndexPath(row:3, section:1)
if (expanded.contains(checkPath)) {
print ("is expanded")
} else {
print ("collapsed")
}
Update
So in your button handle, you'll do the following:
#IBAction moreButtonTapped(_ sender: Any) {
if(expanded.contains(indexPath)) {
expanded.removeAll { (checkPath) -> Bool in
return checkPath == indexPath
}
} else {
expanded.append(indexPath)
}
entriesTableView.beginUpdates()
entriesTableView.reloadRows(at: [indexPath], with: .none)
entriesTableView.endUpdates()
}

UITableView.reload leaves old cells in TableView but hidden

I have a UITableView used to show search results. As I type, I’m calling Tableview.reloadData(). Visually, everything works. As I begin typing, I show up to 5 matches and as I go below that, the list will show fewer items correctly. Here are the how the cells are created and number of rows reported.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "placeCell") as! PlaceCell
if shouldShowSearchResults {
let place = filteredPlaces[indexPath.row]
cell.dataSource = place
} else {
let place = allPlaces[indexPath.row]
cell.dataSource = place
}
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowSearchResults {
vlog?.debug("Number of FILTERED rows in PlacesTableView: \(filteredPlaces.count)")
return filteredPlaces.count
} else {
vlog?.debug("Number of unfiltered rows in PlacesTableView: \(allPlaces.count)")
return allPlaces.count
}
}
Since the PlaceCell is a custom class, here are some details of it:
// I've omitted labels, etc.
class PlaceCell: UITableViewCell {
var dataSource : PlaceView? {
didSet {
if let ds = dataSource {
self.isAccessibilityElement = true
self.accessibilityLabel = ds.getAccessibilityLabel()
} else {
self.isAccessibilityElement = true
self.accessibilityLabel = nil
}
}
}
weak var delegate : PlaceCellDelegate? = nil
override func prepareForReuse() {
self.isAccessibilityElement = false
self.accessibilityLabel = nil
super.prepareForReuse()
}
}
I began noticing a problem when UI Tests using Google's Earl Grey began failing due to multiple cells with the same Accessibility Label. Visually, I didn't understand why this was failing since there was only one cell visible that matched.
Upon inspect the views using Reveal, it seems that, as the count of cells drops below what was the maximum of 5, the old cells are still in the TableView, but hidden. So there is a hidden cell that used to be displaying the same data as is displayed by a different cell.
Any idea why this would be happening? This has worked for a number of months and I'm not sure what's changed.
It is always perilous when you traverse the view hierarchy; things can change, and perhaps that is what has happened here.
Regardless, you can make your test more robust by only selecting the visible item with the required label by using grey_sufficientlyVisible
Something like:
grey_allOf(grey_accessibilityLabel("Whole Foods Market, East Mayo Boulevard, Phoenix"), grey_sufficientlyVisible(), nil)

Dynamic table view cell height for embedded collection view that is filled after dequeue of table view cell

I have a collection view inside a table view cell at row = 1, that is loaded with content from Firebase Database after the cell is already dequeued. Therefore, using AutoDimensions on the cell at heightForRowAt: doesn't work since at that time, there is no content within the collection view. The collection view is anchored to all sides of the cell. What's the best way of updating the table view cell height at that specific index path when the collection view has loaded all of the data. Thanks!
In the VC containing the table view, I've set up a func retrieving a list of users from the DB that is called in viewDidLoad(), and I've set up protocols to reload the collectionView that is located in the tableView cell class.
fileprivate func retrieveListOfUsers() {
print ("fetching users from database")
let ref = Database.database().reference()
guard let conversationId = conversation?.conversationId else {return}
ref.child("conversation_users").child(conversationId).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String:AnyObject] else {return}
for (key, _) in dictionaries {
let userId = key
Database.fetchUsersWithUID(uid: userId, completion: { (user) in
self.users.append(user)
DispatchQueue.main.async {
self.reloadDelegate.reloadCollectionView()
}
})
}
}, withCancel: nil)
}
This is the code for dequeuing the cell. I've removed the code for the first cell in this post since it's irrelevant and didn't want to make you read the unnecessary code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Code for cell in indexPath.row = 0
if indexPath.row == 1 {
let usersCell = tableView.dequeueReusableCell(withIdentifier: usersCellId, for: indexPath) as! UsersTableViewCell
usersCell.backgroundColor = .clear
usersCell.selectionStyle = .none
usersCell.collectionView.delegate = self
usersCell.collectionView.dataSource = self
self.reloadDelegate = usersCell
usersCell.users = users
return usersCell
}
}
If I return UITableView.automaticDimension the collection view doesn't show. It only shows if I set the func to return a constant like 200. I've set estimatedRowHeight and rowHeight in viewDidLoad().
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return UITableView.automaticDimension
}
return UITableView.automaticDimension
}
After you load the data source for the collection view, you can update specific rows in your table like this:
UIView.performWithoutAnimation {
tableView.reloadRows(at: [indexPath], with: .none)
}
For the row height - try to remove the method implementation, it should be enough if you already set a value for estimatedRowHeight.

Display image when UITableView is empty

I'm trying to display an image when my UITableView is empty, but for some reason the code won't run when the tableView is empty, though it's fine when there are cells:
// Configures cell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject!) -> (PFTableViewCell!) {
let cell = tableView.dequeueReusableCellWithIdentifier("upcomingCell", forIndexPath: indexPath) as! UpcomingTVCell
cell.configureCell(object)
//makes it so the separators won't dissapear on us
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
if (self.objects?.count == nil) {
println("test")
UIGraphicsBeginImageContext(self.view.frame.size);
UIImage(named: "homeZero")?.drawInRect(self.view.bounds)
var backImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
self.tableView.backgroundColor = UIColor(patternImage: backImage)
} else {
self.tableView.backgroundColor = UIColor.whiteColor()
println("this other code ran")
}
return cell
}
I've tried self.objects?.count == 0, I've tried using numberOfRowsInSection, and I've tried visibleCells.
What am I missing?
If there is no objects in the datasource, the function above will not be called. Hence you need to find another place to do it. If your datasource is static (no delete or add operations), I suggest to check if the datasource is empty in viewDidLoad. If the datasource could be changed, then I suggest to do it in the function where you delete the object from datasource. Every time you delete an object, you check the datasource, if it becomes empty then show the image.
In your numberOfRowsInSection delegate check self.objects?.count if it is equal to 0 (no data) then return 1 else return the count as the number of rows in your table. This will allow the cellForRowAtIndexPath delegate to fire, even where there is not data so you can show your image.
I subclass UITableView with slight modification. If a section contains no cells, then the table shows placeholder view.
class PlaceholderTableView: UITableView {
#IBOutlet var placeholder: UIView?
#IBInspectable var emptiableSection:Int = 0
override func reloadData() {
super.reloadData()
let count = self.numberOfRowsInSection(emptiableSection)
self.placeholder?.hidden = (count != 0)
}
}

Resources