Programmatically modify a UITableViewCell initialized from a nib? - iphone

I am loading a UITableViewCell from a nib using [[NSBundle mainBundle] loadNibNamed:...]. Now I want to do some post-initialization work programmatically, before the tableviewcell get used in my code. Where should I put this code, as I can't seem to do it in the initWithCoder methods, as the label objects in the class are still nil (so can't set anything). When are the UILabels in the tableviewcell initialized in the first place (they are all defined as IBOutlets)?

You should subclass UITableViewCell, and put an awakeFromNib method in it to perform initializations after awaking from the nib.
To keep your code flexible, put this init code in some routine called myInit and call that from awakeFromNib, and from other places where it should be called.
After some struggles I've come up with a slightly different approach for this situation. I subclass UITableViewCell and have an init routine like this:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"MyUITableViewCell" owner:self options:nil];
[self addSubview:self.contentView];
}
return self;
}
where contentView is an IBOutlet containing the cell content. This allows the rest of my code to just invoke this cell as any other cell. (apart from one nasty cast for (MyUITableViewCell*)[tv dequeueReusableCellWithIdentifier:CellIdentifier];)

Related

Storyboard and xib use in same project

I am stumbling over what I believe is probably a fundamental misunderstanding of how classes work in Objective-C. I am using Storyboards but in this app I wanted to create a simple custom date picker view for a textfield on one of my view controllers. However, I seem to be having a problem accessing any of the properties of the date picker class from my view controller:
First I modeled my CustomDatePicker.xib in IB as follows:
I have created a custom class by sublcassing UIView as follows:
CustomPicker.h
#interface CustomPickerView : UIView
#property (strong, nonatomic) IBOutlet UIDatePicker* datePicker;
#end
CustomerPicker.m
#implementation CustomPickerView
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, 320, 258)];
if (self) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"CustomPickerView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
In my ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.customPickerView=[[CustomPickerView alloc] init];
// THE MAIN ISSUE IS...
// Following line has no effect on how picker is presented ???
self.customPickerView.datePicker.datePickerMode=UIDatePickerModeTime;
self.dateField.inputView=self.customPickerView;
}
When the textfield is tapped, my CustomDatePicker pops up fine. However, I can't seem to set the .datePickerMode either from the viewDidLoad method of my ViewController. The only way I can change the .datePickerMode is through IB and of course that's not going to work at run-time.
I have wired up the outlet in IB and am able to access the datePicker.date from within the class but not the ViewController.
I have researched and viewed a number of ways to implement this concept. My question isn't how to implement a CustomDatePicker it is "Why can't I access properties of my CustomDatePicker from the ViewController that instantiated it?
I successfully changed the datePickerMode property when I loaded the NIB like this:
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"CustomPickerView" owner:self options:nil];
self.customPickerView = (CustomPickerView *)[nibArray objectAtIndex:0];
self.customPickerView.datePicker.datePickerMode = UIDatePickerModeTime;
self.dateField.inputView = self.customPickerView;
}
And you can probably remove your custom view's init method altogether. I've never seen a NIB loaded in the init method like you're doing. While it might be possible, I believe that's what is causing your problem. Loading the NIB in viewDidLoad of the ViewController and setting it directly seems more straightforward.
Please see solution here at:
Objective-C Custom Class Actions and Outlets

No Visible View in Custom Table View Cell

