iOS Container View in UITableViewCell - ios

I'm trying to add another view controller inside a UITableView cell. The idea is that you tap the cell, and it expands to show more content--a messaging interface. It's important (I think) that this is controlled by a separate Messaging ViewController.
Expanding the cell and having views inside the cell expand with the proper constraints is actually very straightforward in Storyboards, so I tried to keep everything in storyboards by adding my new VC to the TableViewCell via a Container. That way I'd be able to add constraints on the container view, and pipe the content in from my Messaging VC.
Here's the error:
Illegal Configuration: Container Views cannot be placed in elements that are repeated at runtime.
Any way to get around this issue, or is there a way I can pipe the view from my viewcontroller into this tableviewcell and have it constrain to a configuration that I set in Storyboards? Thank you!

I had the same task and decided it this way:
Step 1. Create subclass MyCell: UITableViewCell.
Step 2. If you use Self-Sizing Cells, in InterfaceBuilder add UIView to MyCell, then add height constraint and constraints to all sides. This view needed for set height of cell.
If not, skip this step and use heightForRowAtIndexPath.
Step 3. In MyCell.h add outlet from view height constraint and controller property:
#interface MyCell: UITableViewCell
#property (weak, nonatomic) MessagingVC *controller;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewHeight;
#end
Step 4. In cellForRowAtIndexPath add code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell" forIndexPath:indexPath];
// adjust this for your structure
cell.controller = [[UIStoryboard storyboardWithName:#"MessagingVC" bundle:nil] instantiateInitialViewController];
[self addChildViewController:cell.controller];
[cell.contentView addSubview:cell.controller.view];
[cell.controller didMoveToParentViewController:self];
// if you use Self-Sizing Cells
cell.viewHeight.constant = 200; // set your constant or calculate it
return cell;
}
Step 5. Add didEndDisplayingCell method:
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cell isKindOfClass:[MessagingVC class]])
[((MyCell*)cell).controller removeFromParentViewController];
}

Make your UITableViewController content as Static.

You can just drag Container View into UITableVeiw in the storyboard. For example, you can drag it before prototype cell, and you will see your container's view controller before your prototype cells. By the way you can drag any UI element to table view. I'm not sure, how to deal with autolayout in the combination table view + container view, maybe you need to manually calculate / set constraints at the runtime. Will update my answer when I'll find the right solution about autolayout.

Putting container views in table view cells is way too heavy. Table view cells should be lightweight so the user can scroll through them quickly. It's not necessary to put the entire view controller in each cell. The cell should just represent some of the data for that row.
When the user touches the cell you just use a normal segue to the messaging view controller. Its presentation will be automatic. Then create and specify an animationController to handle the transition to make it appear as though the message composition view was contained within the table view cell.

Related

iOS: create table "header" programmatically from existing cell

My tableOne contains custom UITableViewCell called CustomCellForTableOne (.h and .m). When user clicks on a cell in tableOne, I want them to segue to tableTwo. So far so easy.
I want the cell in tableOne to become the table header in tableTwo. Is there a simple straightforward way for doing this? Ideally, I want to simple create a CustomCellForTableOne from data that are passed through the segue and assign that cell as the header of my tableTwo.
I don't know how to assign header to a table programmatically (so I could try it)
Would it work? (since I haven't tried it yet anyway, I thought I might ask to save some time) Or must I take some different approach?
It should be possible as cell and header view for table both inherit from UIView. Implement the following in your tableTwo and return your table view cell instance from it. -
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section
Headers are hard to handle in table views. I would place an extra section in the second table only for that specific cell which has been passed through. This could be the first section of the second table and you could design it as it looks like a header.
Another way is to create a plain view and place it in the header of the second table (this is totally legal). Then you can safely add your specific cell (if it has a view) in that plain view via addSubView: .
I was able to achieve what you're looking to do with the following code in the first view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// create the new table view
TestTableViewController2 *testView2 = [TestTableViewController2 new];
// get a reference to the cell that they clicked and create a totally new cell, a 'copy' of
// the original that we can pass to the next view
UITableViewCell *clickedCell = [tableView cellForRowAtIndexPath:indexPath];
UITableViewCell *clickedCellCopy = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
clickedCellCopy.textLabel.text = clickedCell.textLabel.text;
clickedCellCopy.detailTextLabel.text = clickedCell.detailTextLabel.text;
// set this new cell as the header cell on the new view
testView2.myHeaderCell = clickedCellCopy;
// push the new view onto the stack
[self.navigationController pushViewController:testView2 animated:YES];
}
Then add the property to your second table view controller:
#property (nonatomic, retain) UITableViewCell *myHeaderCell;
And then in the .m's viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// set our header cell as the table view's header
self.tableView.tableHeaderView = self.myHeaderCell;
}
That gave me the following result:
https://www.dropbox.com/s/t42lraue1kfolf3/cell%20demo.mov?dl=0
You could create your custom UITableViewCell in a xib file and load it for each view. You are going to have to create it in both view controllers. Depending how complex, you could also do this entirely in code.

