I have a UICollectionView in Storyboard, in a UICollectionViewController. The UICollectionViewController is linked to my custom class MasterViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate, and it's delegate and datasource are linked in the storyboard to this class.
I have a prototype UICollectionViewCell in storyboard, with an identifier "MyCell", from my custom class Cell: UICollectionViewCell
In the cellForItemAtIndexPath method, the app crash at the line : let cell:Cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as Cell
I don't find why. I haven't implemented the registerClass:forCellWithReuseIdentifier: method, and the identifier of the storyboard is exactly "MyCell", I checked many times, and the delegate and datasource are linked to the right class.
When the app crash, nothing is printed in the console, just "(lldb)"
Here's my code :
class MasterViewController: UICollectionViewController,UICollectionViewDataSource,UICollectionViewDelegate {
var objects = [ObjectsEntry]()
#IBOutlet var flowLayout: UICollectionViewFlowLayout!
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
flowLayout.itemSize = CGSizeMake(collectionView!.bounds.width - 52, 151)
}
// MARK: - Collection View
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return objects.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:Cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as Cell
return cell
}
I had the same problem. Raywenderlich Swift manual helped me. I copying MyCollectionViewController here.
Identifier must match in controller and Storyboard.
Create custom UICollectionViewCell class.
Set this UICollectionViewCell in Storyboard.
Do not call viewDidLoad().
Do not call registerClass:forCellWithReuseIdentifier:
Set cell item size with UICollectionViewDelegateFlowLayout in collectionView:layout:sizeForItemAtIndexPath:.
import UIKit
class MyCollectionViewController:
UICollectionViewController,
UICollectionViewDelegateFlowLayout {
private let reuseIdentifier = "ApplesCell"
// MARK: UICollectionViewDataSource
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as MyCollectionViewCell
cell.backgroundColor = UIColor.redColor()
cell.imageView.image = UIImage(named: "red_apple")
return cell
}
Related
I have a UICollectionView embedded in a UITableViewCell & I want to perform a segue when a UICollectionViewCell is pressed & pass the data represented by that cell to the destination ViewController for detailed information.
Here is the code for the embedded UICollectionView inside the UITableViewCell
#IBOutlet weak var EventCollection: UICollectionView!
var events = [Events]()
override func awakeFromNib() {
super.awakeFromNib()
EventCollection.delegate = self
EventCollection.dataSource = self
}
extension PopularCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return events.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = EventCollection.dequeueReusableCell(withReuseIdentifier: "EventCell", for: indexPath) as! EventCell
let event = events[indexPath.row]
print("Event Name:\(event.event_name)")
cell.event = event
cell.tag = indexPath.row
return cell
}
How do I perform & prepare a segue in the main ViewController when a UICollectionViewCell is pressed so as to pass the data contained by that cell to the destination ViewController
Here are the steps that you need to do.
As you said, your CollectionView is inside TableView. So your TableView delegates/DataSources binded with the MainViewController. CollectionView delegates/DataSources binded with the TableViewCell.
Now create a protocol to know user has clicked on collectionView.
protocol MyProtocol : class {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
}
In TableViewCell, you need to call delegate like this,
class MyTableCell : UITableViewCell, UICollectionViewDelegate {
weak var delegate : MyProtocol?
:
:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let delegate = delegate {
delegate.collectionView(collectionView, didSelectItemAt: indexPath)
}
}
}
Now your MainViewController must have to conform this protocol,
class MainViewController :UIViewController, MyProtocol {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Do segue here ....
}
}
Note : Make sure to bind delegate with your MainViewController i.e. in TableviewCellForRow have cell.delegate = self
Add the following code with in a new file named UIView_Ext
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
In func didSelectItem(At indexPath: IndexPath) method , write the following code
self.parentViewController?.performSegue(withIdentifier: "Identifer", sender: "Your Data in place of this string")
i'm doing with this, i want to use CollectionView, but i haven't seen prototype cell, and don't know how to use CollectionView in this case, can someone help me ?
I try to use like this way but it take alot of time and hard to manage than UICollectionView
The main way to use UICollectionView is by managing the logic programmatically.
First, create a new class which inherits from UICollectionViewCell. Choose if you want to include a xib to easily design your cell:
Design your cell with Interface Builder or programmatically.
Create your main view controller including a xib (or a storyboard) with the collection view inside and link it to the associated class via Interface Builder. Alternatively you can add a collection view programmatically to your UIViewController
Make the target view controller conform to the UICollectionViewDelegate and UICollectionViewDataSource protocols by declaring them after the father class:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
//...
}
Register the associated nib or the class for your cell in the viewDidLoad method and associate the datasource and delegate protocols to the view controller class:
let cellIdentifier = "cellIdentifier"
override func viewDidLoad() {
super.viewDidLoad()
//if you use xibs:
self.collectionView.register(UINib(nibName:"MyCollectionCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)
//or if you use class:
self.collectionView.register(MyCollectionCell.self, forCellWithReuseIdentifier: cellIdentifier)
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
Implement the methods declared in the UICollectionViewDelegate and UICollectionViewDataSource protocols :
let objects = ["Cat", "Dog", "Fish"]
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.objects.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! MyCollectionCell
//in this example I added a label named "title" into the MyCollectionCell class
cell.title.text = self.objects[indexPath.item]
return cell
}
Run your app in the simulator (or on a real device) and.. Et voilĂ ! :)
For more info: https://developer.apple.com/reference/uikit/uicollectionview
ok first you must have the IBOutlet of your collection view and implements the methods like this
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
count = 9;
let nib = UINib(nibName: "yourItemView", bundle: nil)
collectionView.registerNib(nib, forCellWithReuseIdentifier: "yourItemView")
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
ok in the function you add a xib file, next you must create that extend from UICollectionViewCell, and when you finish this you must override the next methods
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return count
// the numbers of items
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {//size of your item for screen sizes
let wsize = UIScreen.mainScreen().bounds.size.width
switch(wsize){
case 414:
return CGSize(width: 190, height: 102)
case 375:
return CGSize(width: 190, height: 102)
case 320:
return CGSize(width: 174, height: 102)
default:
return CGSize(width: 174, height: 102)
}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("yourItemView", forIndexPath: indexPath) as! yourItemView
return cell
}
and this is all, good luck
I have a tableView in which I have 2 custom cells, in both cells I have different CollectionView, the dataSource and delegate of both the CollectionViews is my ViewController.
So now how do I check which CollectionViews is to be configured in UICollectionView's respective methods?
Here is the view hierarchy
How do I come to know which is the collectionView that's there in the parameter of above function?
So here is my code:
class FeatureBannerTableViewCell: UITableViewCell {
#IBOutlet weak var featureCollectionView: UICollectionView!
}
class FeaturedTableViewCell: UITableViewCell {
#IBOutlet weak var FeatureTitle: UILabel!
#IBOutlet weak var seeAllButton: UIButton!
#IBOutlet weak var collectionView: UICollectionView!
}
extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 2,3,6,7:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeatureBannerTableViewCell", forIndexPath: indexPath) as! FeatureBannerTableViewCell
cell.featureCollectionView.dataSource = self
cell.featureCollectionView.delegate = self
return cell
default:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeaturedTableViewCell", forIndexPath: indexPath) as! FeaturedTableViewCell
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
return cell
}
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// how do I know if this collectionView is collectionView or featureCollectionView?
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// how do I know if this collectionView is collectionView or featureCollectionView?
}
}
I would use the tag property in UIView to identify your collection view. So when you configure your table cells in cellForRowAtIndexPath get the collection view of the cell and set its tag to something unique (I would use the row value of indexpath).
extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 2,3,6,7:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeatureBannerTableViewCell", forIndexPath: indexPath) as! FeatureBannerTableViewCell
cell.featureCollectionView.dataSource = self
cell.featureCollectionView.delegate = self
cell.featureCollectionView.tag = indexPath.row
return cell
default:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeaturedTableViewCell", forIndexPath: indexPath) as! FeaturedTableViewCell
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
cell.collectionView.tag = indexPath.row
return cell
}
}
}
Then in your collection view methods get the tag value and that will tell you which one of the many collection views it is.
extension ViewController: UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// how do I know if this collectionView is collectionView or featureCollectionView?
let datasourceIndex = collectionView.tag
// now use your datasource for the following index
}
}
I think, you can make use of isKindOfClass
Example:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyReuseIdentifier", for: indexPath as IndexPath) as! UICollectionViewCell
if cell.isKindOfClass(FeaturedBannerCollectionCell){
//implementation code
} else cell.isKindOfClass(FeaturedCollectionViewCell) {
//implementation code
}
return cell
}
I have a UICollectionView that I added to my UIViewController in my Storyboard.
It contains a UICollectionViewCell of class BookCell and reusable identifier BookCell.
bookArray gets passed in the prepareForSegue method from the previous ViewController
cellForItemAtIndexPath never gets called. I have attempted to declare BookCollectionVC as delegate and dataSource both in Storyboard and in viewDidLoad, as you can see commented out, but that does not change anything.
I have read multiple SO answers pertaining to cell size, to the number of items in the section, to the multiple ways of declaring delegate and dataSource and have tried/double checked all of them.
Any ideas?
class BookCollectionVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collection: UICollectionView!
var bookArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// collection.delegate = self
// collection.dataSource = self
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("BookCell", forIndexPath: indexPath) as? BookCell {
// This never gets called
let bookIsbn = bookArray[indexPath.row]
cell.configureCell(bookIsbn)
return cell
} else {
return UICollectionViewCell()
}
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(bookArray.count)
return bookArray.count
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let cellWidth = screenWidth / 4
return CGSizeMake(cellWidth,cellWidth)
}
As a test, don't use an if-let. Just get the cell value and print it. What is its type? Have you properly assigned the cells to be the custom class (BookCell) in your StoryBoard?
I have a UICollectionView with a custom cell with an UIImageView in it as well as a custom class with the outlet
class theCell: UICollectionViewCell {
#IBOutlet var theImage:UIImageView!
}
I have an array of UIImage (Which only has one constant image). The image is in the main bundle.
var tempImages = [UIImage(named: "placeholder.png" )]
delegate methods
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tempImages.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = self.collectionView?.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! theCell
//Unexpectadly found nil
cell.theImage.image = tempImages[indexPath.row]
return cell
}
If you have added this line:
self.collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
then remove this first and here is complete working code:
CollectionViewController.swift
import UIKit
class CollectionViewController: UICollectionViewController {
var tempImages = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
tempImages.append(UIImage(named: "bg.jpg")!)
}
// MARK: UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return tempImages.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
// Configure the cell
cell.theImage.image = tempImages[indexPath.row]
println(cell.theImage.image)
return cell
}
}
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet var theImage:UIImageView!
}
And don't forget to assign Identifier to your cell:
Sample for more Info.
Try registering first in cellForItemAtIndexPath:
collectionView.registerNib(UINib(nibName: "FeedCell", bundle: nil), forCellWithReuseIdentifier: "cell")
EDIT
In your situation:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
collectionView.registerNib(UINib(nibName: "FeedCell", bundle: nil), forCellWithReuseIdentifier: "cell")
let cell = self.collectionView?.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! theCell
cell.theImage.image = tempImages[indexPath.row]
return cell
}
The problem is reinitialize tempImages array so it return nil.
var tempImages = [UIImage(named: "placeholder.png" )]