I'm looking for your help for my association between a tableview and a collection view.
I've got a CollectionView with 6 horizontal scrollable cells.
In each cell, I would like to put a table view.
For each tableView, I need to know in which cell I am to put my wanted data.
Actually, I make my code work for displaying tableView inside the cells but I'm blocked for finding in which collection view cell I am.
BTW, my Meal_vc_cell controller is actually Empty, it just has a simple outlet. Nothing else.
Here is my code
class TrainFoodPlanViewController: BaseViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UITableViewDataSource, UITableViewDelegate {
let train_meals_list: [String] = ["breakfast", "snack_1", "pre_intra_post", "lunch", "snack_2", "diner"]
#IBOutlet weak var trainCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Food Plan"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return train_meals_list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! Meal_vc_cell
cell.meal_number.text = train_meals_list[indexPath.row]
return cell
}
}
Here is what I'm trying to do
There are a number of ways this can be achieved. My recommendation is to subclass UITableView and add a parentCollectionViewIndexPath property
class MyCustomTableView: UITableView {
var parentCollectionViewIndexPath: IndexPath?
}
I'm assuming Meal_vc_cell has an outlet to the relevant tableView. Change that tableView's type to MyCustomTableView
class Meal_vc_cell: UITableViewCell {
#IBOutlet weak var tableView: MyCustomTableView
}
Note: If you're using storyboard, don't forget to update the tableView's class in storyboard as well
Now, just set parentCollectionViewIndexPath in the collectionView's cellForItemAt method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! Meal_vc_cell
cell.tableView.parentCollectionViewIndexPath = indexPath
return cell
}
Related
I am trying to display a xib of a collectionViewCell from the tableViewCell (so I can do both horizontal and vertical scrolling while having categories per row). It seems that the register of the xib is where the issue is coming into play as it will not register it properly from the proper place? tableView cannot be registering it since it fails when I tried to do so, I have registered it in the collectionView but where it wont crash but nothing displays. Maybe this isn't the best way to do it - but what would be a good alternative to implement a spaced out horizontal scrolling and vertical scrolling? Here is my source code so far
import UIKit
class DiscoverViewController: UIViewController {
#IBOutlet weak var discoverTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension DiscoverViewController: UICollectionViewDelegate, UICollectionViewDataSource, UITableViewDelegate, UITableViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.register(UINib(nibName: "DiscoverCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "DiscoverCollectionViewCell")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DiscoverCollectionViewCell", for: indexPath) as! DiscoverCollectionViewCell
cell.authorLabel.text = "this"
cell.coverImage.image = #imageLiteral(resourceName: "discover")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = discoverTableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as! CategoryTableViewCell
return cell
}
}
figured it out, the delegate and datasource had to be set to the TableView cell file as well as registering the xib on the TV with an instance of the CV on there. thanks to #Daniel for a point in the right direction.
I have a collection view, and each collection view cell will have a table view which will have different data for each collection view cell. The collection view is done using a storyboard and works fine, but I can not seem to get a UITableView to work inside of it. The Collection view code is below and any help is appreciated. Ideally a technique that allows me to use storyboard to customise the tableview cell, and I am using Swift 4.
extension StoryboardViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt
indexPath: IndexPath) {
print("Selected Cell #\(indexPath.row)")
}
}
extension StoryboardViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: "CollectionViewCell"), for: indexPath) as! StoryboardCollectionViewCell
cell.label.text = "Cell #\(indexPath.row)"
return cell
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
print("Current centered index: \(String(describing: centeredCollectionViewFlowLayout.currentCenteredPage ?? nil))")
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
print("Current centered index: \(String(describing: centeredCollectionViewFlowLayout.currentCenteredPage ?? nil))")
}
}
And I want to put a table view (list) inside of it like so:
UICollectionViewCell class that is being used should conform to UITableViewDelegate and UITableViewDataSource protocols that will initialize the cells in the table view
Your UICollectionViewCell Class should confirm to Protocols, UITableViewDelegate and UITableViewDataSource
import UIKit
class CollectionViewCell: UICollectionViewCell,UITableViewDelegate,UITableViewDataSource {
//MARK: TableViewDelegateAndDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Configure your cell here
return UITableViewCell()
}
}
Finally don't forget to assign TableView DataSource and Delegate in your storyboard
I have a UICollectionView (let name aCollection) inside a View controller and i use a UICollectionReusableView to show header at the top of collection view
I also have a another UICollectionView (let name bCollection) in UICollectionReusableView. I need to show here top user list. but when i am trying to connect an outlet from storyboard i am getting an error
I know how to reload data in collection view
self.aCollection.reloadData()
My issue is how to connect bCollection outlet and how to reload bCollection to show user list coming from web services?
To take the outlet of bCollection, you need to create a subclass of UICollectionReusableView.
Example:
UIViewController containing aCollection:
class ViewController: UIViewController, UICollectionViewDataSource
{
#IBOutlet weak var aCollectionView: UICollectionView!
override func viewDidLoad()
{
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCell(withReuseIdentifier: "aCell", for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
return (collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "reusableView", for: indexPath) as! ReusableView)
}
}
UICollectionReusableView containing bCollection:
class ReusableView: UICollectionReusableView, UICollectionViewDataSource
{
#IBOutlet weak var bCollectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCell(withReuseIdentifier: "bCell", for: indexPath)
}
}
Interface Screenshot
Edit:
To reload bCollection:
You need a reference to the reusableView that you are using. The way you are using it ReusableView() is not right.
Use it like this:
var reusableView: ReusableView?
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
self.reusableView = (collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "reusableView", for: indexPath) as! ReusableView) //Storing the reference to reusableView
return self.reusableView!
}
Now, to reload bCollection,
self.reusableView?.bCollectionView.reloadData()
Since your UICollectionView (bCollection) is inside the UICollectionReusableView you can connect outlet to UICollectionReusableView's class only. So I believe you may have to create a custom class for UICollectionReusableView and assign it in the storyboard and connect bCollection 's outlet to that custom class
I have a tableview cell inside which i have added collectionview cell ( for horizontal scrolling).
Now i want to push to other navigation controller on pressing any cell of horizontal collectionview. How to do it ? Or how can i define delegate methods for cell press.
Code :
ViewController.swift :
class ViewController: UIViewController {
var categories = ["Action", "Drama", "Science Fiction", "Kids", "Horror"]
}
extension ViewController : UITableViewDelegate { }
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
return cell
}
}
CategoryRow.swift
class CategoryRow : UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CategoryRow : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! VideoCell
return cell
}
}
extension CategoryRow : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
VideoCell.swift
class VideoCell : UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
Here you will get the cell click at the delegate method didSelectItemAtIndexPath on CategoryRow class and from there you can fire a delegate to get call inside ViewController
ViewController.swift :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
cell.delegate = self
return cell
}
VideoCell.swift :
protocol CategoryRowDelegate:class {
func cellTapped()
}
CategoryRow.swift :
class CategoryRow : UITableViewCell {
weak var delegate:CategoryRowDelegate?
#IBOutlet weak var collectionView: UICollectionView!
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if delegate!= nil {
delegate?.cellTapped()
}
}
Add the delegate function inside ViewController
func cellTapped(){
//code for navigation
}
First create protocol for delegation from CategoryRow.swift like below code
protocol CollectionViewSelectionDelegate: class {
func didSelectedCollectionViewItem(selectedObject: AnyObject)
}
Now create delegate object on VideoCell.swift like below
weak var delegate:CollectionViewSelectionDelegate?
Change ViewController.swift code before return cell
cell?.delegate = self
Override method of delegate in ViewController.swift and call similar method from VideoCell.swift from UICollectionView Delegate method.
I think its better to use Notification in this case.
post a notification in didSelectItem of collection view
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)
and add an observer in viewController viewDidLoad as follows
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(pushToNew(_:)), name: notificationIdentifier, object: nil)
in the new pushToNew function, perform your segue
func pushToNew(notification: Notification) {
// perform your segue here. Navigate to next view controller
}
Make a protocol
protocol collectionViewCellClicked{
func cellClicked()
}
Implement this protocol in main View Controller Your View Controller look like this
class ViewController: UITableViewController, collectionViewCellClicked{ func cellClicked(){ // Your Code }}
Your cellForRowAt delegate look like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: <#T##String#>, for: <#T##IndexPath#>)
cell.delegate = self
return cell
}
In your Table View Cell Make a variable of type collectionViewCellClicked
var delegate: collectionViewCellClicked?
and in your didSelectItemAt delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate.cellClicked()
}
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
}