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.
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 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 2 prototype dynamic cell called InvoiceDetailCell and TotalCostFooterCell. I make the TotalCostFooterCell as the Footer Cell View using viewForFooterInSection. here is the code I use to assign data to the UITableView
here is my code in UITableViewController.
extension InvoiceDetailVC : UITableViewDataSource {
// MARK: - UI Table View Datasource Methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return invoiceElements.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InvoiceDetailCell", for: indexPath) as! InvoiceDetailCell
cell.invoiceElementData = invoiceElements[indexPath.row]
return cell
}
}
extension InvoiceDetailVC : UITableViewDelegate {
// MARK: - UI Table View Delegate Methods
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "invoiceDetailFooterCell") as! TotalCostFooterCell
cell.totalCost = singleInvoiceData.unpaid
return cell
}
}
but the result is not as I expected, I mean the footer cell is stick / not move. here is the .gif file : http://g.recordit.co/vf0iwCfEWX.gif
you can see the total cost (red colour) is sticky / static, I want that footer cell can be scrolled and always on the bottom. or do I have the wrong to implement what I want?
make the table style to grouped
you can do it in two ways:
In viewDidLoad() do tableView.style = .grouped
Select the table view from storyboard and in the attribute inspector change the style to grouped. Please refer attached image.
Can't you just make it as the last row of the table view? I mean, the view is already a table view cell, so it makes sense to use it as the last row.
First change this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return invoiceElements.count + 1
}
And then for cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == invoiceElements.count {
let cell = tableView.dequeueReusableCell(withIdentifier: "invoiceDetailFooterCell") as! TotalCostFooterCell
cell.totalCost = singleInvoiceData.unpaid
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "InvoiceDetailCell", for: indexPath) as! InvoiceDetailCell
cell.invoiceElementData = invoiceElements[indexPath.row]
return cell
}
You can set your custom view that you want by:
tableView.tableFooterView = yourCustomView
Or you can put everything inside your xib/storyboard like this:
I have a ViewController which has a collection view. Inside each collection view cell their is a tableview. And I am facing a performance issue because of that tableView. I try to reloadData for each of CollectionViewCell when its cellforItemAtIndexPath is called. My CPU usage gets uptp 90% because of that. My ViewController looks like this
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCustomCollectionViewCell", forIndexPath: indexPath) as! MyCustomCollectionViewCell
// This line is causing performance issue
cell.tableView.reloadData()
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(40, 40)
}
MyCustomCollectionViewCell looks like this
class ChannelCollectionViewCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate {
#IBOutlet weak var tableView: UITableView!
var delegate: ChannelCollectionViewCellDelegate?
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 10
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 120
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCustomTableViewCell", forIndexPath:
indexPath) as! MyCustomTableViewCell
cell.title = "Testt Title"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50
}
}
Can cell.tableView.reloadData() be moved to a better place so that it doesn't cause performance issues
I don't think so, because UITableView like UICollectoinView fill themselves with dataSource, and when you dequeue another UICollectoinViewCell you have to call reloadData() (because this cell might be initialized before and you have to refill it).
What you can do is to stop dequeuing (do it at your own risks). Dequeuing is a technique for memory optimization, If you have only 10 cells in collectionView, you "can" init a UICollectionViewCell directly and show it. But collectionView will still call cellForRowAtIndexPath so you have to cache all cells for each indexPaths, if you have already initialized the cell, return it. But this technique uses lots of memory, so it's probable you receive lots of memory warnings.
I guess you're implementing something like Trello or something (because tableView in collectionView which tableView contains about 120 rows does not sound logical to me :/). If that's the case i suggest change your architecture to UIPageViewController + UITableViewContoller.