Why my collection view disappear when updating the stepper value? - ios

here is my simplified code in my view controller
class WishListVC: UIViewController {
#IBOutlet weak var wishListCollectionView: UICollectionView!
private var products = [Product]()
private var selectedProduct : Product?
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - cell Delegate
extension WishListVC : ListProductCellDelegate {
func addToCartButtonDidTapped(at selectedIndexPath: IndexPath, collectionView: UICollectionView) {
guard let userOrder = userOrder else {return}
let selectedProduct = products[selectedIndexPath.item]
Order.addProductToOrderRealmDatabase(userOrder: userOrder, selectedProduct: selectedProduct)
wishListCollectionView.reloadData()
updateBadgeOnCartTabBar()
}
func stepperButtonDidTapped(at selectedIndexPath: IndexPath, stepperValue: Int, collectionView: UICollectionView) {
guard let userOrder = userOrder else {return}
let selectedProduct = products[selectedIndexPath.item]
if stepperValue > 0 {
Product.changeProductQuantityInRealmDatabase(selectedProduct: selectedProduct, quantity: stepperValue)
} else {
Order.removeProductFromOrderRealmDatabase(userOrder: userOrder, selectedProduct: selectedProduct)
Product.changeProductQuantityInRealmDatabase(selectedProduct: selectedProduct, quantity: 0)
}
wishListCollectionView.reloadData()
updateBadgeOnCartTabBar()
}
}
//MARK: - Collection View Data Source
extension WishListVC : UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return products.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WishListStoryboardData.CollectionViewIdentifiers.productSliderCell.rawValue, for: indexPath) as? ListProductCell else { return UICollectionViewCell()}
cell.productData = products[indexPath.item]
cell.delegate = self
cell.collectionView = wishListCollectionView
return cell
}
}
and here is the code for my collection view cell:
protocol ListProductCellDelegate {
func addToCartButtonDidTapped(at selectedIndexPath: IndexPath, collectionView : UICollectionView)
func stepperButtonDidTapped( at selectedIndexPath: IndexPath, stepperValue: Int, collectionView : UICollectionView)
}
class ListProductCell: UICollectionViewCell {
#IBOutlet weak var productImageViewAspectRatio: NSLayoutConstraint!
#IBOutlet weak var addToCartButton: UIButton!
#IBOutlet weak var stepper: GMStepper!
var collectionView : UICollectionView?
var delegate: ListProductCellDelegate?
var productData : Product? {
didSet {
updateUI()
}
}
#IBAction func addToCartButtonDidPressed(_ sender: UIButton) {
guard let collectionView = collectionView else {return}
guard let selectedIndexPath = collectionView.indexPathForView(view: sender) else {return}
self.delegate?.addToCartButtonDidTapped(at: selectedIndexPath, collectionView: collectionView)
}
#IBAction func stepperDidTapped(_ sender: GMStepper) {
guard let collectionView = self.collectionView else {return}
guard let selectedIndexPath = collectionView.indexPathForView(view: sender) else {return}
self.delegate?.stepperButtonDidTapped(at: selectedIndexPath, stepperValue: Int(sender.value), collectionView: collectionView)
}
private func updateUI() {
guard let product = productData else {return}
stepper.value = Double(product.quantity)
setLikeButton(product: product)
setCartAndStepperButton()
}
private func setCartAndStepperButton() {
guard let selectedProduct = productData else {return}
func showStepperButton(status: Bool) {
// to decide whether to show stepper or add to cart button.
stepper.isHidden = !status
stepper.isEnabled = status
addToCartButton.isHidden = status
addToCartButton.isEnabled = !status
}
if selectedProduct.quantity == 0 {
showStepperButton(status: false)
} else {
showStepperButton(status: true)
}
}
}
I don't understand why after I tap the stepper for the first time after the 'Add To Cart' disappear, the collection view will disappear.
I don't have collectionView.isHidden in my entire code, but I don't know why my collection view disappear like the file .gif below
http://g.recordit.co/NAEc36MbrM.gif
but if the stepper is already show with some stepper value more than 1, then it will make my collection view dissapear like the gif below
http://recordit.co/SLdqf1ztFZ.gif
the minimum stepper value is set to be 1.
If I change the collection view reload data wishListCollectionView.reloadData() in the stepperButtonDidTapped method above to be just reload data in certain cell only using wishListCollectionView.reloadItems(at: [selectedIndexPath]) the problem will be solved, but the stepper value seems it will be updated little slower, and it looks laggy.
I don't know how to trace the last line that will be executed so it makes my collection view disappears.
and if I reload the data in the main thread using:
DispatchQueue.main.async {
self.wishListCollectionView.reloadData()
}
it won't make the collection view disappear, but If I edit the cell index 4 it will affect the cell index 1 like gif here: http://g.recordit.co/6802BJDdtx.gif
I change the number in the fifth cell but it will automatically change the second cell.

