Custom UITableViewCell problem loading from NIB - iphone

UPDATE: I've solved this now - see my own comment. Amazing how often writing out the problem can lead to the resolution when you go through it again!
I must have read every thread on SO and the examples on Apple, but I just can't get this to work in my case and I wonder what I'm doing wrong?
I have made a custom cell in IB with XCode 4. The file's owner is set to the class of the custom view controller for the table and I have an IBOutlet for the UITableView cell which is hooked up OK from what I can tell. I normally do all my table cells via code and use the fast scrolling method, but for this project I really need to use nibs to load the cell content and I'm having some grief I can't figure out.
So in my interface file I have;
#interface myCustomTableViewController <various delegates> {
UITableViewCell *customNibCell;
...
}
#property (nonatomic,assign) IBOutlet UITableViewCell *customNibCell;
In the customTableViewController I do this in my cellForRowAtIndexPath;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#:"customDirCell"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"DirectoryCellWithMenu" owner:self options: nil];
cell = customNibCell;
self.customNibCell = nil;
}
[self configureCell:cell atRow:indexPath.row];
And my configureCell method does this to reach into the cell and pull out the things I wish to customise.
UIImageView *fileImageView = (UIImageView *)[cell viewWithTag:3];
UILabel *headingTextLabel = (UILabel *)[cell viewWithTag:4];
UILabel *modifiedTextLabel = (UILabel *)[cell viewWithTag:5];
UILabel *sizeTextLabel = (UILabel *)[cell viewWithTag:6];
The Problem
The cells do load into the table OK in as much as I can see the correct layout. Furthermore, the custom actions I have defined on various buttons do all fire OK so it seems that the cell is hooked up properly.
BUT none of the customisation works - I can't seem to change the text of any of the labels for example and on investigation, the result of every [cell viewWithTag:nn] is nil which clearly signposts the problem, but I can't see what the cause is.
The identifier for the
UITableViewCell is set to
"customDirCell"
The object identities for the labels
etc are indeed 3,4,5,6 according to
IB
The xib contains exactly 1 object,
the UITableViewCell
I can't see anything wrong with either the nib file creation or the code, but I'm obviously missing something!
Can any kind soul shed light on where I should be looking?

Did you try this
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#:"customDirCell"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DirectoryCellWithMenu" owner:self options: nil];
cell =[nib objectAtIndex:0];
self.customNibCell = nil;
}

I've resolved this as per my comment.

Related

Optimize tableView scrolling

