My keyboard extensions which consists of a UICollectionView of UIImages keeps crashesing on iOS12 after exceeding 53MB of RAM
collectionView:willDisplay cell is called on all cells, regardless if they are shown
NOTE: didEndDisplaying gets never called
func collectionView(_ collectionView: UICollectionView, willDisplay c: UICollectionViewCell, forItemAt indexPath: IndexPath)
can i bring back the behaviour of collectionView:willDisplay so its called only when the cell is really displayed?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: EmojiCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "EmojiCollectionViewCell", for: indexPath) as! EmojiCollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay c: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = c as! EmojiCollectionViewCell
let imageName = String(format:"%#-%#", categories[selectedCategory], NSNumber(value: indexPath.row + 1));
let emojiImage = UIImage(named: imageName)
cell.setupWithEmojiImage(image: emojiImage)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying c: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = c as! EmojiCollectionViewCell
cell.setupWithEmojiImage(image: nil)
}
You're probably calling reloadData() or something within a tableview datasource method, causing an infinite loop. what is the code in setupWithEmojiImage(image: nil)? I think you should place the following in cellForRow instead of willDisplay:
let imageName = String(format:"%#-%#", categories[selectedCategory], NSNumber(value: indexPath.row + 1));
let emojiImage = UIImage(named: imageName)
cell.setupWithEmojiImage(image: emojiImage)
Also avoid using the bang operator (!) to force unwrapping, this leads to crashing if the object doesn't exist. Do this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell: EmojiCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "EmojiCollectionViewCell", for: indexPath) as? EmojiCollectionViewCell {
return cell
}
return UITableViewCell()
}
Related
How to change the color of view in collection view when it select and when the select the other view before view came to original color How to set it?
You can use the didSelectItemAtIndexPath method and the didDeselectItemAtIndexPath method from the UICollectionViewDelegate protocol. UICollectionViewDelegate is an Objective-C protocol, indicated by #objc, that allows you to have optional methods like didSelectItemAtIndexPath and didDeselectItemAtIndexPath that you can choose to implement optionally in your delegate.
When UICollectionView gets instantiated initially, it goes through all the available methods you've implemented in the instance of UICollectionViewDelegate with responds(to:), which takes a selector parameter and returns a bool, to see which methods from the UICollectionViewDelegate protocol you've implemented without actually sending a message to them. As it detects the following methods, it remembers them and calls those methods whenever you select or deselect a cell from the collection view:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let indexPaths = collectionView.indexPathsForSelectedItems, let firstIndex = indexPaths.first {
cell.backgroundColor = firstIndex == indexPath ? .white : .gray
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = .white
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) else {
return
}
cell.backgroundColor = .gray
}
Alternatively, you can use the collectionView(_:didHighlightItemAt:) method and the collectionView(_:didUnhighlightItemAt:) method. The following sample code is from here:
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = #colorLiteral(red: 1, green: 0.4932718873, blue: 0.4739984274, alpha: 1)
}
}
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
You can simply add two methods for changing colour of the view or cell in the collectionView:-
//When cell got selected
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath){
cell.backgroundColor = .green
}
}
//When cell deselected
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath){
cell.backgroundColor = .clear
}
}
If you have view inside your cell as backgroundView then you can use same functions and just need to replace cell.backgroundView.backgroundColor in place of the cell.backgroundColor. Just remember "backgroundView" is the name of the view you have inside cell.
If you want more information about this then you can follow below attached links:-
1). https://www.tutorialfor.com/questions-296884.html
2). https://en.it1352.com/article/fccf358a040b4ddba872d7324602754c.html
In my match app Xcode project I have an error stating:
Use of unresolved identifier 'cell'.
My Code:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
#IBOutlet weak var collectionView: UICollectionView!
var model = CardModel()
var cardArray:[Card] = []
override func viewDidLoad() {
super.viewDidLoad()
//call the getCards method of the card model
cardArray = model.getCards()
collectionView.delegate = self
collectionView.dataSource = self
}
//mark: -UICollectionView Protocol Methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardArray.count
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//get a cardCollectionViewcell object
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCollectionViewCell
//get card that
let card = cardArray[indexPath.row]
cell.setCard(card)
return
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
_ = collectionView.cellForItem(at: indexPath) as!CardCollectionViewCell
//flip the card
cell.flip() <--------- THIS IS THE ERROR
Once the error is fixed I am supposed to be able run a match app example on the fake iPhone. It would allow me to flip the cards on a click.
I think you forgot to set reusable identifier
I hope this will be resolve your issue.
To access that cell method you have to declare variable
let cell = collectionView.cellForItem(at: indexPath) as!CardCollectionViewCell
First of all you are misusing collectionView:willDisplay:forItemAt. Move everything into collectionView:cellForItemAt, replace return with return cell and delete willDisplay:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCollectionViewCell
//get card that
let card = cardArray[indexPath.row]
cell.setCard(card)
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { ... }
The error is pretty clear: You never declared cell is the scope of didSelectItemAt. Change
let cell = collectionView.cellForItem(at: indexPath) as! CardCollectionViewCell
I am practicing some coding on collectionViews. However I like to work in Main.storyboard to see what I am working with visually. Unfortunately it appears that makes more work on my end when working in the viewController because this would have been much faster if done programmatically.
I am trying to call for multiple reuse identifiers for the collectionViewCell. However I only know how to call one cell. Now I am not getting any errors, I just do not know how to display all of the cells when I run the program.
Here is the code.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Fashion Cell 0", for: indexPath)
return cell
}
When I run the app I get this.
Please note, in Main.Storyboard I am add the cells manually.
Example:
Do in this way
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Fashion Cell 0", for: indexPath)
return cell
}
if indexPath.item == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Fashion Cell 1”, for: indexPath)
return cell
}
//……
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Fashion Cell 0", for: indexPath)
return cell
}
Although you can use switch instead of if for better readability
You can not in this way.Create one array of your number of images and set it to collectionView. No need to more cell in storyboard.
let video:[UIImage] = ["","","",""]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return video.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)as! yourcellfile
let image = video[indexPath.row]
cell.img.image = image
return cell
}
You can go this way. It's proper method to imaplement collectionview.
I have a collectionView of images and I want to show when multiple of these cells are selected.
I use a simple cell.myUIImage.isHidden = false but nothing seems to work.
Multiple select is enabled in the Interface Builder and is set in viewDidLoad(), this is what I have:
#IBOutlet weak var myUIImage: UIImageView!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as! PhotoFeed
cell.myUIImage.isHidden = false
print ("cell was selected at:", indexPath.item)
photoCollection.reloadData()
}
You can not access UICollectionViewCell with dequeueReusableCell other than cellForItemAt indexPath method.
To access cell in didSelectItemAt indexPath use:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let collectionCell = collectionView.cellForItem(at: indexPath) as? PhotoFeed {
collectionCell.myUIImage.isHidden = false
print ("cell was selected at:", indexPath.item)
photoCollection.reloadData()
}
}
But again photoCollection.reloadData() will reload the cell and myUIImage will be hidden again.
So you need to maintain the selected indexes and show/hide in cellForItemAt indexPath. With the help of selectedIndexes you can have the functionality like show image on selection and hide image on select again.
You should have an array of selectedIndexes. Say
var selectedIndexes = [IndexPath]()
Add in didSelectItemAt indexPath, Update method as:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if selectedIndexes.contains(indexPath) {
if let index = selectedIndexes.index(of: indexPath) {
selectedIndexes.remove(at: index)
}
} else {
selectedIndexes.append(indexPath)
}
photoCollection.reloadData()
}
In cellForItemAt indexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {\
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as? PhotoFeed else {
fatalError("cell not found")
}
if selectedIndexes.contains(indexPath) {
cell.myUIImage.isHidden = false
} else {
cell.myUIImage.isHidden = true
}
return cell
}
You should create #IBOutlet for myUIImage in collectionviewcell:
class PhotoFeed: UICollectionViewCell {
#IBOutlet weak var myUIImage: UIImageView!
}
In ViewController:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as! PhotoFeed
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! PhotoFeed
cell.myUIImage.isHidden = !cell.myUIImage.isHidden
}
I am modally presenting a viewcontroller that hosts a viewcollection with "pre-selected" cells formatted to a different background color based on data passed on segue.
When I tap on one of these "pre-selected" cells, it takes two taps to trigger the didDeselectItemAt delegate method. I understand why this is happening while debugging, where the cell although of different color is not necessarily recognized in a selected state. Is there any way to trigger didDeselectItemAt first for the "pre-selected" cells?
I've tried, within the delegate method cellForItemAt, to incorporate, as part of a conditional statement that changes the cell background color, setting cell.isSelected = true. Similarly within the same delegate method, I've also tried invoking a function that would invoke the delegate method didSelectItemAt with indexPaths of these "pre-selected" cells. Both produced the same result.
Below is (abbreviated) relevant code snippets:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! ExampleCollectionViewCell
if preselectedDataPoints { cell.backgroundColor = blue }
else { cell.backgroundColor = white }
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = white
preselectedDataPoints.remove(at: existingDataPoint)
}
Programatically call collectionView.deselectItem(at: indexPath, animated: true) in didSelectItem if cell is preselected.
Refrer code
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if preselectedDataPoints {
collectionView.deselectItem(at: indexPath, animated: true)
}else{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
}
or
Directly call what ever code you need to execute in deSelect method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if preselectedDataPoints {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = white
preselectedDataPoints.remove(at: existingDataPoint)
}else{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
}