So I have the main view of my controller that has a table view. This table view will be displaying many different custom classes that subclass UITableViewCell. Some of these cells will ALSO have table views inside of them.
My problem is that I do not know what class I should assign to to be the UITableViewDelegate in this sort of situation for the table view in the table view cell. My intial thought was to make it the cell view class:
class MyTableViewCell: TableViewCell {
#IBOutlet var tableView: UITableView!;
var messages: Array<String>?;
//called by parent tableview when cellForRowAtIndexPath is called in main controller
//to initialize view with dynamic properties at run time
override func render(obj: MyObject) {
messages = obj.getMessages();
}
}
extension MyTableViewCell: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages == nil ? 0 : messages!.count;
}
}
This is problematic because I have no where to register my nib files to the table view:
nib = UINib(nibName: "MyTableViewCell", bundle: nil);
self.tableView.registerNib(nib!, forCellReuseIdentifier: "custom");
Also, I feel like making a view a table view data source is violating MVC principles. What is the best way to go about with my table views within table view cells?
The UITableView within a UITableCell was indeed implemented in Pulse as described by genalipsis. In Obj-C,there is a full tutorial plus posted code located here for UITableView within a UITableCell:
http://iosstuff.wordpress.com/2011/06/29/adding-a-uitableview-inside-a-uitableviewcell/
http://iosstuff.wordpress.com/2011/06/29/creating-pulse-style-scrolling-horizontally-scrolling-uitableview-as-a-subview-of-uitableviewcell/
The was done in Xcode 4. I am not sure if this will work in Xcode 6.1 but it does describe a methodology.
I think an even more descriptive and easier to follow tutorial was posted at Ray Wenderlich's site here:
http://www.raywenderlich.com/4680/how-to-make-an-interface-with-horizontal-tables-like-the-pulse-news-app-part-1
http://www.raywenderlich.com/4723/how-to-make-an-interface-with-horizontal-tables-like-the-pulse-news-app-part-2
Create a regular UITableView
Create a custom UITableViewCell
Add a rotated UITableView as a subview of our UITableViewCell
Create another custom UITableViewCell for our articles
Rotate our custom cell and use it for our horizontal table view
While the tuorial was from 2011, some of the comments are very recent, so the approach must still work.
There is also a github project that references a stack overflow discussion from earlier this year:
https://github.com/hefgi/TableViewInTableViewCell
If you open the project, the storyboard file for the iPhone is instructive (Main_iPhone.storyboard):
A table view inside of a table view has been done before. One of the first iPad Apps, Pulse, used this strategy to allow uses to scroll vertically between RSS feeds and horizontally, within each cell, between RSS entries. That is, the embedded table view was rotated and the cells inside it where also rotated so that their orientation allowed reading.
For usability reasons, you will probably want to follow a similar pattern, else it might be difficult to scroll.
Tentative architecture: MainTableViewController is the controller for the table view that contains sub table views. SubTableViewController is the controller for the table views contained within the cells of the MainTableViewController. This approach lets you use TableViewControllers in the "standard" fashion
class MainTableViewController: TableViewController {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//dequeue a cell or create an instance of a cell
//create instances of SubTableViewController or change the data source for indexPath
//add the view of SubTableViewController to the cell's view hierarchy
//make necessary view adjustments depending on orientation, etc.
}
Not a standard setup, so I would expect that a few further hacks will be necessary.
Related
I’m coding a “chatbot” app, and to hold the message bubbles I have a UITableView and a custom message-bubble shaped cell. Here’s what it looks like so far:
All the cells will look the same, except I’d like every other cell to be, say, half the width of the table and alternating right/left aligned. How could I do this programmatically?
The better way - to create two classes InMessageCell, OutMessageCell, and add all properties (like aligning of all elements) hide inside of this cell. Both cell will have the same width, but all bubbles will be moved on one or other side. It may inheritance from the main class MessageCell, so all logic may stay in main class, but UI part - splitted up.
Two straightforward ways of achieving this by using custom table view cells:
Have two different cell classes. They can share much of their implementation (e.g. by class heritage), but differ in their layout (one is left aligned, one right aligned). For each row in your table, decide which cell you need to use, and dequeue the appropriate one.
Have one cell class whose layout can be toggled when it's being dequeued. (How exactly this toggle looks like depends of course on how you chose to layout your cell. It could e.g. be that you exchange the constant to an autolayout constraint you reference via #IBOutlet, the switching of a background image, or changing the alignment property of a stack view, among many others.)
Both ways depend on making a decision which cell flavor to use in your UITableViewDataSource's tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) function.
Here is a slightly abstract example using the first method. In your UITableViewDataSource:
enum ChatCellAlignment {
case left
case right
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellAlignment: ChatCellAlignment = self.cellAlignmentForIndexPath(indexPath) // you need to implement something like this
var identifier = "Default"
switch cellAlignment {
case .left:
identifier = "LeftAlignedBubbleCell"
case .right:
identifier = "RightAlignedBubbleCell"
}
let cell = self.tableView.dequeueReusableCell(withIdentifier: identifier)
if let cell = cell as? ChatBubbleCell { // assuming your custom cell classes all inherit from a "ChatBubbleCell"
cell.set(text: self.textForIndexPath(indexPath))
... // whatever setup you need to do on your custom cell
}
return cell
}
You can give the table view cell a value to know it. Then you can use autolayout (SnapKit) to make it align left or right
I've been learning table views from tutorials on YouTube. I was following every step told in the video, and did everything the same as the author of this tutorial, but for some reason I got a strange error - I've heard that it's about old version of Xcode used in the tutorial - I'm working on the latest one.
Debugger tells that "CustomCell" is undeclared.
Every help will be appreciated!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfCwiczenia.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell
let person = arrayOfCwiczenia[indexPath.row]
cell.setCell(cwiczenia.nazwa, imageName: cwiczenia.obrazek)
return cell
}
CustomCell looks to be a subclass of UITableViewCell
Looks like they left out the part where you need to create it
Create a new file called CustomCell and make sure it's base class is UITableViewCell
You must have a file where you define the behaviour of the custom cell - if that's not called 'CustomCell' then it won't be declared.
Just make sure that you have the same name when you define the class and when you use it.
I would suggest looking at Apple's walkthrough on how to implement a table view. It has step by step instructions with pictures. If you scroll down the the Design Custom Table Cells section I think you will be able to see how to link the custom cell to a file properly
Your tutorial should have mentioned all the details, but here it goes...
You need to define a subclass of UITableViewCell named CustomCell:
import UIKit
class CustomCell: UITableViewCell
{
// Your custom properties/methods.outlets/etc.
}
(typically, in a source file named CustomCell.swift, but this is not compulsory)
If you are using a storyboard, you need to set the class of your prototype cells to "CustomCell" in the identity inspector (third tab from the left on the right inspector pane - the one with the icon that looks like a newspapaer):
Also, In the attributes inspector (fourth tab from the right, icon looks like a slider), set the cell's identifier (in the case of your code, "Cell"):
If you are not using a storyboard, you need instead to register the custom cell class and identifier programmatically:
func viewDidLoad()
{
tableView.registerClass(CustomCell.self, forCellReuseIdentifier: "Cell")
// other setup...
}
I had this same error. Tried cleaning and building which worked but the main issue seemed to just be saving the CustomCell which then becomes recognised by the compiler and removes the error.
Its not limited to cells I've had it with other custom classes before as well. Good one to know about though!
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I am developing an app where I have a collectionview with 2 different tableviews. But what I need to know is how I can recognize which tableview is filled with data at that moment.
1) Use a different delegate and data source for your 2 table views
or
2) Create outlets for your 2 tables and just compare. The delegate and data source methods pass a reference to the table
Example
#IBOutlet weak var table1: UITableView!
#IBOutlet weak var table2: UITableView!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.table1 {
// something
}
if tableView == self.table2 {
// something else
}
}
Each UIView Object has a tagproperty, which is used to identify a UIView at runtime.
You could set it like this:
myTableView1.tag = Int.min
myTableView2.tag = Int.max
and in the Delegate and DataSource methods:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let rowCount: Int
if tableView.tag == Int.Min
{
rowCount = self.dataForTableView1.count
} else
{
rowCount = self.dataForTableView2.count
}
return rowCount
}
Use the tag value to identify the right TableView and that's it.
You need to give them different tags and then build your method around the use of these tags.
What is a CollectionView?
1.It's similar to a UITableView
2.Layout grid instead of columns.
3.Defined by the UICollectionViewFlowLayout.
4.Nees a custom UICollectionViewCell
5.Requires a data source and delegate
It can have sections just like TableViews have multiple sections. Items in each section are equivalent to the cells in a TableView.
The UICollectionViewCells have a stack of background properties. The first thing you drag into the cell will happen on top of the cell's content view. It' a property thats like an empty tabelViewCell. There's nothing in it, it's just a view so anything you drag on top of that view will be visible. such as an ImageView, LabelView etc.
If you turn the cell.contentView background colour on it will obscure whatever is underneath it. Underneath the cell.contentView is something called the cell.backgroundView / cell.selectedBackgroundView. Those are views that are initially nil but we can populate them with our own views.
Just drag a CollectionViewController to the storyboard and mark it as the Initial Controller. Take a look in the Document Outline panel (click square at bottom of Xcode window) and you'll see the CollectionViewController comes with a collectionView and in the CollectionView is a collectionViewCell and a UICollectionViewFlowLayout which controls where the cells are.
This is where you can have all of your fun. You can place any type of Views in there. Good luck.
I would like to create a custom header for a table view section with interface builder. I cannot use the method titleForHeaderInSection because i need to display two labels. I used this instructions: Customizing Header and Footer of TableView in iOS8 but it does not work.
What I have done so far:
Create a custom class CustomTableCell which inherits from UITableViewCell
class CustomTableCell: UITableViewCell {
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var dateDescriptionLabel: UILabel!
}
Create a dynamic prototype cell in storyboard
Add an identifier
Connect the labels to the custom UITableViewCell
Implement viewForHeaderInSection
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("CustomTableCell") as CustomTableCell
headerCell.backgroundColor = UIColor.cyanColor()
headerCell.dateLabel.text = "Test date"
headerCell.dateDescriptionLabel.text = "Test date description"
return headerCell
}
When i run the app the section appears one second and then moves under the table cells and I get an error: no index path for table cell being reused.
What is wrong with this solution? I downloaded the project from the tutorial I have linked and it works there.
Your problem has its roots in how UIKit handles UITableViews.
To make sure table views are fast and responsive, even with a large amount of cells, the cells are re-used.
By calling tableView.dequeueReusableCellWithIdentifier("CustomTableCell") you are asking the tableView to give you a cell to reuse.
A lot of people have been using the reusable cells to design their headers/footers in Storyboards. Ever since iOS 7 Beta 5 this may lead to errors. This answer explains the situation well: What is the meaning of the “no index path for table cell being reused” message in iOS 6/7?
To design your own custom header/footer views I don't recommend using UITableViewCells. Instead you should create and design a custom UIView directly in your code. If you want to use Interface Builder you could create a .xib or create a view in your Storyboard that is not a subview of the actual controller's view.
I have a UITableViewController and when I click on it, it will show a DetailViewUIController.
I want to add a functionality which when I am in DetailViewUIController and I swipe to right, it will show the DetailViewUIController of next item and when left, it will show the previous item.
I find a link which kind of do that in swift. But it only has 3 static subviewcontroller.
https://medium.com/swift-programming/ios-swipe-view-with-swift-44fa83a2e078
The number of entries in my TableView can be pretty long, how can I do the same thing dynamically, i.e. without having static number of subviewcontroller created and add as 'addChildViewController'?
Update:
Thanks again #rdelmar for your help.
I am having trouble getting the ' set the contentOffset of the collection view to (collection view width) * selectedRow' to work.
In my prepareForSegue function, I have added:
x = Float(indexPath.row) * 375.0
var point = CGPointMake(CGFloat(x), 0)
println ("***x=")
println (x)
detailController.collectionView?.setContentOffset(point , animated: false)
where detailController is UICollectionViewController.
and in the
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as DetailViewCell
// Configure the cell
println("DetailViewController in cellForItemAtIndexPath()")
println(indexPath.row)
cell.myitem = allItems[indexPath.row]
return cell
}
And I always see cellForItemAtIndexPath trying to get 0th element of the allItems (that is the whole collections of all my objects.
So setContentOffset() does not change what I am displaying in the Detail View regardless which item I click in my TableView to launch the Detail View.
Any idea to solve this?
Thank you.
There are a lot of different ways you could do this. One way would be to make your detail view controller's view be a collection view (with paging enabled), whose cells are the same size as the screen. You would need to pass in the array that you use to populate your table so the collection view could populate its cells. This would be quite efficient, since the collection view would only ever need to instantiate two cells.