Note to:
Use Xcode Ui Debugging tool and check if your collectionView is hidden or empty
Realm is realTime database ,any changes you make in database will be applied
on your arrays too(like products array)
The reason of stepper problem is becouse of :
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WishListStoryboardData.CollectionViewIdentifiers.productSliderCell.rawValue, for: indexPath) as? ListProductCell else { return UICollectionViewCell()}
and your using stepperButtonDidTapped delegete inside of cell.
declure your cells once and store them inside array,like below:
var cellArray:[ListProductCell]=[]
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (cellArray.count > indexPath.row){
return cellArray[indexPath.row]
}else{
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WishListStoryboardData.CollectionViewIdentifiers.productSliderCell.rawValue, for: indexPath) as? ListProductCell else { return UICollectionViewCell()}
cell.productData = products[indexPath.item]
cell.delegate = self
cell.collectionView = wishListCollectionView
cellArray.append(cell)
return cell
}}

My case load get data success but not see data when reload.
This problem case born set Height in func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize is less than height cell on storyboard.

Related

Swift - Issue accessing data from UICollectionView embedded in UITableView

I have a tableView, with a prototype cell containing a UICollectionView. I’ve setup the tableView according to this tutorial (https://medium.com/#stasost/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429), and the UI is working. I can pass data through my tableView and into the collectionView.
View Layout
When a collectionViewCell is selected it segues to another view.
I haven’t figured out how to access the data from the collectionViewCell and pass it to the new view.
The collectionView is initialized within the tableView prototype cell. I've tried didSelectRow -> prepareForSegue (code below), but the commands do not autocomplete, and are not working.
Here's the code for the tableViewCell, where the collectionView is setup.
EDIT: Removed commented code for clarity
import UIKit
class homeFeedTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var feedCollectionView: UICollectionView!
var selectedEvent : Event?
var collectionItems = [CollectionViewModelItem]()
var collectionItem : CollectionViewModelItem?
#IBOutlet weak var sectionHeadingLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
feedCollectionView.delegate = self
feedCollectionView.dataSource = self
print("collection items \(collectionItems.count)")
for item in collectionItems{print("type: \(item.type), title: \(item.eventTitle)")}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// setup view model
var item: TableViewModelItem? {
didSet {
// if not right class, skip
guard let item = item as? TableViewModelFeed else {
return
}
sectionHeadingLabel.text = item.sectionTitle
}
}
// create reuse identifier property
static var identifier: String {
return String(describing: self)
}
}
import Foundation
import UIKit
extension homeFeedTableViewCell {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// print("dataCount3: \(collectionItems.count) \(collectionItems[collectionItems.count-1].type)")
return collectionItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let cell = UICollectionViewCell()
// return cell
self.collectionItem = collectionItems[indexPath.row]
switch collectionItem!.type {
case .yourEvents:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier:YourEventsCollectionViewCell.identifier, for: indexPath) as? YourEventsCollectionViewCell{
cell.item = collectionItem
print(cell.item?.type)
print(".yourEvents")
return cell
}
case .feed:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: mainFeedCollectionViewCell.identifier, for: indexPath) as? mainFeedCollectionViewCell{
cell.item = collectionItem
print(".feed")
return cell
}
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("\(collectionItems[indexPath.row].eventTitle) tapped")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourEventsToEventViewController" || segue.identifier == "feedToEventViewController"{
print("prepare for segue1")
let destinationVC = segue.destination as! EventViewController
if collectionItem != nil{
print("prepare for segue2")
destinationVC.backgroundImageUrl = collectionItem!.backgroundImageUrl
}
}
}
}
}
A UICollectionView keeps track of its selected indexPaths with the property indexPathsForSelectedItems. Since you trigger your segue in collectionView(didSelectItem: atIndexPath:), your selected indexPath is available during prepare(forSegue:). You could try the following:
class MyViewController: UIViewController, UICollectionViewDelegate {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "mySegue", sender: self)
}
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let destinationVC = segue.destination as! EventViewController,
segue.identifier == "mySegue" else { return }
// In this context, your selected cell is the one who fired the segue
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems,
let firstSelectedIndexPath = selectedIndexPaths.first {
let selectedObject = collectionItems[firstSelectedIndexPath.row]
destinationVC?.backgroundUrl = selectedObject.backgroundUrl
}
}
}
The sequence is:
You select a cell (through user interaction, ie tapping).
didSelect performs a segue named "mySegue" (in this example).
In prepareForSegue, you look for your selected index paths. Assuming you aren't using multi-selection, you want your first and only indexPath. Using that index path, you can retrieve your data in your collectionItems array.

