I have a custom Collection view in a Viewcontroller , whenever my Collection View is loaded always I get my first cell empty. How to remove this empty cell.
//CollectionView functions
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionCell
let collectionData = self.collectionData[indexPath.row]
cell.NameLbl.text = collectionData["name"] as? String
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let Info: NSDictionary = self.collectionData[indexPath.item] as! NSDictionary
let vc = self.storyboard?.instantiateViewController(withIdentifier: "edit") as! editVC
self.navigationController?.pushViewController(vc, animated: false)
}
Depends on what is the case that you should handle hiding the first element, there are tow options that you might want to implement:
1- If it is okay to remove the first object from the data source (collectionData array), then you can simply remove the first element from it:
In your view controller (viewDidLoad()) you could implement:
override func viewDidLoad() {
.
.
.
collectionData.remove(at: 0)
.
.
.
}
2- if you are required to keep the collectionData as is without removing the first element, but it should not be displayed in the UI, you will need to implement the UICollectionViewDataSource as follows:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// subtract 1
return collectionData.count - 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionCell
// add 1 to indexPath.row
let collectionData = self.collectionData[indexPath.row + 1]
cell.NameLbl.text = collectionData["name"] as? String
return cell
}
That should lead to displaying the collection view as expected without editing the collectionData data source array.
Related
I want to create a collectionView with two different cells. The first cell should be displayed one time and the second should be displayed as often as the array is large. The result should be something like in the image in the attached link. Here is also a example code for better understanding my problem. Thanks to everyone who helps me!!! 🙂
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch indexpath { // No indexpath in numberofitemsinsection!
case 0:
return 1 //display one time the first cell
default:
return images.count // display as often as the array is large the second cell
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.row {
case 0:
let cell = imageCollectionView.dequeueReusableCell(withReuseIdentifier: "addImageCell", for: indexPath)
return cell
default:
let cell = imageCollectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
cell.imageView.image = images[indexPath.row]
cell.delegate = self
cell.selectedAtIndex = indexPath
return cell
}
}
Here is the collectionView I want to create
You can achieve this inside cellForItemAt and you need to change numberOfItemsInSection like below:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return 1 more than our data array (the extra one will be the "add item" cell)
return dataSourceArray.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// if indexPath.item is less than data count, return a "Content" cell
if indexPath.item < dataSourceArray.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath) as! ContentCell
// configure your ContentCell: cell. <attribute>
return cell
}
// past the end of the data count, so return an "Add Item" cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddItemCell", for: indexPath) as! AddItemCell
// configure your AddCell: cell. <attribute>
return cell
}
For that you need to create a ContentCell and AddItemCell and also have a dataSourceArray to store all data you need.
Why don't you use this?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1 + theArray.count
}
I am new to iOS and I want to implement a UICollectionView inside a UITableView which can have multiple select/unselect in section 1 of the UITableview. And in section 2, only a single selection is allowed. And save the selection sate event if I dismiss the Viewcontroller and when I open it again, it must show the last selected cell as highlight.
I searched for tutorials but all of them don't mention to select/unselect the state of collection cell or saving state after dismissing view controller.
Can someone help to implement it?
Thank in advance!
Here is my code I do till now:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clvData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clvCell", for: indexPath) as! demoCollectionViewCell
cell.title.text = clvData[indexPath.item] as? String
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .red
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .white
}
you guys can also check my project here:
https://mega.nz/#!xRs0EQyQ
try this and let me know if you have any problem or if this solved your problem.
var arrSelectedIndex:[IndexPath] = []// store this array either in database by api or in local
var clvData:[String] = []// your data array
//get the arrSelectedIndex from default in viewDidLoad before reloading the table and collection.
override func viewDidLoad() {
super.viewDidLoad()
if let myArray = UserDefaults.standard.array(forKey: "selectedArray") as? [IndexPath] {
arrSelectedIndex = myArray
} else {
arrSelectedIndex = []
}
}
// and save the arrSelectedIndex in viewWillDisappear method
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UserDefaults.standard.set(arrSelectedIndex, forKey: "selectedArray")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clvData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clvCell", for: indexPath) as! demoCollectionViewCell
cell.title.text = clvData[indexPath.item] as? String
if arrSelectedIndex.contains(indexPath) { // You need to check wether selected index array contain current index if yes then change the color
cell.backgroundColor = UIColor.red
}
else {
cell.backgroundColor = UIColor.white
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .red
if !arrSelectedIndex.contains(indexPath) {// if it does not contains the index then add it
arrSelectedIndex.append(indexPath)
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .white
if let currIndex = arrSelectedIndex.firstIndex(of: indexPath) {// if it contains the index then delete from array
arrSelectedIndex.remove(at: currIndex)
}
}
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 am using Swift4 and Xcode9.
I create my collectionView cells by using the delegate method below;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
return cell
}
Then, I want to replace the image of selected cell with a "stop" image. To achieve this, I have tried to use didSelectItemAt method;
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (mediaPlayer?.isPlaying == true) {
// stop the player
mediaPlayer?.stopPlayingMedia()
} else {
// change the image and start playing
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
cell.mediaItemCellImageView.image = UIImage(named: "stop.png")
...
...
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
// stop playing
mediaPlayer?.stopPlayingMedia()
}
Now, when I select the cell which is already selected (hence the original image has already been replaced with "stop.png") the code above works; i.e. the original image is restored, which is done by stopPlayingMedia() method.
When I select a cell different than the currently selected one, the App crashes. I tried to move the didSelectItemAt call out of the delegate method, but it did not work.
Surprisingly, the logical check if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell succeeds when I select the currently selected cell, and fails when I select another cell.
I want to change the image of the cell, hence need to cast the variable type but it fails. Why does to cast fail when I select a different cell than the currently selected one?
Thanks
Edit:
I use collectionView.reloadData() to restore all the images to originals (I mentioned this in the OP - when stopPlayingMedia() is invoked). It appears that, if I add a private var index:IndexItem? to my ViewController class and update its value within didSelectItemAt (to save the last selected cell) and use the code below, the code does not crash
extension ViewController:MediaPlayerControllerDelegate {
// this delegate method is invoked when stopPlayingMedia() is called
func mediaPlayerControllerDidFinishPlaying() {
// restore the UI
// collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image
timeLabel.text = "00:00"
progressBar.progress = 0.0
}
}
I tried the following to create cell and changing the image inside cell on selection. Not crashing at all. Please check the implementation, It might help you.
import UIKit
class ViewController: UIViewController {
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing
#IBOutlet weak var collectionView: UICollectionView! {
didSet{
collectionView.delegate = self
collectionView.dataSource = self
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isPlayingModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
}
class MediaItemCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageVw: UIImageView!
}
Check the implementation here.
You are not passing indexPath correctly to load a cell, that's why it is crashing.Edit your didSelectItemAt: method as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
cell.imgView.image = UIImage(named: "3.jpg")
}
Collection view indexPath has element item, and not row
Try this and see: (let me know what does it print)
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell {
if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image { // Collection view indexPath has element `item`, and not `row`
cell.mediaItemCellImageView.image = cellImage
} else {
print("cellImage not found at indexPath - \(indexPath)")
}
} else {
print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
}
// stop playing
mediaPlayer?.stopPlayingMedia()
}
I have a collection view cell and inside there's an imageView. On it I display array of images using sd_setImage once loaded from firestore. In console I see all images which my app downloaded.
I use this code:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newDetailCollectionCell", for: indexPath) as! NewDetailCollectionViewCell
cell.imageView.sd_setImage(with: URL(string: images[indexPath.item]))
return cell
}
When I use this code I can't see my images. But if I use the code below everything works fine.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newDetailCollectionCell", for: indexPath) as! NewDetailCollectionViewCell
cell.imageView.sd_setImage(with: URL(string: images[indexPath.item]))
return cell
}
The problem is that you should be calling collectionView.reloadData() instead of tableView.reloadData() in your Dispatch.main.async {} Because in this situation the collectionView needs to update the dataSource. I Hope this helps.
DispatchQueue.main.async {
self.collectionView?.reloadData()
}