Wrong images loaded after scrolling in collectionView swift 3 - swift

I am having issues with my collectionView in Swift 3. I have found some tips on Stackoverflow, tried them out, but alas to no avail. Many mentioned to use the 'prepareReuse' method, but I could not get those to work in my code. After Scrolling down, and back up again, the images have changed. All the images are letters of the alphabet. So A,B,C are the first images to appear at the top of the view. If you scroll down, and back up, some random other letters have taken their place. My entire code is as follows:
import UIKit
class colViewController2: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var imageData: [String] = [String]()
var imageCounter: Int = 0
var userHasHieroglyph: NSArray = ["","","C","D","E","F","G","H","I","J","K","L","M","N","O","","Q","R","S","T","U","V","W","X","Y","Z"]
override func viewDidLoad() {
super.viewDidLoad()
imageData = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
for b in imageData {
if userHasHieroglyph.contains(b) {
let newHieroglyph = b.lowercased()
imageData[imageData.index(of: b)!] = "h-"+newHieroglyph
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellid", for: indexPath) as! MyImageCell
cell.backgroundColor = UIColor.white
var currImage:String = ""
currImage = self.imageData[self.imageCounter]
self.imageCounter += 1
if self.imageCounter >= self.imageData.count {
self.imageCounter = 0
}
cell.image.image = UIImage(named: currImage)
return cell
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 26
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 90, height: 90)
}
}
I hope I am missing some small bit of code to solve this problem, but after many hours of searching on Stackoverflow, and the internet, I still can not find a solution to this problem. If anyone has a solution or tip, it would be greatly appreciated!
Greetings

The issue is that you're using imageCounter as the index into your array of images, but incrementing it instead of using the indexPath.item. Remember that the UICollectionView will reuse UICollectionViewCell instances. Basically, it will only create cell instances for those that are on the screen. If a cell scrolls off the screen and a new one takes its place (e.g. if you have "A", "B", and "C" on the screen, and scroll down so you see "B", "C", "D", the UICollectionView will reuse the "A" cell for "D". This is a bit of an oversimplification, but more or less how it works). As such, the cellForItem call will give you the information about which cell is being displayed in the indexPath parameter. In this case, you can get rid of all of the self.imageCounter logic and instead do cell.image.image = UIImage(named: imageData[indexPath.row])
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellid", for: indexPath) as! MyImageCell
cell.backgroundColor = UIColor.white
cell.image.image = UIImage(named: imageData[indexPath.row])
return cell
}

Related

Improper width of cell in collectionview

[updated]
I have created collectionview containing cells with different captions and different widths. It works fine when I read collection on launch of application.
But when I add new cell during usage of the application it has standard, narrow, width.
When I again relaunch the application it will again have correct width.
After adding reloadData() it works fine for one cell. But when I have multiple cells they are drawn one on each other.
And here is the code:
override func viewDidLoad() {
super.viewDidLoad()
projectCollectionView.delegate = self
projectCollectionView.dataSource = self
projectCollectionView.register(UINib(nibName: "projectCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "projectCollectionViewCell")
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: projectCollectionViewCell.identifier, for: indexPath) as? projectCollectionViewCell
else {
return projectCollectionViewCell()
}
cell.projectButton.setTitle("a title", for: .normal)
projectCollection[indexPath.row].cellIndex = indexPath.row
cell.projectButton.sizeToFit()
cell.layer.bounds.size.width = cell.projectButton.layer.bounds.width
return cell
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
projectCollectionView.collectionViewLayout.invalidateLayout()
projectCollectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var result: Int = 0
for i in 0...projects.count-1 {
if (projects[i].status>=2) {
result += 1
}
}
return result
}
When I remove the row: cell.projectButton.sizeToFit() it started to look like this:
try it :-
relod collectionview after adding element.
collectionview.reloaddata()
[edited]
add flowlayout to collectionview
add below code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let label = UILabel(frame: CGRect.zero)
label.text = textArray[indexPath.item]
label.sizeToFit()
return CGSize(width: label.frame.width, height: 32)
}
for more visit https://stackoverflow.com/a/53284536/12451658

UICollectionViewCell is not rounding corners inside UITableView

The problem is that when I have UICollectionView inside UITableViewCell - UICollectionViewCell cornerRadius function not working.
Code is very simple and it was always working for me when playing with UICollectionView.
extension ExploreTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ExploreCollectionViewCell
cell.thumbnail.layer.cornerRadius = 20
cell.thumbnail.clipsToBounds = true
return cell
}
Is it a bug or what? Cannot find any answer all day. Most of the people suggest to use contentView instead of the specific image inside the cell but that's not for me since I want only image to be rounded.
P.S this one works (but I want only image inside the cell to be rounded):
override func awakeFromNib() {
super.awakeFromNib()
self.layer.cornerRadius = 30
self.clipsToBounds = true
}