xcode document outline has extra content view

I am learning objective c and I'm following this tutorial here:
http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1
Whenever I create a table cell view, It creates an extra content view that makes it impossible to link my labels, images to the cell. How Can I delete the extra content view and link my objects?
Here is a screenshot showing what I mean...
Getting an Error with this code... use of undeclared identifier on cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MingleViewCell *cell = (MingleViewCell *)[[tableView dequeueReusableCellWithIdentifier:#"MingleViewCell"]];
LocalPerson *local = [self.people objectAtIndex:indexPath.row];
cell.nameLabel.text = local.name;
cell.aboutMeLabel.text = local.game;
return cell;
}
Relax. From the docs:
The content view of a UITableViewCell object is the default superview for content displayed by the cell. If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode.
This is how it's done, the content view should be there and you should be able to connect IBOutlets/IBActions normally by ctrl-dragging from your elements added to the cell in the storyboard into the custom cell class code.
you should not delete the content view which you are calling is extra. If you see UITableViewCell has a property contentView by default in which you add all the other content like UIButton, UILabel, etc. It's this contentView which is the main container.
So whenever you inherit any class from UITableViewCell, that class also has this contentView. So instead of deleting it you can use it as a container and add other elements.

Cannot set properties on custom table view cell

I have a problem settings my view elements on a custom cell. The table cells appear in my tableView, but the properties do not set and thus only empty/blank cells appear.
The tableView is not a tableView controller, but only a tableView in a viewController.
I have the following files:
CustomCell.xib:
Here i use IB to build the custom cell by using a Table View Cell from object library with labels and images on. I set the Identifier as orderListCell. From this screen I ctrl+drag to create the outlets in customCell.h
CustomCell.h
Here I see all my IBOutlets as properties from above mentioned file
CustomCell.m
Here I leave as is
OrderListViewController.h
Here I import customCell.h and use protocols UITableViewDelegate, UITableViewDataSource
OrderListViewController.m
Here I set my tableView delegate and tableView dataSource to self. I also create an IBOutlet for my tableView from the Storyboard.
I use the following code to try and display my cells:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"orderListCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[CustomCell alloc] init];
}
cell.myLabel.text = #"aaaaaaaa"; //[[self.orders objectAtIndex:indexPath.row] objectForKey: #"tableNo"];
return cell;
}
I have simplified my code a bit to demonstrate that even setting the label to a simple string (#"aaaaaaaa") doesnt work. When I look at the objects in my debugger the cell does have all the properties from the IBOutlets and the cell does appear in my tableView, just the label.text = xxx does not seem to work.
I have looked at the following posts but either dont understand it properly or it does not work for me:
ios 7 customizing UITableViewCell's content view
Can't set properties in Custom UITableViewCell
Set label for a custom cell
Any help or advice would be greatly appreciated
cell = [[CustomCell alloc] init]; does not create your cell from the XIB, so none of the XIB content will be created.
Use registerNib:forCellReuseIdentifier: to register the XIB with the table view so that it will create your cell instances for you (you don't need to create instances yourself in cellForRowAtIndexPath).
If you don't want to do that, you can load your NIB explicitly with nibWithNibName:bundle: and instantiateWithOwner:options:, then get the cell instance from the list of returned views (which should only contain 1 item).

Using Custom Static Table View Cell XIB in Storyboard

I have a custom static UITableViewCell that I want to look exactly the same as a right detail cell (UILabel on the left, UILabel on the right), except I want the right UILabel to be an editable UITextField. Since I want to use this cell in multiple view controllers in my storyboard, I decided the best option would be to create a MyEditableTableViewCell.xib file, along with corresponding *.h,m files. I have two public properties in MyEditableTableViewCell.h:
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#property (weak, nonatomic) IBOutlet UITextField *detailTextField;
These IBOutlets are connected to the UILabel and UITextField in the .xib file. In my storyboard, where I have a custom UITableViewController subclass, I change the class of the necessary UITableViewCell to MyEditableTableViewCell. I have a property in my view controller that is:
#property (weak, nonatomic) IBOutlet MyEditableTableViewCell *myCell;
However, when I run the app, the cell appears as simply a blank cell. Running some checks on it, I see that [myCell isKindOfClass:[MyEditableTableViewCell class]] returns true. However, the UITextField never seems to get instantiated. When I try to alter myCell.detailTextField.text, nothing happens, and myCell.detailTextField appears to be nil.
Any help would be appreciated!
How are you creating instances of MyEditableTableViewCell? My guess is you're doing [[MyEditableTableViewCell alloc] init] instead of loading them from the nib.
You don't need that property in your view controller. You need to register your cell nib with the table view using -[UITableView registerNib:forCellReuseIdentifier:]. Do that in your viewDidLoad. Then, when you send dequeueReusableCellWithIdentifier: to the table view, it will instantiate the nib, creating a MyEditableTableViewCell, and return it to you.
UPDATE
If you're laying out static cells in a storyboard, using a xib to lay out a cell is somewhat difficult. The simplest thing to do is simply lay out the cell's subviews in the storyboard. You can copy and paste the subviews to each cell if you have a few of the same type.
If you really want to use a xib, the easiest way is to structure your cell's view hierarchy like this:
MyEditableTableViewCell (in storyboard)
|
+- cell's content view (created automatically by UIKit)
|
+- UIView (top-level view of the xib)
|
+- UILabel (textLabel)
|
+- UITextField (detailTextField)
So you set the cell class in the storyboard to MyEditableTableViewCell. Then you create your xib. You set your xib File's Owner class to MyEditableTableViewCell. The xib does not contain a MyEditableTableViewCell. The top-level view of the xib is just a plain UIView, containing the subviews (the label and the text field).
In -[MyEditableTableViewCell initWithCoder:], after doing self = [super initWithCoder:aDecoder], instantiate the xib, passing self (the cell) as the xib file's owner. The cell can have outlets connected to the label and the text field in the xib.
After instantiating the xib, add the top-level view from the xib as a subview of self.contentView. If you're using auto layout, create constraints betwixt the top-level view and the content view. Otherwise, set the top-level view's frame to the content view's bounds and set the autoresizing mask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight.
Although a static table view will not fetch your cell from the nib, it will still call cellForRowAtIndexPath. There you can dequeue and return your fully unarchived custom cell. Properties on the custom cell that were set in IB, can be fetched by calling super on cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get an empty cell with properties retained from IB
UITableViewCell *sup = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if ([sup isKindOfClass:[MyCustomCell class]]) {
// fetch a fully unarchived cell
MyCustomCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"foo" forIndexPath:indexPath];
// copy properties over...
[cell copyPropertiesFromCell:(MyCustomCell *)sup];
return cell;
}
return sup;
}
Depending on the situation, this might actually be easier than to mess with IB objects.

Set detailtextlabel of static cell programmatically

I have a tableView with a few sections and I have it set for static cells instead of dynamic prototypes. The problem is that I can't set the detail text label of a static cell programmatically or at least I don't know how. Is it possible ? The only way I see of doing this is having dynamic prototypes which means I'm going to have to deal with setting up all the cell.textLabels in my dataSource and also all the sections and my segues will not work anymore. If anyone has ideas it would be great help. Thanks :)
Assuming that your UITableView is in a UITableViewController, here are 2 approaches that are useful:
Custom UITableViewCell: In the view controller class, declare a property for a label as: #property (strong) IBOutlet UILabel *labelInCell;
In the storyboard, drag a UILabel into the cell, select the controller's Connections Inspector, and connect the outlet of the property by dragging from the inspector to the UILabel object.
You can then assign the label text programmatically, for example, in viewDidLoad: of the controller class.
Standard datasource: Alternatively, you can implement just one method: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath in the view controller's datasource and set the detailTextLabel property there.

Resources