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
Related
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
}
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 need to create a collectionView A contains few types of cells. One of the cell contains collectionView B that can scroll horizontally. So collectionView A will scroll vertically.
Can I know how to create it and how to display the data into collectionView B?
Thanks again guys
Collection A have Cells lets call our target cell as ASubCell
then at ASubCell we will have Collection B with all of its delegate , and you will create delegate say ASubCellDelegate to send action on Collection B to ViewController of Collection A , here example
import UIKit
protocol ASubCellDelegate {
// CollectionView Cell Pressed
func onSubCellAtcolectionBPressed(_item : YourData)
}
class ASubCell: UICollectionViewCell {
#IBOutlet weak var collectionViewB : UICollectionView!
var delegate : ASubCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
// setup your Collection View B
}
}
// set up your horizontal Collection View
extension UICollectionViewCell:UICollectionViewDelegate,UICollectionViewDataSource {
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourBCellID", for: indexPath) as! YourBCellID
return cell
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if delegate != nil {
self.onSubCellAtcolectionBPressed(YourData)
}
}
}
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()
}
am trying to embed a UIStackView inside of a UITableViewCell. Inside of the UIStackView should be two items: a UICollectionView and another UITableView.
The approach I am taking is to embed everything inside of a UIView contained in a root UIViewController.
I have created a dataSource outlet from the parent UITableView and made the root UIViewController conform to UITableViewDataSource. Afterwards, I implemented the standard UITableView functions and did a similar thing for the embedded UICollectionView in the UITableViewCell. Here is the code for the root UIViewController and custom UITableViewCell:
class OverviewController: UIViewController, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "AppTitle"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell
return cell
}
}
class AppCell: UITableViewCell, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "featureCell", for: indexPath) as! AppCollectionCell
return cell
}
}
When I run this, I just get an empty table view:
What am I doing wrong?
I made a little project for you: https://github.com/mvdizel/q44833725
Use same dataSource's references as in project
Be careful with implementing data source methods
Your top table view has data source - your view controller.
Other one table view (inside the top table view cell) and collection view, both has data source - your cell.