Smooth animation in UICollectionView when changing data source

I have a UISegmentControl that I use to switch the datasource for a UICollectionView. The datasources are different types of objects.
For example the objects might look like this
struct Student {
let name: String
let year: String
...
}
struct Teacher {
let name: String
let department: String
...
}
And in the view that contains the CollectionView, there would be code like this:
var students = [Student]()
var teachers = [Teachers]()
... // populate these with data via an API
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex) == "Students") {
return students?.count ?? 0
} else {
return teachers?.count ?? 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") {
cell.title = students[indexPath.row].name
cell.subtitle = students[indexPath.row].year
} else {
cell.title = teachers[indexPath.row].name
cell.subtitle = teachers[indexPath.row].subject
}
return cell
}
#IBAction func segmentChanged(_ sender: AnyObject) {
collectionView.reloadData()
}
This correctly switches between the two datasources, however it does not animate the change. I tried this:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)
But this just crashes (I think this is because performBatchUpdates gets confused about what to remove and what to add).
Is there any easy way to make this work, without having a separate array storing the current items in the collectionView, or is that the only way to make this work smoothly?
Many thanks in advance!
If your Cell's UI just look the same from different datasource, you can abstract a ViewModel upon your datasource, like this:
struct CellViewModel {
let title: String
let subTitle: String
...
}
Then every time you got data from an API, generate ViewModel dynamically
var students = [Student]()
var teachers = [Teachers]()
... // populate these with data via an API
var viewModel = [CellViewModel]()
... // populate it from data above by checking currently selected segmentBarItem
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") {
viewModel = generateViewModelFrom(students)
} else {
viewModel = generateViewModelFrom(teachers)
}
So you always keep one datasource array with your UICollectionView.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
cell.title = viewModel[indexPath.row].title
cell.subtitle = viewModel[indexPath.row].subTitle
return cell
}
#IBAction func segmentChanged(_ sender: AnyObject) {
collectionView.reloadData()
}
Then try your performBatchUpdates:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)

Pass coredata to ViewController based on cell selection