UICollectionViewCell is populated with previous cells data before new the cell's data loads, need to implement PrepareForReuse()

I have a problem with my UICollectionView, The problem is that if you scroll fast enough (not even that fast) the data that was in the cell before the reuse gets displayed for a second or two before the new data gets displayed.
This is the video of it happening for more context (youtube video link): https://youtu.be/I63hBuxBGI0
This is the code inside of the cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let post = feedElements[indexPath.row] as? FeedPost {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! feedCollectionViewCell
cell.delegate = self
cell.post = post
cell.commentButton.tag = indexPath.row
// assigning all of the data
cell.captionLabel.text = post.caption
cell.locationLabel.text = post.location
cell.timeAgoLabel.text = post.timeAgoString
cell.setUpElements()
cell.prepareForReuse()
return cell
}
}
The code for inside of the numberOfRowsInSection:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return feedElements.count
}
I'm pretty sure what I need to implement is prepareForReuse() but I'm not sure exactly how to do that and couldn't find anything helpful online.
Thanks a lot!
You need to add prepareForReuse in feedCollectionViewCell class like this
class feedCollectionViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
// Reset your values here as you want ...
self.post = nil
self.commentButton.tag = 0
// assigning all of the data
self.captionLabel.text = nil
self.locationLabel.text = nil
self.timeAgoLabel.text = nil
}
}

UICollectionViewCell not shown properly

I have the following code. For some strange reason, the cell is not registering properly and the cell is not rendered.
I expect there to be 5 purple cells but instead there is only red background.
class RateController: UICollectionViewController {
var user: User?
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .red
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = .purple
return cell
}
}
There's nothing wrong with your code. I pasted it right into a fresh project, defined the User class, made the necessary adjustments in the storyboard, and ran it:
So if there's a problem, it's somewhere else — not in that code!
I've been thinking about where else the problem could be; the problem you are describing would happen if you disabled the Flow layout in the storyboard and replaced it with an empty Custom layout, like this:
If you did that, set the Layout back to Flow.

Swift - UICollectionView reloadItems takes too long

I want to reload only single cell after user select the contact (method didSelect contact). If the number of item in cell getting bigger, the process is getting slower. I also try to implement in DispatchQueue.main.async { code } and it still take some time to load.
cellForItemAt and noOfItemInSection (AddCell) - Total cell 4
var indexPaths = [IndexPath]()
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
indexPaths.append(indexPath)
if indexPath.item == 1 {
let addCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId, for: indexPath) as! AddCell
addCell.btnAdd.addTarget(self, action: #selector(handleOpenContact), for: .touchUpInside)
addCell.invitees = invitees
return addCell
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
cellForItemAt and noOfItemInSection (SubAddCell)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: subAddId, for: indexPath) as! SubAddCell
cell.delegate = self
cell.invitee = invitees[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return invitees.count
}
didSelect (ContactPicker)
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let invitee = Invitee()
invitee.name = contact.givenName
if contact.isKeyAvailable(CNContactPhoneNumbersKey) {
for phoneNumber: CNLabeledValue in contact.phoneNumbers {
let a = phoneNumber.value
print("\(a.stringValue)")
invitee.phoneNo = a.stringValue
}
}
self.invitees.append(invitee)
for index in self.indexPaths {
if index == [0, 0] {
print(index)
self.collectionView?.reloadItems(at: [index])
}
}
}
I have obtain the value of indexPaths at cellForItemAt method. If the invitee contains 5 items, the print(index) will print 19 values. I dont know why. Below is the image.
Design of the UI
How to optimize the reloadItems?
Any help is really appreciated. Many thanks.
I think you have this indexPaths.append(indexPath) in cellForItemAt so that why the indexPaths can increase after reload data. You might use NSSet instead of NSArray to make sure your objects are unique.
And with this: cell.invitee = invitees[indexPath.item], did you implement didSet for invitee?
If you did, I think need to prepare data in background queue first. E.g.
DispatchQueue(label: "queue").async {
let image = <# process image here #>
let name = <# process name here #>
DispatchQueue.main.async {
self.imageView.image = image
self.label.text = name
}
}
Hope my tips could help you!
Collectionviews are the most powerful UI tool of iOS, but if done wrong, or if misused (which happens, don't worry), they can be worse for your UX. . To view how this stuff is affected read up on XCode Tools and how to use them to debug your UI performance.
Important note to consider:
frame-by-frame operations (ie, loading your collectionviewcell, expanded, or unexpanded) should take no more than 17 ms to execute for smooth scrolling.
UICollectionView performance - _updateVisibleCellsNow
So you need to make your cells as simple and efficient as possible. That's the best place to start, especially if your expandable cells themselves contain complex subviews like collectionviews.