I've seen many tutorials online pertaining to embedding UICollectionViews inside dynamic table view cells and subclassing the collection view to set the delegate but I was wondering if the process is any different for static table view cells.
I tried following the example here but I couldn't quite follow it through because it seems overly complicated with little to no explanation. Would anyone mind outlining the basic steps I need to go through with in order to get my collection view to work?
Here's my code so far:
class FeaturedController: UITableViewController, UIScrollViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
popularCollectionView.dataSource = self
popularCollectionView.delegate = self
}
//MARK: Popular Events Collection View
#IBOutlet weak var popularCollectionView: UICollectionView!
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = popularCollectionView.dequeueReusableCellWithReuseIdentifier("popular", forIndexPath: indexPath) as! UICollectionViewCell
return cell
}
Many thanks!
If you know how to add a collection view to a dynamic table view cell, adding it to a static one would be far easier. You don't need to sub class anything at all (but doing so could be a nice idea). Under the hood, a static table view is nothing more than just a normal table view that having support from the hosting UITableViewController to set what you have layout in the Interface Builder automatically. So, here's how to do it:
Dragging a Collection View from the Object Library in the Interface Builder and place it in the cell you want.
Make sure the Table View is hosted by a Table View Controller.
Set constraints or layout the Collection View in the cell.
Set the Collection View's dataSource to the hosted Table View Controller.
Adding UICollectionViewDataSource conformance to the Table View Controller.
Implement UICollectionViewDataSource's methods, namely collectionView:numberOfItemsInSection: and collectionView:cellForItemAtIndexPath: in the UITableViewController.
If you know how to work with a UITableView or UICollectionView in general, this should not be hard to follow.
UPDATE
Your code looks like it should have worked.
So, you should check whether:
You've really set the class of the Table View Controller to your FeaturedController class.
You really have wired the Collection View in the Interface Builder to popularCollectionView.
You already have a prototype Collection View Cell with the identifier popular. Although, it should crash if you haven't done so.
In the IB, you have already set the Table View to be static.
I have a small example I did here
The orange view is the Collection View with the greenish view the prototype Collection View Cell with identifier myCell.
And then I set my view controller to the Collection View's data source. But you could set it in the code too like you did too.
Then I implement the data source methods below.
#interface ViewController () <UICollectionViewDataSource>
#end
#implementation ViewController
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 20;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"myCell" forIndexPath:indexPath];
return cell;
}
#end
And this is the result:
For TableView static cells:
In your view controller where you have connected collection view, add in view did load delegates and data source for the collection view, this will add the delegates to tableview cell, if you do with storyboard, you are connecting it wrong because delegates and datasource has to be inside cell initialization.
#IBOutlet weak var quotesCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
quotesCollectionView.delegate = self
quotesCollectionView.dataSource = self
}
For TableView dynamic cells:
class QuotesTableViewCell: UITableViewCell {
// MARK: Properties
#IBOutlet weak var quotesCollectionView: UICollectionView!
// MARK: Initialization
override func awakeFromNib() {
super.awakeFromNib()
quotesCollectionView.delegate = self
quotesCollectionView.dataSource = self
}
}
Related
I'm currently trying to build a screen that contains 2 UIButton, and 1 UIImageView.
Below these UI Elements, I want to add a static table view that would contain 1 UITextField for each cell in order to create a kind of scrollable form.
The error I'm having is the following one:
Static table views are only valid when embedded in UITableViewController instances
While it doesn't seem possible to create a static table view without a table view controller, I was wondering if there could be any way to get the same result as my initial idea?
Please note that I'm building my UI using storyboard.
Here's a screenshot of what I was trying to build initially:
EDIT: I finally decided to use a static view controller, and implemented the buttons in a cell and the other textfields in different cells. Thank you all for your help.
You can add the UITableViewController as a childViewController to your bigger UIViewController (parentVC)
Then manage parentVC's view hierarchy so that you can achieve the 2 UIButton, 1 UIImageView and a table view at the bottom
In Xcode 10.2 you can use Container View to implement the UI you described. Drag and drop a container view object to the required view controller in your storyboard scene:
Then add UITableViewController instance to your storyboard scene:
Set Static Cells for it's Content:
Then right-click on Content View that you added in one of the previous steps, and setup it as described on the following screenshots:
Setup constrains and cells content. Then you will see something like that on your testing device:
I think you should manage this adding elements in a UIScrollView, there's no need to use a UITableView. So you can scroll all the contents when you show the keyboard
A static tableview is nothing more than a UITableViewController handling the UITableView's UITableViewDataSource methods on your behalf.
You can simply add a UITableView to your UIViewController, set the UITableView datasource to your UIViewController and implement the methods as appropriate.
e.g.
class MyViewController: UIViewController, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
tableview.datasource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create UITableViewCell
}
}
I have a UICollectionView with horizontal scrolling direction.
It has a button. On that button click i want to show a list tableView. My question is where should i implement table view delegate method,
in BasicVC, where i create collectionView,
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier:"cell", for: indexPath)
let btn = c.viewWithTag(9) as? UIButton
btn?.layer.masksToBounds = true
btn?.layer.cornerRadius = (btn?.frame.size.height)!/2
btn?.backgroundColor = getRandomColor()
return c
}
or in collectionCell class.
class PlayerHeaderCell: UICollectionViewCell{
#IBOutlet weak var btn: UIButton!}
tableView delegate methods needs to implemented in UICollectionViewCell like so
class PlayerHeaderCell: UICollectionViewCell, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var btn: UIButton!
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
return cell
}
}
It sounds like you want a table view to popup on top of the collection view like a drop down menu right? If you're wanting to have a table view that is displayed over top your current view, I'd recommend creating a new view controller for it in your story board that contains the table view, and then presenting that controller on top of the current view. The result would be a table view floating wherever you want on top of your collection view.
Unlike what the other answer said, do NOT make the collection cell the table view delegate, it just doesn't make sense unless you want a different table view for every cell, in which case you should do this and set the delegate to be the selected collection cell.
To implement the popup table view:
Create a view controller in storyboard
Create a new table view, place it INSIDE the new view controllers default view (the default view is used as the background but will be invisible.)
Set view controller's presentation to be Over Content
Set the new view controllers background view to be completely transparent by changing its color
Ctrl drag from your previous view controller to your new view controller and create a modal segue, give it some identifier string "id".
Call performSegue with the ID you set will cause the table view to appear as it is in the new view controller you created.
The advantage to this approach is that your table view can be designed in it's view controller and wont clutter up the original view controller in a storyboard. Just my thoughts, you seem to be doing things programatically so I'm not sure if it's suitable to your use case but I hope it helps.
It depends on where you are showing the tableview. I mean when you tap button, if you are navigating to a new controller with tableview, the delegate should be in the new controller. Or if you are showing the tableview in the same controller, the delegates will be in BasicVC. I think the collection cell has only the button.
This is my understanding. Someone please correct if I am wrong.
I have a collectionView with cell and then again a collectionView in this cell. This child collectionView is again having a cell in it. Now i want to communicate with the cell in my child collectionView from the parent collectionView. How to do that in swift 3 in storyboard? As shown in the image attached I want to communicate from first collectionView to the radio cell
In your main ViewController.swift conform it to the protocols UICollectionViewDataSource, UICollectionViewDelegate.
Now from your storyboard take an outlet of your parent CollectionView to your ViewController.swift.
Then create a Cocoa Touch Class which is subclass of UICollectionViewCell for your custom cell of UICollectionview and take an outlet of your child CollectionView in this class.
Now go to your storyboard and select your parent Collectionview cell and set its class to your custom cell class and then set an Identifier for this cell in Attributes inspector.
Now again create class for your child UICollectionview cell and do the same as parent UICollectionview.
Now everything is setup in your storyboard. After this you should write the code for your procedure in delegate methods of UICollectionView.
Now in your ViewController.swift file write the delegate methods of UICollectionView.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell =
parentCollectionViewOutlt.dequeueReusableCell(withReuseIdentifier: "parentcell", for: indexPath) as! ParentCollectionViewCell
cell.childCollectionView.reloadData()
return cell
}
Now in your parent CollectionView cell class file:
Also conform it to the protocols UICollectionViewDataSource, UICollectionViewDelegate and write all delegate methods for UICollectionView and then write following code as:
override func awakeFromNib() {
super.awakeFromNib()
childCollectionView.delegate = self
childCollectionView.dataSource = self
childCollectionView.reloadData()
}
Now here in cellForItemAt() method do the same procedure for taking cell of your child CollectionView by specifying its identifier and then do your code in it and return your cell. Here you can do whatever you want to do in your child CollectionView's cell that means you have an access to your child CollectionView's cell in this class for custom cell of your parent CollectionView.
Follow this link
https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/
In this tutorial the same scenario is being explained just one change
adding UICollectionView in UITableViewCell.
Go through this above link , and try to implement. It will help you
I have looked around for this issue, and have not found an answer that works for my case. Basically, I have a tableView where each cell contains a collectionView. What I would like to do is to refresh the specific TableViewCell where the collectionView was scrolled, in order to update a label under the CollectionView, within that TableViewCell.
So when the user scrolls on the collectionView, I need to know in which cell that collectionView is. I have tried using didSelectRowAtIndexPath, however, it only is called when the non-CollectionView part of the cell is tapped. When the collectionView is tapped or scrolled, it isn't called. I am coding in Swift.
Any ideas on how I can do this?
Thanks!
This seems like a architecture issue. Sure it can be done the way you want, but it'd be much easier if your rearranged some things. There is a fundamental problem how you want to do this. You want to manage all of the cells and their collection views directly from your view controller. But this poses the problem of needing to know where messages are coming from and directing messages back to the correct cells and collection views. This will create a lot of bloat that can be fixed with a simple UITabelViewCell subclass. It also is a step in contracting Massive View Controller syndrome. Instead, you should make the individual cells responsible for managing their own collection views.
First off, make the UITableViewCell own and be the delegate and data source of the UICollectionView. This centralizes the data and more closely models the tree of data that you actually see on screen.
class CollectionTableViewCell: UITableViewCell {
#IBOutlet var collectionView: UICollectionView! {
didSet(newCollectionView) {
newCollectionView.delegate = self;
newCollectionView.dataSource = self;
}
}
var model: NSArray?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
extension CollectionTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return model?.count ?? 0
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
// configure cell
return cell
}
}
Now you wanted to have a state refresh when your collection view scrolls. So we're going to add a label to the cell in the prototype cell (Nib or Storyboard.)
#IBOutlet var indicatorLabel: UILabel!
And you want to update it when the collection view is scrolled. We can do that with the scrollViewDidScroll method off the UIScrollViewDelegate protocol. Because UICollectionViewDelegate implements the UIScrollViewDelegate, it's available for us to use since we implement the UICollectionViewDelegate in our extension.
// in the CollectionTableViewCell extension...
func scrollViewDidScroll(scrollView: UIScrollView) {
let scrollViewOffset = scrollView.contentOffset.x
let scrollViewWidth = CGRectGetWidth(scrollView.frame)
let completionString = String(format: "%# / %#", scrollViewOffset, scrollViewWidth)
self.indicatorLabel.text = completionString
}
So by making the individual cells responsible for their own respected collection views, we make managing them easier. It allows us organize our code to be more compact, understandable, and keeps us from getting Massive View Controller syndrome. The more code you can move out of your view controller, the better.
Some good talks to hear on this would be:
WWDC 2014 – Advanced iOS Application Architecture and Patterns
Let's Play: Refactor the Mega Controller!
You can use tag property of UITableViewCell. Set tag to row number and when cell is tapped, fetch tag property to find out the tapped cell index.
Your CollectionView will be contained within some kind of cell. Once you have found this cell, you can ask the table for the index. Navigate from your CollectionView up the view hierarchy to find the cell. For example:
CollectionView* collectionView = // your collectionView;
UITableViewCell* cell = (UITableViewCell*)[collectionView superview];
UITableView* table = (UITableView *)[cell superview];
NSIndexPath* pathOfTheCell = [table indexPathForCell:cell];
NSInteger rowOfTheCell = [pathOfTheCell row];
NSLog(#"rowofthecell %d", rowOfTheCell);
I've just joined StackOverflow and i'm struggling with a programming requirement with a iPhone app I'm developing in swift. I have a tableview list of different calculators and i would like to segue to another UITableView when a item is clicked to then do the detailed calculations for that tool.
I am likely to have lots of tools in the first table (>20) and so i don't want to use storyboard to draw up each new UITableView static table with a different segue for each one.
I wonder if anyone can give me some advice on how to programmatically code a presentation of a new UITableViewController with static cells when a item is clicked. I don't want to use storyboard so i would need to use code to both manage the presentation as well as the generation of the next UITableViewController with static cells.
I have been able to program a static tableview with a custom class programmatically and linked to a storyboard UITableViewController item but i want to do all this programmatically and cut out storyboard all together.
Any advice you could offer would be greatly appreciated.
Thanks.
A UITableViewController abstracts some things. It seems like maybe what you want to do is to separate things out and have a little more granular control.
You can do this fairly easily. You need 3 things to make this happen:
UITableView
UITableViewDataSource
UITableViewDelegate
A UITableViewController puts these all together for you. We'll have to create them ourselves.
To do this, we make a View Controller, and inherit UITableViewDataSource and UITableViewDelegate
class Example: UIViewController {
}
// MARK - UITableViewDataSource
extension Example: UITableViewDataSource {
// We need to implement some methods here
}
// MARK - UITableViewDelegate
extension Example: UITableViewDelegate {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Handle the user clicking an item here
}
}
Three things left to do:
Create and show the table
Get the data to display in the table
Implement the delegate methods
Creating the table
You should decide if you want to completely programatically create a UITableView, or have Interface Builder lay one out for you in a .xib, and you just link it up via an IBOutlet.
Assuming you want to do the former, you can do the following:
var table: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
table = UITableView(frame: view.bounds)
view.addSubview(table!)
table?.delegate = self
table?.dataSource = self
}
Get the data
When you push this view controller from your previous view controller, be sure to set a variable on this view controller with your data. Assuming you have an array, it's as simple as something like:
exampleViewController.myData = someArray;
navigationController?.pushViewController(exampleViewController, animated: true)
(be sure to create the myData variable in your View Controller to take this)
Implement the delegate methods
Now we can implement the delegate methods to show the data. You may already be familiar this, but for the sake of completeness:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// You should really use dequeueReusableCellWithIdentifier here.
var cell = UITableViewCell()
cell.textLabel!.text = myData[indexPath.row]
return cell
}