My tableView scrolls with lags if extra populated. Up to 20 cells go well, but above - it starts lagging while scrolling. Please, suggest an implementation with a better scrolling result. Here is the way I did it:
I have defined a custom UITableViewCell class.
The cell has 4 labels and an imageView (each outlet is a synthesized property):
I have placed a tableView in my viewController, and populated it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
MyCustomCell *cell = (MyCustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
for (id currentObject in topLevelObjects)
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (MyCustomCell *) currentObject;
break;
}
}
[cell.label_descr setText:#"bla-bla-bla"];
[cell.label_date setText:#"bla-bla-bla"];
[cell.label_time setText:#"bla-bla-bla"];
[cell.label_numeric setText:#"bla-bla-bla"];
[cell.image_view setImage:[UIImage imageWithContentsOfFile:#"bla-bla-bla"]];
return cell;
}
The amount of text in each cell, as you can see, is miserable, and the image used for the UIImageView is about 25x25 .png file.
My tableView is supposed to hold more than 300 cells (don't blame on me, I have a "customer from hell").
Please, suggest a way to make the tableView scroll smoother, without (much) lags. Or an alternative way to present those "damn-over-300-cells" to my "from hell" customer.
300 thanks in advance!
P.S.: sorry if duplicated, but the solutions found didn't help at all.
EDIT:
About the image used for the imageView:
I use 2 different images only:
a "checkmark" - transaction done
and a "pending" - transaction in process
Maybe I use to define 2 imageView outlets in my tableViewCell xib, and just selecting the needed imageView, instead of setting each time the required image?
SOLUTION FOUND, thanks to everybody, especially to iNoob and max_.
In tableViewCell's xib, I have set the "checkMark" as the default image of the imageView.
When defining the cell's values, in cellForRowAtIndexPath, only if needed, I say:
if_I_should_present_a_pending_image:
[cell setPending];
to replace the "checkMark" with a "pending" image (method defined in tableViewCell class):
- (void)setPending{
self.image_view.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"pending_1.png"],
[UIImage imageNamed:#"pending_2.png"],
[UIImage imageNamed:#"pending_3.png"],
[UIImage imageNamed:#"pending_4.png"],
nil];
self.image_view.animationDuration = 2.0;
self.image_view.animationRepeatCount = 0;
[self.image_view startAnimating];
}
l
After that, the table scrolls like a charm. Thanks to everybody again. Cheers.
Don't iterate through all of the subviews: cell = [topLevelObjects objectAtIndex:0];
Load the images in the background using gcd, and store them in an NSDictionary for easy access:
Pseudo code:
If caches dict contains an object for the URL you want to load
Retrieve that image from the dict
Set it as the image
Else
Load the image using dispatch_async()
Add it to the dict
I found this article suggesting that creating the cells programatically instead of using a nib file could be up to 5-10% faster. I don't know if it's true or not, so take it with a grain of salt, but it may be worth a try.
Replace your code with following one and try it out.
For the below code :
1) Take IBOutlet of your UITableViewCell in the your controller. for below code it is myCustomTableViewCell.
MyCustomCell *customCell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"MyCustomCell"];
if(customCell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
customCell = myCustomTableViewCell;
}
Hope it will work.

How do I populate tableview using cell?

to the point, i have custom cells, inside it has 2 label and 1 textfield. both label and textfield got input from user. i also have other view that has uitableview inside it. my question is how do i populate cell in uitableview? please help.
this is my code inside tableviewcontroller.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1; // i want to populate this using 'count' but i dont know how.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier]];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = _customCell;
_customCell = nil;
}
cell.titleLabel.text = [NSString stringWithFormat:#"%#",titleTextString];
cell.timerLabel.text = [NSString stringWithFormat:#"%#",timerString];
cell.statusLabel.text = [NSString stringWithFormat:#"%#",statusString];
return cell;
}
how do i populate my tableview if i push add button after finishing input by user? Please if you dont mind help me with code. i'm beginner and im hard to understand by using opinion.
If I understood your question correctly, you did a custom nib file for your cells that has 2 UILabel in it and one UITextField, and you want to access these objects when populating your table. Here are some steps for this issue:
First, you have to give a tag number for each object in your custom cell. You find this property in the Attribute Inspector in Interface Builder. Say you gave the first label tag 1, the second label 2 and the text field 3.
Second you have to give a. Identifier for this nib file, for example MyCustomCellIdentifier. This identifier will be used later on in the view that has the table so you can link to it.
Third, also in the custom cell nib, you click on the yellow square that says File's Owner and in the Identity Inspector you change the Class to the class name that has the table that will use this custom cell.
Fourth, in the class that you have the table that will use the custom cell, create an outlet of type UITableViewCell. We will link this in the custom nib cell.
Fifth, goto the custom nib cell, click on the cell window, then in the Connections Inspector link New Referencing Outlet to the File's Owner, you will see the outlet that you created in the table class showing here, simply link to it.
Now since the connections are established thing are more easy, in the cellForRowAtIndexPath function (in the class that contains the table for sure), you have to load the custom cell from the nib file as follows:
static NSString *tableIdentifier = #"MyCustomCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TheNibClassNameOfYourCustomCell" owner:self options:nil];
if([nib count] > 0) cell = theNameOfTheOutletYouUsed;
else NSLog(#"Failed to load from nib file.");
}
Ok, your custom cell is loaded in variable cell, now you have to access every object in it from the tags you created:
UILabel *label1 = (UILabel *)[cell viewWithTag:1];
UILabel *label2 = (UILabel *)[cell viewWithTag:2];
UITextField *textField1 = (UITextField *)[cell viewWithTag:3];
Now you can access everything through label1, label2, and textField1 easily like label1.text = #"Hi";
I hope this answers your question.

Custom Table Cell Loading Data Objective C iOS -

Currently I have the following method, but it doesn't quite work...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
cell = tvCell;
self.tvCell = nil;
}
UILabel *label;
label = (UILabel *)[cell viewWithTag:5];
label.text = [NSString stringWithFormat:#"Hole #%d", indexPath.row];
return cell;
}
The table view gets created with no errors, but each individual cell contains nothing, but clearly the TVCell.xib has a label with a tag of 5. The only question I have is this. I don't quite understand these steps apple gives here...
Select File’s Owner in the nib document window, open the Identity pane of the inspector, and set the class of File’s Owner to your custom view controller class.
Connect the cell outlet of File’s Owner (now the placeholder instance of your custom subclass) to the table-view cell object in the nib-file document.
Here is where those steps are...
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
Can someone please explain those steps for a noob like me? I think that is what I messed up on, but I could have done anything wrong.
I don't think TcCell becomes a property of self. Try this instead when the cell queue is empty:
if (cell == nil) {
cell=[[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
}
It's kind of difficult to explain the steps that you said you don't understand in words. I would suggest looking for tutorials that have images or videos for that. (I did a quick search and there are lots available, but I didn't really want to choose one to direct you to without having read them more closely).
However, I prefer to create custom table view cells this way:
http://www.mobilesce.com/2011/12/27/the-best-way-to-do-custom-reusable-uitableviewcells/
It's slightly different from the way described in the apple docs, but I've seen a lot of people use it and I find it easier.
The NSBundle method loadNibNamed:owner:options: returns an NSArray containing the top level objects in your nib file. Try this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"TVCell" owner:self options:nil];
cell = [nibArray objectAtIndex:0];
}

Iphone UITableViewCell CustomCell

Attempting to implement a "Simple" a CustomCell,
I have a normal tableViewController that renders fine using the normal "default" methods,
but I need to implement a Custom cell with some UILabel's and a UIImage.
So I created the CustomCell.h, CustomCell.m, CustomCell.xib
The .H
#interface CustomCell : UITableViewCell <UITextViewDelegate>
{
IBOutlet UIImageView *image;
IBOutlet UILabel *name;
IBOutlet UILabel *date;
IBOutlet UILabel *comment;
}
#property (retain,nonatomic) IBOutlet UIImageView *image;
#property (retain,nonatomic) IBOutlet UILabel *name;
#property (retain,nonatomic) IBOutlet UILabel *date;
#property (retain,nonatomic) IBOutlet UILabel *comment;
and .M
#implementation CustomCell
#synthesize image;
#synthesize name;
#synthesize date;
#synthesize comment;
#pragma mark -
#pragma mark View lifecycle
- (id) initWithController: (Controller *) ctnlr
{
ControllerPointer = ctnlr;
return(self);
}
- (void) SetImage:(UIImageView*)Image
{
image = Image;
}
- (void) SetName:(NSString*)Name
{
[Name retain];
[name.text release];
name.text = Name;
}
- (void) SetDate:(NSString*)Date
{
[Date retain];
[date.text release];
date.text = Date;
}
- (void) SetComment:(NSString*)Comment
{
[Comment retain];
[comment.text release];
comment.text = Comment;
}
anyway, when I attempt to create these customcells in cellForRowAtIndexPath (as one would expect might be implemented) I am left with only a blank screen. So obviously I am missing something big... When I created the .XIB file with "Interface Builder" I made sure to connect the "Referencing Outlets" to the appropriate labels and images.
So following the implied logic of the way the Xcode framework appears to work,
I followed the same reasoning (for lack of an exact example) No worky...
Anyway, if there are any IPhone geeks that would like to enlighten me...
(yes, there are no "[something release]" calls, I am not even sure if anything needed to be alloc'd. Please tell me there's just a couple calls I am leaving out, it can't be too much more than something simple like this Right...?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil] lastObject];
}
NSUInteger row = [indexPath row];
SnsObject *sObj = [SnsArray objectAtIndex:row];
[cell SetName:[sObj getUserName]];
NSUInteger row = [indexPath row];
SnsObject *sObj = [SnsArray objectAtIndex:row];
cell.name = [[UILabel alloc]init];
cell.name.text = [sObj getUserName];
cell.date = [[UILabel alloc]init];
cell.date.text = [sObj getDateTime];
cell.comment = [[UILabel alloc]init];
cell.comment.text = [sObj getCommentText];
cell.image = [[UIImageView alloc]init];
cell.image.image = [sObj getImageUrl];
return(cell)
}
Thanks in Advance!
There are other issues with the code beyond what mrcrowl mentioned about now needing to "alloc-init" the outlets. In particular, this line:
cell = [[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil] lastObject];
This is not the typical idiom used to load a custom tableview cell from a .xib. First of all, you pass "owner:self", which means you want to hook up the outlet objects in the .xib file with outlet members in your tableviewcontroller object, probably not what you intended.
Second, you're relying on the order of objects returned from loadNibNamed:owner:options:, which while it may work today, may not work tomorrow, or on a new release of iOS.
Instead, the usual idiom is to declare an outlet for the entire tableviewcell in your tableviewcontroller:
(in the .h file):
...
UITableViewCell *tvCell;
...
#property (nonatomic, retain) IBOutlet UITableViewCell *tvCell;
Then in place of your line, you have this:
[[NSBundle mainBundle] loadNibNamed:#"NewsArchiveTitleTvCell" owner:self options:nil];
cell = tvCell;
self.tvCell = nil;
Normally this isn't done with subclassing, notice how I didn't declare the class as CustomCell, but as a vanilla UITableViewCell. So how to you get at those pesky subviews so you can configure them? Using tags is the normal way:
...
#define kMyKewlLabelTag 1
...
UILabel *kewlLabel = (UILabel *) [cell viewWithTag:kMyKewlLabelTag];
kewlLabel.text = [NSString stringWithFormat:#"Hi there from row %d!", indexPath.row];
...
EDIT:
edit: here's a bit more detail, comments are too short to address the "what's going on here?" question. Here's an excerpt from one of my apps that loads the UITableViewCell from a .xib:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyShoppingCartTvCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"ShoppingCartTvCell" owner:self options:nil];
cell = tvCell;
self.tvCell = nil;
}
...
// (insert code to grab model data for this row)
...
UILabel *nameLabel = (UILabel *) [cell viewWithTag:1];
nameLabel.text = itemNameStr;
UILabel *countLabel = (UILabel *) [cell viewWithTag:2];
countLabel.text = [NSString stringWithFormat:#"%d", itemCount];
UIImageView *iv = (UIImageView *) [cell viewWithTag:3];
...
Here's what's going on here:
There is no custom UITableViewCell subclass, there is only a .xib file named "ShoppingCartTvCell.xib" containing a UITableViewCell, and UI elements placed inside the UITableViewCell. UI elements whose data must change per row are assigned a unique tag (the tag field is in the CMD-1 Attributes Inspector in IB) so that your code can get a handle to those objects to change them (customize labels, images, etc). Make sure you don't use "0" since all elements by default have a 0 tag. Also, make sure the Identifier field of the UITableViewCell in CMD-1 Attributes Inspector is the CellIdentifier string.
The File's Owner of the .xib file is your table view controller where you want to display the cell. More precisely, it can be any class containing a IBOutlet UITableViewCell *tvCell; member. It is an instance of this class that you pass in as owner to loadNibNamed:owner:options:. As long as the value of the linked outlet is nil in the owner, when you call loadNibNamed:owner:options, the outlet of the owner is filled in with the object from the .xib (as long as the connection was made in the .xib in IB). Understanding that is a magic moment in Apple programming that opens whole new vistas to you :).
You must set self.tvCell = nil; to prepare for the next cellForRowAtIndexPath that needs to load from the .xib. I also sometimes set to nil before loadNibNamed:owner:options:, I think that's a bit safer actually.
Here's how you go about loading your UITableViewCells from a .xib:
In xcode, add an IBOutlet UITableViewCell *tvCell; to your UITableViewController class (including property declaration if you like)
In your xcode project, create a New File, User Interface, Empty Xib. Open this .xib in IB
In IB, drag a TableViewCell from the Library into your empty .xib file under First Responder
Click File's Owner, CMD-4 (Identify Inspector), and under Class select the class containing the IBOutlet UITableViewCell *tvCell that you added (probably your UITableViewController subclass, the class where you're manipulating your table).
Control-drag from File's owner to the UITableViewCell, and select the outlet you want to hook up. This is the field that will hold the newly-loaded-from-xib UITableViewCell when you call loadNibNamed:owner:options with an instance of File's Owner as the "owner" parameter.
Add UI elements into the UITableViewCell (make sure they're inside the UITableViewCell hierarchy). Any elements that you want to customize per-row require a unique tag value.
follow the recipe I gave above in cellForRowAtIndexPath
Have a magic moment where you start to understand how .xib files and File's Owner objects really work together, and start creating lots of cool UITableViewCells and other custom view objects in IB because it's really easy and way better than creating them in code (IMNSHO).
When you load a UITableViewCell from a .xib, you shouldn't need to create the controls manually.
For example, this kind of thing is unnecessary:
cell.name = [[UILabel alloc]init];
This will replace the label loaded from your xib with a new label that has a zero frame -- that is, the new label will be located at 0,0 and will have no width or height. Hence, no worky.
Assuming you have the xib hooked up correctly to CustomCell's IBOutlets, they controls you are seeking should already be there.
P.S. Forgive me if I am reading too much into your method name, but I don't think this line will work either, because the .image property expects a UIImage:
cell.image.image = [sObj getImageUrl];
Ok... Thanks all for the good input, but sometimes the simplest answer is not only the most eloquent, it's the best... Here's what I found to work,, keeping it as simple as possible, without changing a thing outside of one function.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for(id oneObject in nib)
{
if([oneObject isKindOfClass:[CustomCell class]])
{
cell = (CustomCell*)oneObject;
}
}
}
NSUInteger row = [indexPath row];
printf("MainMessageBoard.m cellForRowAtIndexPath = [%i]\n",row);
SnsObject *sObj = [SnsArray objectAtIndex:row];
cell.Name.text = [sObj getUserName];
cell.Date.text = [sObj getDateTime];
cell.Comment.text = [sObj getCommentText];
cell.Image.image = [self resizeImage: [self imageFromURLString: [sObj getImageUrl]] scaledToSize:CGSizeMake(32.0f, 32.0f)];
cell.CommentCount.text = [NSString stringWithFormat:#"(%d)", [sObj getCommentCount]];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return(cell);
}

iPhone - "Class" may not respond to "method"

I am building a simple Navigation-based app using tables.
I have a custom "UITableViewCell" to customize the table cell data attached below.
#interface NewsCell : UITableViewCell
{
IBOutlet UILabel *newsTitle;
}
- (void)setNewsLabel:(NSString *)title;
#end
And then in my RootViewController, I set the text of the label "newsTitle" in "cellForRowAtIndexPath" method as follows.
static NSString *MyIdentifier = #"MyIdentifier";
MyIdentifier = #"NewsCell";
NewsCell *cell = (NewsCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"NewsCell" owner:self options:nil];
cell = newsCell;
}
[cell setNewsLabel:#"hello testing"];
return cell;
When I run this, the app runs fine, but I get "NewsCell may not respond to '-setNewsLabel:'" warning.
Please help! Thank you.
In RootViewController.m, you need to `#import "NewsCell.h".
Or, stick #import "NewsCell.h" in your project's PCH (pre-compiled header) file.
The underlying issue is that the compiler only knows about methods that it has previously seen when parsing the header files (or ones in the PCH).
You're creating the nib, but not assigning it to anything. See this question to create it right.
Where does the variable newsCell come from? Is it really of type NewsCell?