IndexPath.row of CollectionView gets changed while scrolling - swift

I am new in swift, the issue I am facing is I have a collection view of height 2000. when I scroll down to the bottom, the indexPath.row gets changed due to which the logic gets disturbed. I understood that this happens due to the reusability of cells but how should I stop this. This is how I am initializing cellForItemAt.
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "analysis", for: indexPath) as? analysisCollectionViewCell
if goalData.notesArr.count == 0 {
cell?.viewNotes.isHidden = true
cell?.constraintHeightNotesView.constant = 0
self.constraintHeightMainView.constant = self.constraintHeightMainView.constant - 400
}else{
cell?.viewNotes.isHidden = false
cell?.constraintHeightNotesView.constant = 470
}
I am changing the height of a few components(cell?.constraintHeightNotesView.constant) as well as of my mainView also(self.constraintHeightMainView.constant).

Please, edit the question so that We can see your code in full
This cell initializing method is correct
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "analysis", for: indexPath) as? analysisCollectionViewCell
But I can't understand why you need an collection view height 2000?
If you want to setup layout the collection view in full screen, You need to set constraints of collection view to the edges of the main view of your ViewController
if you collection view have a many elements, you will be able to scroll it.
This functionality is available without additional configuration.

Related

UICollectionViewDiffableDataSource cellProvider called more often than expected