I need to present a new ViewController when selecting a UICollectionView Cell and pass the data from the entity used to fill selected cell.
Here is the code used to fill cell data:
let pets = PersistenceManager.shared.fetch(Pet.self)
var _fetchResultsController: NSFetchedResultsController <Pet>?
var fetchResultsController: NSFetchedResultsController <Pet>?{
get{
if _fetchResultsController == nil {
let moc = PersistenceManager.shared.context
moc.performAndWait {
let fetchRequest = PersistenceManager.shared.petsFetchRequest()
_fetchResultsController = NSFetchedResultsController.init(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) as? NSFetchedResultsController<Pet>
_fetchResultsController?.delegate = self
do {
try self._fetchResultsController?.performFetch()
}catch {
}
}
}
return _fetchResultsController
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewHorizontal.dequeueReusableCell(withReuseIdentifier: "HorCell", for: indexPath) as! PRMainHorizontalCollectionViewCell
if let pet= self.fetchResultsController?.fetchedObjects, indexPath.row < pet.count{
let _pet= fetchResultsController!.object(at: indexPath)
// cell UI goes here
}
return cell
}
I understand I need to use didSelectItemAt, I just don't know what information needs to go in the function. Please let me know of anything else needed to better help answer this question. Thank you.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Added the line below based on karthik's answer. But I am unsure how to implement it.
let selectedObj = fetchResultsController!.object(at: indexPath)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "selectedPetViewController") as! PRSelectedPetViewController
navigationController?.pushViewController(vc, animated: true)
}
I prefer the following architecture:
This is the main controller with data.
For a better understanding, I will simplify the data source.
class ViewController: UIViewController {
// code ...
#IBOutlet var collectionView: UICollectionView!
fileprivate var data = [Pet]()
}
extension ViewController: UICollectionViewDataSource {
// code ...
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HorCell", for: indexPath) as? PRMainHorizontalCollectionViewCell else {
return UICollectionViewCell()
}
let pet = data[indexPath.row]
// TODO: configure cell using pet ...
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let row = indexPath.row
let pet = data[row]
// TODO: Get the child controller in any way convenient for you.
let childController = ChildViewController()
// With the help of the delegate, we will receive notification of changes pet.
childController.delegate = self
// Thus, we pass the data to the child controller.
childController.pet = pet
childController.indexPath = indexPath
// TODO: Present the view controller in any way convinient for you.
}
}
extension ViewController: ChildViewControllerDelegate {
func saveButtonPressed(_ controller: ChildViewController) {
guard let pet = controller.pet, let indexPath = controller.indexPath else {
return
}
// We save data and reload the cell whose data we changed.
self.data[indexPath.row] = pet
collectionView.reloadItems(at: [indexPath])
}
func cancelButtonPressed(_ controller: ChildViewController) {
// Do something if necessary...
}
}
In addition to the controller, the child controller also provides a delegate protocol for notification of changes.
protocol ChildViewControllerDelegate {
func saveButtonPressed(_ controller: ChildViewController)
func cancelButtonPressed(_ controller: ChildViewController)
}
// This is the controller you want to show after selecting a cell.
// I assume that in the child controller there is a button to save and cancel.
class ChildViewController: UIViewController {
var delegate: ChildViewControllerDelegate?
// The object whose data we are editing in the controller.
var pet: Pet!
// The location of the object in the main controller.
var indexPath: IndexPath!
override func viewDidLoad() {
// TODO: Configure user interface using self.pet
}
#IBAction func saveButtonPressed(_ button: UIButton) {
delegate?.saveButtonPressed(self)
}
#IBAction func cancelButtonPressed(_ button: UIButton) {
delegate?.cancelButtonPressed(self)
}
}
you can follow this to pass information to another view controller.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedObj = fetchResultsController!.object(at: indexPath)
// instantiate presenting view controller object
// add one property (manange object) in your presenting viewcontroller
// assign the selected object to that property
// present the view controller
}

How to add multiple collection view in a view

