I am trying to get the first selected cell in the collection view. It is showing nil but in the app there is cells showing up. The clientCollectionView is coming up nil too. Here is the function that i am trying to use
func currentClient() -> Client? {
let idx = self.clientsCollectionView?.indexPathsForSelectedItems?.first
guard let firstSelected = idx?.row else {
return nil
}
return clients()[firstSelected]
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clients().count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clientCollectionViewCell", for: indexPath)
if let cell = cell as? ClientCollectionViewCell {
cell.client = clients()[indexPath.row]
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
populateExerciseFields()
}
func currentClient() -> Client? {
// You haven't really explained what "clientsCollectionView" is
// I suspect you just need to replace it with a reference to the
// actual collection view, as shown below.
let idx = self.collectionView?.indexPathsForSelectedItems?.first
guard let firstSelected = idx?.row else {
return nil
}
return clients()[firstSelected]
}
See here: https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623983-collectionview
You are probably referencing some other instance ... but not the one that is associated with your collection view controller.
Related
How is it possible to change numberOfItemsInSection parameter of a collection view?
I have made a basic setup of a collection view. And now I try to make a button, that changes the amount of items in it.
The general setup is a standard one:
func numberOfSections(in collectionView: UICollectionView) -> Int {
1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
arrayA.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.image = arrayA[indexPath.item][0].image
return cell
}
The question is - how to configure a button, so it could change numberOfItemsInSection parameter from current arrayA.count to some other (e.x. arrayB.count)?
Example:
You could take a common a flag to toggle between arrayA and arrayB. When the button is clicked as
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isUseArrayA ? arrayA.count : arrayB.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.image = isUseArrayA ? arrayA[indexPath.item][0].image : arrayB[indexPath.item][0].image
return cell
}
#IBAction func changeSource(sender: UIButton) {
if sender.tag == 0 {
isUseArrayA = true
sender.tag = 1
} else {
sender.tag = 0
isUseArrayA = false
}
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.collectionView.reloadData()
}
}
// No need of numberOfSection because it is bydefault 1.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
arrayA.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.image = arrayA[indexPath.item][0].image
return cell
}
#IBAction func changeArrayCount(sender: UIButton) {
arrayA.append(image)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.collectionView.reloadData()
}
}
I am developing an application in swift where you have the possibility to filter the data displayed in the UICollectionView. After the data is filtered, collection view should update, so only items which fit the filter will be visible(eg. price is more than 30$). I can't update the collection view, I've tried literally everything...(reloadData(), deleteItems(at: IndexPath), batch, etc...) Here is the code for collection view functions:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dogsSort.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: 150)
}
internal func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedDog = dogsSort[indexPath.row]
print("selected dog " + selectedDog.name)
let vc = self.storyboard!.instantiateViewController(withIdentifier: "ItemVC") as! ItemViewController
vc.dog = selectedDog
self.navigationController!.pushViewController(vc, animated: true)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.layer.cornerRadius = 10
/*storageWizard.getImage(path: "/dogs/" + dogsSort[indexPath.row].id + "/image.png", imageCompletionHandler: { (image) -> Void in
cell.imageView.image = image
self.dogsSort[indexPath.row].image = image
return
})*/
print(indexPath.row)
if dogsSort.count > indexPath.row {
cell.btnPrice.setTitle("Get for " + String.init(self.dogsSort[indexPath.row].price) + "$", for: .normal)
cell.labelView.text = self.dogsSort[indexPath.row].name
cell.CityView.text = self.dogsSort[indexPath.row].description
cell.breedView.text = self.dogsSort[indexPath.row].breed
cell.ageView.text = self.dogsSort[indexPath.row].age
cell.weightView.text = self.dogsSort[indexPath.row].weight
}
return cell
}
And code which I am using currently, dummy code just to test the functionality, but it is not working...
dogsSort.remove(at: 0)
collectionView.deleteItems(at: [IndexPath(row: 0, section: 0)])
collectionView.reloadData()
If I understand you question this is what you have to do:
get your main array of dogs;
filter it:
filtered = dogsArray.filter({ $0.price > 30 })
If you want to filter the items based on a certain property, set again:
filtered = dogsArray.filter({$0.name == "something"})
In your CollectionView's delegate and data source methods you need to work with the filtered array, ie. (for the number of items in row):
filtered.count
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
}
}
I want to change a cell label text when I click/tap on a collectionView cell.
I tried the following way, but this not working.
#objc func tap(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: self.collectionView)
let indexPath = self.collectionView.indexPathForItem(at: location)
if let index = indexPath {
let subL = zoneDict?.sublevel[index.row]
if (subL?.sublevel.count)! > 0 {
DispatchQueue.main.async {
self.zoneDict = subL!
print("self.zoneDict --\(self.zoneDict!)")
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "colViewCell", for: index) as! CollectionViewCell
cell.zoneNameLabel.text = self.zoneDict?.name // Cannot update the text label. It show the default value
print("zone name-- \(self.zoneDict?.name)") // Its print the result.
}
self.delegate?.selectedZoneWithCellItems(items: "cell")
}
}
}
I think when you tap collectionViewCell then iOS system default call function didSelectItemAtIndexPath of CollectionView so that you must handle default event selected cell by the way register UITapGestureRecognizer for your cell and after that you must set property of view (isUserInteractionEnabled = true).
For example: self.yourview.isUserInteractionEnabled = true
you can use this in the VC containing the collection view
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = segmentCollectionView.dequeueReusableCell(withReuseIdentifier: SegmentCellId, for: indexPath) as! CollectionViewCell
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(printHello))
cell.addGestureRecognizer(tapGesture)
return cell
}
#objc func printHello(){
print("Hello")
}
You can use its delegate method in this way to change the cell label text when clicked on cell: -> Here I am using two arrays to update the text of the label as an example:
//Variables that are used below.
let nameCapitalArr1 = ["ABC", "DFG", "EFG", "HIJ", "KLM", "NOP", "QRS", "TUV", "WXY", "Z"]
let nameSmallArr2 = ["abc", "dfg", "efg", "hij", "klm", "nop", "qrs", "tuv", "wxy", "z"]
var changeFlag: Bool = false
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return nameCapitalArr1.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LabelTextCollectionViewCell", for: indexPath) as? LabelTextCollectionViewCell else { return UICollectionViewCell() }
cell.nameTextLabel.text = !changeFlag ? nameCapitalArr1[indexPath.row] : nameSmallArr2[indexPath.row]
return cell
}
/*That method is called when tapping on the cell and reload that particular cell and also change the label text.*/
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
changeFlag = !changeFlag
collectionView.reloadItems(at: [indexPath])
}
Output: -> Its reflect the cell text with capital to small array values while tapping on the cell with toggle effect.
I have UITableViewCell and I want to send the indexPath.row number to UIViewController using didSet, but xcode gives me an error when i use the value for other things (in UIViewController) says that the value is nil error: unexpectedly found null when unwrapping an optional value.
But if I print in the variable in UIViewController the value appears.
What i do? Thanks.
class TableViewCellCentral: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var CollectionData: UICollectionView!
var send = Int() {
didSet{
ViewController().reload = send
}
}
override func awakeFromNib() {
super.awakeFromNib()
CollectionData.dataSource = self
CollectionData.delegate = self
CollectionData.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
cell.LabelData.text! = "number \(indexPath.row)"
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
send = indexPath.row
CollectionData.reloadData()
}
}
ViewController() is just an instance of your ViewController. What you're doing when you say ViewController() is you're creating a new ViewController, rather than accessing the one you're looking for. If you want the indexPath.row, you need to send it to the actual VC, not just an instance of it.
You need to keep in mind that not all data is loaded instantly and at the same time, also that it does not always load in the order that you expect it to. It is possible that you are asking for the data before it is even assigned. Try
import UIKit
class TableViewCellCentral: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var CollectionData: UICollectionView!
var send = Int() {
didSet {
ViewController().reload = send
}
}
override func awakeFromNib() {
super.awakeFromNib()
CollectionData.dataSource = self
CollectionData.delegate = self
CollectionData.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
if indexPath.row != nil {
cell.LabelData.text! = "number \(self.indexPath.row)"
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
send = indexPath.row
CollectionData.reloadData()
}
}
I am only printing if the value is not nil! Let me know if you have more questions, or let us know if my solution is not working. =)