Using Custom Static Table View Cell XIB in Storyboard - uitableview

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.

Related

How to add an UIView from a XIB file as a subview to another Xib file

I am trying to add a custom UIView that I created in XIB, to my view controller in my main.storyboard as a subview. How can I do this?
matchScrollView (tag 1) is the UIScrollView in view controller in main.storyboard, while matchView (tag 2) is the custom UIView I created in another XIB file.
With the press of a button i want to have the custom UIView added to the UIScrollView as a subview. But how can i actually make it show up on display? I guess i have yet to alloc and init it, along with indicate position and such, but how can i do that? I tried different ways without success. I can create UIViews programmatically, but have yet to find a way to just load the UIView from XIB.
-(IBAction) buttonTapped:(id)sender {
UIScrollView *matchScrollView = (UIScrollView *) [self.view viewWithTag:1];
UIView *matchView = (UIView *) [self.view viewWithTag:2];
[matchScrollView addSubview:matchView];
}
The reason that I am creating my custom UIView in another XIB file instead of directly implementing it on my main.storyboard view controller, is because I want to re-use the same view multiple times. So the UIScrollView has a numerous subviews of UIViews.
I was hoping I could create numerous instances of MatchView and add them all to matchScrollView as subviews.
The issue you are having is completely normal. The way Apple designed it doesn't allow to reuse custom views with their own xib into other xibs.
Lets say you have a custom view named HeaderView with a custom xib named HeaderView.xib. And lets say you want to be able to, in another xib named GlobalView.xib, drag a subview and specify its class to be of type HeaderView expecting it to load that view from HeaderView.xib and insert it inplace. You can do it like this:
A) Make sure File's Owner in HeaderView.xib is set to be HeaderView class.
B) Go to your GlobalView.xib, drag the subview and make it of class HeaderView.
C) In HeaverView.m implement initWithCoder, if after loading the view there aren't subviews means it got loaded from GlobalView, then load it manually from the correct nib, connect the IBOutlets and set the frame and autoresizingmasks if you want to use the GlobalView's frame (this is usually what you want).
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self.subviews.count == 0) { //means view got loaded from GlobalView.xib or other external nib, cause there aren't any subviews
HeaverView *viewFromNib = [[NSBundle mainBundle] loadNibNamed:#"HeaverView" owner:self options:nil].firstObject;
//Now connect IBOutlets
self.myLabel1 = viewFromNib.myLabel1;
self.myLabel2 = viewFromNib.myLabel2;
self.myLabel3 = viewFromNib.myLabel3;
[viewFromNib setFrame:self.bounds];
[viewFromNib setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self addSubview:viewFromNib];
}
return self;
}

iOS Container View in UITableViewCell

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.

Objects inside my custom UITableViewCell (created with storyboard) are nil

I've created a custom table view cell in my iPhone app through the following steps.
In my storyboard, I created a sample cell, dragged in a UILabel and a UIImageView.
Added new files, which I made a subclass of UITableViewCell.
In Interface Builder, I selected my cell and I assigned its class as the class I just created in step 2.
In the code for my custom table view cell, I created two IBOutlet properties and connected them to my UILabel and UIImageView in the storyboard.
My custom table view cell also includes a method, where it receives another object from which it sets its own attributes:
-(void)populateWithItem:(PLEItem *)item
{
if (item.state == PLEPendingItem) {
status.text = #"Pending upload..."; //status is a UILabel IBOutlet property
}
else if(item.state == PLEUploadingItem)
{
status.text = #"Uploading...";
}
imageView.image = [UIImage imageWithContentsOfFile:item.path]; //imageView is the UIImageView IBOutlet property
}
This method is called from my tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath as follows:
PLEPendingItemCell* cell = (PLEPendingItemCell*)[tableView dequeueReusableCellWithIdentifier:item_id];
if (cell == nil) {
cell = [[PLEPendingItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:pending_id];
}
[cell populateWithItem:((PLEItem*)[itemList objectAtIndex:indexPath.row])];
return cell;
The problem is that the cells always show up empty. I set a breakpoint in populateWithItem and realized that both the status UILabel and the image UIImageView were nil inside that method.
Shouldn't IB be initializing these? If not, where should I be doing that?
If you're setting up your cell in the storyboard, you always need to create your cell using tableView:dequeueReusableCellWithIdentifier:forIndexPath: because that's where the storyboard creates your cell and hooks up the views.
Creating a cell with its constructor directly, as in your sample code, won't load any subviews from a storyboard, nib, etc. The class you've made doesn't know anything about the storyboard prototype cells.

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.

Custom Table view cell not resizing as per nib

I created a new custom TableViewCell by subclassing UITableViewCell. I created a table view with UIImage view using a nib and I hooked it with view's outlets. Then while populating the table from my TableView delegate, I used the subclass for return table view cells.
New content from the nib is getting loaded. But the new size of custom table view cell (i resized the table view cell to a new large size) is not getting loaded in the table view.
Am I missing some call during the rendering? Please help
#interface AccountOption : UITableViewCell
{
IBOutlet UIImageView* optionIcon;
}
#property (nonatomic, retain) IBOutlet UIImageView* optionIcon;
#end
In delegate,
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"AccountOption" owner:nil options:nil];
for (id currentCell in topLevelObjects) {
if ([currentCell isKindOfClass:[AccountOption class]]) {
cell = currentCell;
}
}
cell.optionIcon.image = [(NSDictionary*)[accountOptions objectAtIndex:indexPath.row] objectForKey:#"icon"];
return cell;
You need to set the table view's row height either in interface builder, in code in your view controller, or in the table view delegate method tableView:heightForRowAtIndexPath:
Size of a cell is defined by its UITableView. The table has a property and a delegate method which define cell (row) height. Cell width is always equal to the width of the table.
You can try to resize the cell manually but it will be always overwritten by the table.
The height property is [UITableView rowHeight] and it is preferred over setting the height by table delegate method tableView:heightForRowAtIndexPath:. The delegate method should be used only when you need different cells to have different height.

Resources