I am trying to incorporate 2 collection views into same view. I implemented everything and noticed that the data that is loading in the second collection view is the same as the first collection view.
Here is the code that i am trying out
class DashBoardMainSection1CollectionViewCellClass: UICollectionViewCell
{
#IBOutlet weak var DashBoardMainSection1CollectionViewImageListingViewOutlet : UIImageView!
#IBOutlet weak var DashBoardMainSection1CollectionViewLabelOutlet : UILabel!
}
class DashBoardMainSection2CollectionViewCellClass: UICollectionViewCell
{
#IBOutlet weak var DashBoardMainSection2CollectionViewImageListingViewOutlet : UIImageView!
#IBOutlet weak var DashBoardMainSection2CollectionViewLabelOutlet : UILabel!
}
private let reuseIdentifierDashBoardMainSection1Cell = "dashBoardMainSection1CollectionViewCellIdentifier"
private let reuseIdentifierDashBoardMainSection2Cell = "dashBoardMainSection2CollectionViewCellIdentifier"
class DashBoardViewControllerMain: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
{
#IBOutlet weak var DashBoardMainSection1CollectionViewOutlet : UICollectionView!
#IBOutlet weak var DashBoardMainSection2CollectionViewOutlet : UICollectionView!
override func viewDidLoad()
{
super.viewDidLoad()
DashBoardMainSection1CollectionViewOutlet.delegate = self;
DashBoardMainSection2CollectionViewOutlet.delegate = self;
DashBoardMainSection1CollectionViewOutlet.dataSource = self;
DashBoardMainSection2CollectionViewOutlet.dataSource = self;
// Do any additional setup after loading the view.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
var count:Int?
if (self.DashBoardMainSection1CollectionViewOutlet) != nil{
count = 10
}
//if (self.DashBoardMainSection2CollectionViewOutlet) != nil{
else{
count = 5
}
return count!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let collectionView = self.DashBoardMainSection1CollectionViewOutlet{
let cell:DashBoardMainSection1CollectionViewCellClass = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifierDashBoardMainSection1Cell, for: indexPath) as! DashBoardMainSection1CollectionViewCellClass
cell.DashBoardMainSection1CollectionViewImageListingViewOutlet.image = UIImage(named:"Apple.png")
cell.DashBoardMainSection1CollectionViewLabelOutlet.text = "FIRST"
return cell
}
else{
let cell2:DashBoardMainSection2CollectionViewCellClass = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifierDashBoardMainSection2Cell, for: indexPath) as! DashBoardMainSection2CollectionViewCellClass
cell2.DashBoardMainSection2CollectionViewImageListingViewOutlet.image = UIImage(named:"Logo-Disabled.png")
cell2.DashBoardMainSection2CollectionViewLabelOutlet.text = "SECOND"
return cell2
}
}
When i run the project both the collection views are showing Apple picture and the cell count is 10.
Can someone help me to solve this issue...
Please manage both collectionview with tag. Give tag to both collectionview on viewDidLoad method or from storyboard. Now make conditions based on tag.
Example :-
DashBoardMainSection1CollectionViewOutlet.tag = 1
DashBoardMainSection2CollectionViewOutlet.tag = 2
if collectionView.tag == 1 {
return 10
} else {
return 5
}
The problem is in the if statement:
It should be like this:
if collectionView == self.DashBoardMainSection1CollectionViewOutlet{
//code
}
With this code, you check if the collectionView passed by the parameter is the first collectionView.
With your original code, you were making new constant named collectionView and assigning it with your first collectionView.
Because your condition if (self.DashBoardMainSection1CollectionViewOutlet) != nil, will always return true and code for if block will only execute everytime.
Try this:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
var count = 0
if (collectionView == self.DashBoardMainSection1CollectionViewOutlet) {
count = 10
}
// if (collectionView == self.DashBoardMainSection2CollectionViewOutlet)
else {
count = 5
}
return count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == self.DashBoardMainSection1CollectionViewOutlet){
let cell:DashBoardMainSection1CollectionViewCellClass = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifierDashBoardMainSection1Cell, for: indexPath) as! DashBoardMainSection1CollectionViewCellClass
cell.DashBoardMainSection1CollectionViewImageListingViewOutlet.image = UIImage(named:"Apple.png")
cell.DashBoardMainSection1CollectionViewLabelOutlet.text = "FIRST"
return cell
}
else{
let cell2:DashBoardMainSection2CollectionViewCellClass = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifierDashBoardMainSection2Cell, for: indexPath) as! DashBoardMainSection2CollectionViewCellClass
cell2.DashBoardMainSection2CollectionViewImageListingViewOutlet.image = UIImage(named:"Logo-Disabled.png")
cell2.DashBoardMainSection2CollectionViewLabelOutlet.text = "SECOND"
return cell2
}
}
If you get same data for both UICollectionView it means you are using the same DataSource.
Define a separate DataSource for each UICollectionView
And you should do the same for the Delegate except if you need to implement same actions for both.

Cannot Click Custom UICollectionViewCell