I am doing an exercise about tableView and tableViewCell. And I am having some problems with custom view cells.
I have created my own custom tableViewCell with .xib file called as newCellView.xib. I have required .h and .m files and I choose super class as UITableViewCell.
In my view controller called as TableViewController I was creating a table with default cells like this.
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text = [showNames objectAtIndex:[indexPath row]];
cell.imageView.image = [UIImage imageNamed:[iconArray objectAtIndex:[indexPath row]]];
return cell;
}
Once I have created my own custom view. I imported newViewCell.h into my viewController and I updated the code like this.
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *newCellViewString = #"newCellView";
newCellView *cell = [tableView dequeueReusableCellWithIdentifier:newCellViewString];
//newCellView *cell = [[newCellView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:newCellView];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"newCellView" owner:self options:nil];
cell = (newCellView *)[nib objectAtIndex:0];
}
But when I run the app I see a blank page, like I have no related view. Maybe I forgot some connections to add. I can't even see empty cells to view. Only a white blank page. Any help would be great. Thanks.
EDIT FOR OTHER FILES
Here are my .h and .m files.
#import <UIKit/UIKit.h>
#interface newCellView : UITableViewCell <UITableViewDelegate>
#property (nonatomic, strong) IBOutlet UILabel *nameLabel;
#property (nonatomic, strong) IBOutlet UIImageView *iconImageView;
#property (retain, nonatomic) IBOutlet UIButton *extendButton;
#end
.m file
#import "newCellView.h"
#implementation newCellView
#synthesize nameLabel;
#synthesize extendButton;
#synthesize iconImageView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)dealloc {
[extendButton release];
[nameLabel release];
[iconImageView release];
[super dealloc];
}
#end
When dealing with custom cell, I'm always trying to encapsulate everything related to the cell in a UITableViewCell subclass (including the NIB / outlets / ..) and use the tableView registerClass: forCellReuseIdentifier: method to tell my table which class to use for its cells.
In your example to do so you could:
In your newCellView.m, add the nib loading in the cell init:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"newCellView" owner:self options:nil];
self = [nib objectAtIndex:0];
}
return self;
}
Make sure all Outlets connections are correct. (i.e. your UITableViewCell in your Nib is of class NewCellView,..)
Then in the viewDidLoad of your controller you tell your table to use newCellView for its cells:
[yourTableView registerClass:[newCellView class] forCellReuseIdentifier:#"newCellView"];
Finally in the cellForRowAtIndexpath:
newCellView *cell = [tableView dequeueReusableCellWithIdentifier:#"newCellView"];
if (cell == nil)
{
cell = [[NewCellView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"newCellView]";
}
I think, the problem with your question is that the custom cells are not attached to the custom class cell.
When you create a UITableViewCell class, it does not create a xib along with it.
Then you need to create a xib file, that needs to be attached to the custom class file.
Create an EMPTY NIB file, with no content. Then add a uitablecustomcell through the objects,
and when you do add the new object, GO TO File Inspector and in File Name enter the name newCellView.
Now the custom cells will display EMPTY rows.
Now, add several views to the custom cell and attach those views via IBOutlets created in .h files, namely nameLabel, iconImageView, extendButton.
This is a simple error I have encountered before. You forgot to typecast. it should be:
cell = (newCellView*)[nib objectAtIndex:0];
If this does not resolve your issue make sure you have your xib file set to use the "newCellView" class and not the default "UITableViewCell" class.
Also, if you created a tableview manually and added it as a subview of another view or set it as your view in the loadView method or similar, rather than subclassing UITableViewController make sure you set the frame for the tableview and that you added it as a subview.
You may want to remove the from your newCellView.h class. The delegate for the tableview should not be a view or a subclass of one. Especially not the cell that the tableview will be presenting. The UITableViewController should be receiving the delegate methods.
I found the answer. Everything was working good in my code except the positioning of the cell view's.
Project was using autolayout as a default property. So whenever i see the white page actually cells were out of bounds. I disabled autolayout and set movement properties of the items from size inspector.
Thanks for efforts.

Instantiating a nib object to present in a view

I have a LegendViewController that shows a legend. Originally, I just plopped an UIImageView in there. However now, we need a few legends and I want to reuse the LegendViewController. So I created a new initializer:
- (id)initWithView:(UIView *)view withNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
[self initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self.view addSubview:view];
}
return self;
}
So now this works assuming I pass in a UIView object. For one of my legends, I was wondering if I could load a .xib into my view controller without an image or a UIView object. I have a very simple legend where I just want some color coded squares (UIViews with colors), and some text (UILabels). I can create this in a standalone .xib file, but I wasn't sure how I could load it into my LegendViewController. I've got so far as:
UINib *nib = [UINib nibWithNibName:#"HealthLegend" bundle:nil];
where HealthLegend is my standalone .xib file with my data. Or can this not be done and I need to either create an image in some drawing program, or draw the code manually in drawRect? Thanks.
You can load a nib like this
NSArray *topLevelObjects = [[NSBundle bundleForClass:[self class]] loadNibNamed:#"HealthLegend"
owner:nil
options:nil];
// Assuming you only have one root object in your nib
[self.view addSubview:[topLevelObjects objectAtIndex:0];
Change the owner to the appropriate object depending on what you set the File's Owner to in the nib (if that's even required - sounds like you have a static view so it may not be)
From the reference guide
When you create an UINib object using the contents of a nib file, the
object loads the object graph in the referenced nib file, but it does
not yet unarchive it. To unarchive all of the nib data and thus truly
instantiate the nib your application calls the
instantiateWithOwner:options: method on the UINib object
UINib object can provide a significant performance improvement
So I guess you want something like:
UINib *nib = [UINib nibWithNibName:#"HealthLegend" bundle:nil];
NSArray *views = [nib instantiateWithOwner:nil options:nil];
UIView *view = [views objectAtIndex:0];

Customise UITableView Subclass Cell?

I have made my custom cell in Interface Builder and created a custom UITableViewCell class for it, but when its loaded in no changes are made to it. I have this code in the custom class:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Initialization code
//Get the wedding date and display it
myLabel.text = #"Hello";
}
return self;
}
myLabel has been declared in the header, has a property and has been linked in Interface builder, but when I run the app and see my table, I don't get my 'Hello' text. Any ideas?
Thanks.
EDIT:
I am not using a nib file, I have it in a UITableViewController within my Storyboard. Plus, here is the cellForRowAtIndexPath code below, I simply have an array filled with the required cell identifiers and then creates said arrays:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier;
for (int cell = 0; cell <= [tableCellsArray count]; cell++)
{
if ([indexPath row] == cell)
{
CellIdentifier = [tableCellsArray objectAtIndex:cell];
}
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
return cell;
}
How do you create your cell ? Do you load it from a Nib or you allocate it ?
If you allocate it, you need to properly initialize your instance fieds (e.g. myLabel).
If you load the cell from a nib, i fear
(id) initWithCoder:(NSCoder *)aDecoder
is the init method you want to override.
Anyway, you could just put a breakpoint to make sure you pass through there and print myLabel to make sure it's correctly initialized.
-- Edit
If you want to have a customized cell view using a nib, you cannot initialize your cell view using initWithStyle:reuseIdentifier: method.
Using initWithStyle:reuseIdentifier: means you will initialize all your view by your own implementation (e.g. you will allocate, initialize and configure all you views, it won't use the xib file)
If you want to use a xib file, you need to use loadNibNamed:owner:options: method in NSBundle.
Typically, i've seen two implementations :
[[NSBundle mainBundle] loadNibNamed:#"" owner:self options:nil];
In this implementation, you load your xib giving the UIViewController subclass as an owner. In the xib, make sure the File Owner class is your UIViewController subclass. Then just link the cell with a property of your UIViewController subclass and calling loadNibNamed:owner:options: will just allocate and initialize a new cell view which will be available in the property of you UIViewController subclass. This is what the Apple Documentation advise you to do.
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"" owner:nil options:nil];
UITableViewCell myCustomCell = [nibObjects objectAtIndex:0];
In this implementation, you don't pass any owner to the xib file and get the object you want. Just makes sure your xib file only has one object which is the cell view. I prefer this approach as i think it's "weird" to have a property for cell creation but you need to make sure that the content of your xib file will keep your custom cell view at the 0 index.
Whatever method you pick, remember that loading a xib will call
initWithCoder:
instead of
initWithStyle:reuseIdentifier:
Finally, i advise you to go take a look at Aadhira's link to the Apple documentation which explain with exemple how to use custom UITableCellView with a xib file.
Go though the Apple documentation , which describes about customizing UITableViewCell.
In the above page, pl. see Listing 5-5 Loading a cell from a nib file and assigning it content, which describes clearly on customizing cell with nib file.

Is it correct to initialise a UITableViewCell using initWithCoder?

I am loading a UITableViewCell using,
[[NSBundle mainBundle] loadNibNamed:#"BlogCell" owner:self options:nil];
cell = blogCell;
self.blogCell = nil;
where blogCell is an outlet to BlogCell.xib
The actual UITableViewCell is of type BlogCell which is a subclass of UITableViewCell.
In my BlogCell class I am using
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
//initialisation of some cell properties here
return self;
}
Is this correct? Is this the way you would tackle initialisation of a cell loaded from a nib?
Thanks
That, or in awakeFromNib.