I'm using the UICollectionViewDiffableDataSource to populate my UICollectionView. After receiving a list of items via REST API, I create a new snapshot and apply it like this:
DispatchQueue.main.async {
var snapshot = NSDiffableDataSourceSnapshot<RegionSection, DiffableModel>()
snapshot.appendSections(RegionSection.allCases)
snapshot.appendItems(self.spotlights, toSection: .Spotlights)
snapshot.appendItems(self.vendors, toSection: .Vendors)
self.dataSource?.apply(snapshot, animatingDifferences: animated)
}
When setting up my cells in the cellProvider, I asynchronously load images from a URL. I noticed, that the first cell would frantically flick through all the images that are loaded and end up displaying a different image than it was supposed to. (For example the image intended to be displayed by the last cell).
I decided to investigate and figured out that the cellProvider closure is called twice as many times as expected. Also the collectionView.dequeueReusableCell function behaves weirdly for the first half of the calls as it returns the same cell each time even though there are no cells in the collectionView that could be dequeued.
My cellProvider closure:
dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView, indexPath, entry) -> UICollectionViewCell? in
if let spotlight = entry as? Spotlight{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "spotlightCell", for: indexPath) as! SpotlightCell
cell.nameLabel.text = spotlight.title
cell.subtitleLabel.text = spotlight.subtitle
cell.categoryLabel.text = spotlight.type.getDescription().uppercased()
cell.imageView.loadImage(fromUrl: spotlight.titlePictureUrl)
return cell
}else if let vendor = entry as? Vendor{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "vendorCell", for: indexPath) as! VendorCell
cell.nameLabel.text = vendor.title
cell.assortmentLabel.text = vendor.assortmentDescription
cell.imageView.loadImage(fromUrl: vendor.titlePictureUrl ?? vendor.pictureUrls?.first ?? "")
if let distance = vendor.distance{
cell.distanceLabel.text = (distance/1000) < 1 ? (distance.getReadableString(withDecimalSeparator: ",", andDecimalCount: 0) + "m entfernt") : ((distance/1000).getReadableString(withDecimalSeparator: ",", andDecimalCount: 0) + "km entfernt")
}
return cell
}
return nil
}
Here is an example:
I create a snapshot containing 4 vendor entries (For simplicity I didn't add anything in the other section for this example)
The cellProvider is called 4 times (for each indexPath and entry) and the cell that is dequeued is the same one each time.
The cellProvider is called another 4 times (again, for each indexPath and entry) and this time the cells are different each time.
For each time the cellProvider is invoked I call loadImage, which tries to find an image for the URL in my image cache and if it cannot found one loads it asynchronously.
Since all calls happen almost simultaneously every image is loaded twice and the first cell displays one image after another until the last of the 4 URLSessions it initiated returns.
I can't imagine it is expected behaviour for the dataSource to call it's cellProvider closure that often and I simply can't figure out why this happens or find anything in the documentation on this.
I hope someone can explain to me why this happens and in case this is expected behaviour how to properly set up cells with asynchronous image loading using a DiffableDataSource.
EDIT:
The solution that worked for me was to use absolute instead of estimated sizes for my cells, as suggested by #Norb Braun!
Setting the estimated size to none fixed this issue for me. This solution may not work for you when you are required to use self sizing cells but if your cells keep the same size regardless the content you could give it a try.

Condition that should change configuration of a cell is not working, probably because of how the feed is populated

I am using XCode 9, and I have created a collection view programmatically. I have a function called populateFeedCollectionViewCell, where I use the data I have gotten from the server to populate each cell of my collection view. I call this fuction at cell for item.
In my populateFeedCollectionViewCell function I check if the Item has an image of not, and when it doesn't have an image, I tell the image view to be frame 1,1, by altering its constraints.
For some reason, this isn't working properly. All the items that should be altered are being altered with success, but other items are being altered as well (just some of them).
I make the xcode print me the item when it enters the condition where I alter the constraints, and the weird thing is that the items arent being printed (therefore aren't entering the condition by mistake), but are changing its constraints.
My guess is that when the override cell for item is being called, it is mistaking the index or something. But I don't know how to be sure that that is the problem nor how to fix it.
Here is my code:
Where I call populateFeedCollectionViewCell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomFeedCell
presenter.populateFeedCollectionViewCell(cell: cell, item: itemArray[indexPath.row])
cell.shareButton.tag = indexPath.row
cell.delateButton.tag = indexPath.row
return cell
}
Where I give the condition, inside populateFeedCollectionViewCell:
if item.mediaUrl == "empty"{
debugPrint("entrou em empty", "media url:", item.mediaUrl, "title:", item.title)
cell.feedImageView.heightAnchor.constraint(equalToConstant: 206.0).isActive = false
cell.feedImageView.widthAnchor.constraint(equalToConstant: 341.0).isActive = false
cell.feedImageView.widthAnchor.constraint(equalToConstant: 1).isActive = true
cell.feedImageView.heightAnchor.constraint(equalToConstant: 1).isActive = true
} else {
cell.feedImageView.widthAnchor.constraint(equalToConstant: 1).isActive = false
cell.feedImageView.heightAnchor.constraint(equalToConstant: 1).isActive = false
cell.feedImageView.heightAnchor.constraint(equalToConstant: 206.0).isActive = true
cell.feedImageView.widthAnchor.constraint(equalToConstant: 341.0).isActive = true
}
The result on my feed:
ps: notice that when it doesnt have an image it is correct, but when you see the image as a line on top of the text, the constraints were applied when they shouldnt have been. Also, when I scroll the feed, other items change their constraints to the wrong way.
Image 1 (constraints applied correctly)
Image 2 (constraints applied when shouldn't be)
Image 3 (constraints not when shouldn't be)
Since there is cell resuing inside cellForItemAt , every time the cell reused it creates other constraints which conflicts , then cause unexpected results , you need to clear all constraints of feedImageView before creating new ones
Or better to create the constraints inside xib/custom class , then hook them as outlets and play with there constants
if item.mediaUrl == "empty"{
cell.feedImageViewWidth.constant = 1.0
cell.feedImageViewheight.constant = 1.0
} else {
cell.feedImageViewWidth.constant = 341.0
cell.feedImageViewheight.constant = 206.0
}
where
var feedImageViewWidth:NSLayoutConstraint! // or #IBOulet weak var .....
var feedImageViewheight:NSLayoutConstraint!
Warning:
Don't think constraints with
.isActive = false
deactivates old ones , No they add another deactivated constraint

How to turn on editing in cell Swift 3

I have a question about cell in swift. Let's imagine that I have a cell with 20 rows. The cell is selectable right now, but the thing I want is that, when user selects 20 row it should be editable. And the value which enters the user should be saved in some variable. Is it somehow possible? Thank's in advance!
If you want 20 cells in tableView, then
You have to make nib of a cell, and make textfield in cell, and also create outlet in nib, (you can also create prototype cell)
Then in cellForRow AtIndexPath makes textField delegate as self (to class), in that class you have to implement textField delegate, and then you can access textFields as:
let indexPath = IndexPath(row: 0, section: 0)
let cell = tableView(tableView cellForRowAt: IndexPath) as! MYCELL
let text = cell.textField.text
Sorry for my bad english,
I think it will be helpful

Checking If every row in table view have image hidden or not

Is there any way to check inside a function : func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) If rest of the rows ( instead of the first one ) have image hidden or not.
If images of rows which are greater then indexPath.row - 0 are all hidden or not.
So basically I would like to get the first row ( image ) of the table view hidden when the rest one are hidden as well. The simple check boxes.
This how I'm hiding them :
let row = indexPath.row
if row > 0 {
UIView.animate(withDuration: 0.5, animations: {
let currentCell = self.engineStatusTableView.cellForRow(at: indexPath) as! DropDownViewCell
if !currentCell.checkMark.isHidden {
currentCell.checkMark.isHidden = true
} else {
currentCell.checkMark.isHidden = false
}
})
}
Thanks in advance!
It's generally not a good idea to keep your state inside of cells. Cells are reused and somewhat expensive to create, so you want to let UITableView control the creation and manage their reuse.
But, hopefully, your datasource knows if the checkmark is supposed to be hidden or not and you can ask it without having to create a cell to do that.

Use tags for tableView inside UIViewController to update Labels

I added a table view to my UIViewController. then i added a label to my custom cell inside the table view. i tried to update labels inside the tableview by using tags. seems it does not working.
this is my code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("LineItemCell", forIndexPath: indexPath)
let object = lineItemsObject[indexPath.row]
//cell.textLabel?.text = object["quantity"]
print(object["description"]!)
if let descriptionLabel = cell.viewWithTag(500) as? UILabel {
descriptionLabel.text = object["description"]
}
else
{
print("fail")
}
return cell
}
When i call the function always it does not read the tag and prints "fail". i have assigned correct tag value to my label also.
here i have attached a image of my label details in attribute inspector
Please help me the fix the issue.
As #TheAppMentor said code looks fine. Make sure you entered correct cell identifier. Then make sure you connected your view controller to correct class (it's my common mistake). Also make sure that something else in this cell hasn't the same tag. You can also print all subviews to check what's inside.
Your cell is not knowing about your label inside (the one with 500 tag). You are not providing a custom cell, because of this line:
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("LineItemCell", forIndexPath: indexPath)
This is a predefined cell. As of documentation you can have predefined cells or custom ones (obtained by subclassing UITableViewCell):
"When creating cells, you can customize them yourself or use one of several predefined styles. The predefined cell styles are the simplest option. With the predefined styles, the cell provides label and image subviews whose positions and styling are fixed. ... To set the text and images of the cell, use the textLabel, detailTextLabel, and imageView properties."
If you want to go predefined:
If you want to just put the text onto cell use (I see this one is commented on first place).
cell.textLabel?.text = object["quantity"]
Go custom
Extend UITableViewCell on a separate swift file. Do your bindings here and work with your storyboard in parallel. Assign the custom class to your cell. Also, change dequeue:
let cell: MyCustomTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("LineItemCell", forIndexPath: indexPath) as! MyCustomTableViewCell