I have a custom UICollectionView cell with an imageView and a label, as listed below:
import UIKit
class PollCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var pollQuestion: UILabel!
}
I want to make each populated cell clickable and then pass information from that clicked cell into a new ViewController. Below is the ViewController that populates the custom cell.
import UIKit
import FirebaseDatabase
import FirebaseDatabaseUI
import FirebaseStorageUI
private let reuseIdentifier = "PollCell"
class TrendingViewController: UICollectionViewController {
var ref: FIRDatabaseReference!
var dataSource: FUICollectionViewDataSource!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.dataSource = self.collectionView?.bind(to: self.ref.child("Polls")) { collectionView, indexPath, snap in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PollCell
/* populate cell */
cell.pollQuestion.text = snap.childSnapshot(forPath: "question").value as! String?
let urlPollImage = snap.childSnapshot(forPath: "image_URL").value as! String?
cell.imageView.sd_setImage(with: URL(string: urlPollImage!), placeholderImage: UIImage(named: "Fan_Polls_Logo.png"))
//Comment
return cell
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath)
}
}
I also have some code to "invert" the cells that populate in my inverted ViewController:
import Foundation
import UIKit
class InvertedStackLayout: UICollectionViewLayout {
let cellHeight: CGFloat = 100.00 // Your cell height here...
override func prepare() {
super.prepare()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttrs = [UICollectionViewLayoutAttributes]()
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numberOfSectionItems = numberOfItemsInSection(section) {
for item in 0 ..< numberOfSectionItems {
let indexPath = IndexPath(item: item, section: section)
let layoutAttr = layoutAttributesForItem(at: indexPath)
if let layoutAttr = layoutAttr, layoutAttr.frame.intersects(rect) {
layoutAttrs.append(layoutAttr)
}
}
}
}
}
return layoutAttrs
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let contentSize = self.collectionViewContentSize
layoutAttr.frame = CGRect(
x: 0, y: contentSize.height - CGFloat(indexPath.item + 1) * cellHeight,
width: contentSize.width, height: cellHeight)
return layoutAttr
}
func numberOfItemsInSection(_ section: Int) -> Int? {
if let collectionView = self.collectionView,
let numSectionItems = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section)
{
return numSectionItems
}
return 0
}
override var collectionViewContentSize: CGSize {
get {
var height: CGFloat = 0.0
var bounds = CGRect.zero
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numItems = numberOfItemsInSection(section) {
height += CGFloat(numItems) * cellHeight
}
}
bounds = collectionView.bounds
}
return CGSize(width: bounds.width, height: max(height, bounds.height))
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if let oldBounds = self.collectionView?.bounds,
oldBounds.width != newBounds.width || oldBounds.height != newBounds.height
{
return true
}
return false
}
}
You must to implement this method of UICollectionViewDelegate
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
debugPrint("this works")
}
I hope this helps you
This Function used to select the row of your collection view
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
Then you use function call. Whatever you need, just Defined the inside function. And easily called the function during the selected the row (inside the didSelectItemAt indexPath)
Make sure you have enabled UserIntraction for both UILabel and UIImageView, because both have default value as false.
self.dataSource = self.collectionView?.bind(to: self.ref.child("Polls")) { collectionView, indexPath, snap in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PollCell
/* populate cell */
cell.pollQuestion.userInteractionEnabled = true;
cell.imageView.userInteractionEnabled = true;
cell.pollQuestion.text = snap.childSnapshot(forPath: "question").value as! String?
let urlPollImage = snap.childSnapshot(forPath: "image_URL").value as! String?
cell.imageView.sd_setImage(with: URL(string: urlPollImage!), placeholderImage: UIImage(named: "Fan_Polls_Logo.png"))
//Comment
return cell
}
Then you need to use of UICollectionViewDelegate method:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
}
Add a segue in your storyboard from the cell to the view controller you want to transition to. You can use the prepare for segue method, prepare(for:sender:), to pass your data. Using the segue (UIStoryboarySegue documentation) parameter, you can access the 'destinationViewController`.
Check out the View Controller for iOS Programming Guide: Using Segues for more information about using segues.

Resources