IPhone Storyboard custom cell disappears when scrolling down - iphone

I have just finished a project where I use a custom cell for my TableView in Storyboard.
The problem is that when i scroll down the content in each cell is gone, which in my case is
two Labels.
This is the code I use to present each cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCell";
MessageCell *cell = (MessageCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Message *messageForRow = (Message *)[messages objectAtIndex:[indexPath row]];
[cell.messageLabel setText:[messageForRow message]];
[cell.senderLabel setText:[messageForRow sender]];
return cell;
}
I have specified the right cellidentifier in storyboard and linked the Class to my
custom cell class.
What can be wrong? If there is any needed information I have missed to present, please
tell me.
Best regard
Robert

Could you check by logging that this method is called when scrolling?

I solved it by myself, the problem did not lie within the update method.
By mistake I used the weak signature for the properties in my Message class.
#property (nonatomic, weak) NSString *sender;
I solved it by using the strong signature instead.
#property (nonatomic, strong) NSString *sender;
This was a great lesson because I didn't fully understand the concept
of strong and weak.

Related

How to control a customized UITableViewCell within a UITableView Implemented within a UIViewController?

In my app I wish to have a UIViewController that will hold a UITableView in it. In this UITableView I wish to have a customized UITableViewCell (i.e. I wish to define my own elements in this CELL - image, labels and buttons - as seen in the image below). And... I want to create them in the Storyboard.
Now, setting the elements in the Storyboard is easy.
I understand how to connect the UITableView and set it in the UIViewController (including the delegates in the .h file and using the basic table delegate methods).
What I'm not clear about, is how to connect and control the customized UITableViewCell and its outlets. Can I create the Outlets and Actions within the UIViewController .h and .m files? Do I need to create a separated UITableViewCell.h/.m files and call on them in the cellForRowAtIndexPath method?
Can anyone suggest what's the best approach for my needs?
UPDATE:
Here is the code I used in cellForRowAtIndexPath while using the separated MyCell.h/m file option. This code is written in the ViewController.m file, where the UITableView is implemented.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ContentCell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//MyCell is the Objective-C Class I created to manage the table cells attributes.
//#"ContentCell" is what I had entered in the Storyboard>>UITableViewCell as the Cell Identifier.
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//Here is the place I'm not clear about - Am I supposed to init the cell in a different way? If so, how?
}
cell.contentNameLabel.text = [self.dataArray objectAtIndex: [indexPath row]];
// this is the test I had done to check if the new "MyCell" object is actually getting what I would expect it to get. well... the text gets updated in the relevant label, so i guess it gets it
return cell;
}
When running the app, using the debugger break point, I can see that the code always skips the "if (cell == nil)" and never enters the code where the new MyCell object supposes to be allocated and initiated. Any idea what may I be doing wrong?
Correct, create separate UITableViewCell.h/.m files to match your custom UITableViewCell class and call on them in your cellForRowAtIndexPath method.
In your storyboard, set the class of your custom UITableViewCell to your custom class (e.g. CustomTableCell).
Your custom UITableViewCell class would contain IBOutlets which you would wire up in your storyboard, here is an example:
CustomTableCell.h:
#import "CustomStuff.h" // A custom data class, for this example
#interface CustomTableCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
- (void)configureForCustomStuff:(CustomStuff *)stuff;
#end
CustomTableCell.m:
#import "CustomTableCell.h"
#implementation CustomTableCell
#synthesize titleLabel;
#pragma mark - Configure the table view cell
- (void)configureForCustomStuff:(CustomStuff *)stuff
{
// Set your outlets here, e.g.
self.titleLabel.text = stuff.title;
}
#end
Then, in your cellForRowAtIndexPath method, configure your cell:
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomCellID"];
// Your custom data here
CustomStuff *customStuff = <YOUR CUSTOM STUFF>
[cell configureForCustomStuff:customStuff];

Using UITableViewCell in a UITableView

I'm a little confused with something. I'm trying to create a custom cell and I want to use the interface builder way.
The normal way I create a table is to have the table as:
.h
#interface AssessList : UIViewController {
IBOutlet UITableView *tblAssessList;
}
#property(nonatomic, retain) UITableView *tblAssessList;
#end
.m
- (NSInteger)
numberOfSectionsInTableView:(UITableView *)tableView {
return groupArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return totalArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = #"I am the text....";
return cell;
}
Now i've created a new class for the cell and I think I understand how to put that in. But can I leave the .h as
#interface AssessList : UIViewController
or does the class/nib with the full table on it have to be a UITableViewController?
Tom
does the class/nib with the full table on it have to be a
UITableViewController?
No. A UITableViewController is just a convenience UIViewController subclass which has a UITableView and is already setup as its delegate/datasource (it is declared as conforming to the UITableViewDelegate and UITableViewDatasource protocols), it also has pre-filled method implementations for these protocols in the template implementation file which Xcode generates for you. You can just as well do all of this yourself, I often do.
You should however make an IBOutlet for your UITableViewCell so that you can load it from the nib file (see the Loading Custom Table-View Cells From Nib Files in the Table View Programming Guide).
If you want to do in the Interface Builder Way, then create an xib (view xib). Drag and drop a UITableViewCell object from the obj palette. Customize it as you wish. In the tableView:cellForRowAtIndexPath: method, do this:
UITableViewCell * aCell = [tableview dequeueReusableCellWithIdentifier:#"SomeIdentifier"];
if (aCell == nil)
{
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:#"CustomCellNibName" owner:self options:nil];
for (NSObject *anObj in arr) {
if([anObj isKindOfClass:[UITableViewCell class]]) {
aCell = (UITableViewCell *)anObj;
}
}
}
The identifier for the tableviewcell can be set in the IB.
I guess it should be sub class of UItableViewCell
i.e.
#interface AssessList : UITableViewCell
When you want a custom tableview cell you will also need a subclass of UITableViewCell..
A tutorial can be found on this blog
Keep in mind that quite a few things can be done without creating a custom cell, this includes adding the switch to make your tableview look like the one from settings.app, to the way the iPod displays songs.
In the assessList Class you are using the custom cell created in otherviewController (UITableViewCell subclass) so there is no need to change this line
#interface AssessList : UIViewController
Note:- the otherviewController should be a subclass of UITableViewCell

Reusable TableViewCell in Interface Builder WITH changeable labels?

How do I make a reusable TableViewCell in Interface Builder with changeable labels?
Is this even possible? From what I understand apple has been giving custom TableViewCell in Interface Builder some love lately, so this should be possible?
Ps. I know there are a lot of questions with answers about TableViewCell in IB, but I couldn't find anyone that made labels work.
You can change anything in a cell that is being re-used. To customize labels that you create in IB, you should set their tags in IB itself & fetch the label using the same tag.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[MyCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
//Do anything here with the labels. Even add or remove them.
(UILabel*) label1 = (UILabel*)[cell viewWithTag:1];
return cell;
}
I used to do it he same way as in accepted answer, but I've always felt using tags like I'm using "go to" in Pascal. Feels dirty. But maybe it's just me, tags work just fine.
There's an alternative way though. Subclass a UITableViewCell, create an IBOutlet property, connect in IB, and reference your property in cellForRowAtIndexPath: code. Like so:
interface MyCustomCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *myAwesomeLabel;
#end
Don't forget to set you cell's class to MyCustomCell in IB.
After that you can connect your property in IB directly like so
And in your Table View Data Source now you can access this property
#import "MyCustomCell.h"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell) {
cell.myAwesomeLabel.text = #"Hello, World!";
}
return cell;
}
Using tags is error-prone and might turn into a mess very quickly if you use a lot of them.

Using CustomCell on tableView, how can I get didSelectRowAtIndexPath called?

I'm populating a UITableView with CustomCells and I'm trying to get didSelectRowAtIndexPath called. Here is the header for the Custom Cell.
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell {
IBOutlet UILabel *bizNameLabel;
IBOutlet UILabel *addressLabel;
IBOutlet UILabel *mileageLabel;
IBOutlet UIImageView *bizImage;
}
#property (nonatomic, retain) UILabel *bizNameLabel;
#property (nonatomic, retain) UILabel *addressLabel;
#property (nonatomic, retain) UILabel *mileageLabel;
#property (nonatomic, retain) UIImageView *bizImage;
#end
Pretty simple and straightforward. I have a detailDisclosureButton I'm adding to the cell as well in the cellForRowAtIndexPath method in the cell as well, and the method accessoryButtonTappedForRowWithIndexPath: is being called, but didSelectRowAtIndexPath is not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellView"
owner:self options:nil];
#ifdef __IPHONE_2_1
cell = (CustomCell *)[nib objectAtIndex:0];
#else
cell = (CustomCell *)[nib objectAtIndex:1];
#endif
}
// Configure the cell.
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
/*
CODE TO POPULATE INFORMATION IN CUSTOM CELLS HERE
*/
#ifdef __IPHONE_3_0
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
#endif
return cell;
}
I put an NSLog inside all the methods as well as break points. The method I'm trying to get called is not, but inside my CustomCell class, the following method is. So is there a way to get didSelectRowAtIndexPath to get called while using a CustomCell?
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
Custom cell or not should not make any difference. Are you in editing mode? If you are, you have to set allowsSelectionDuringEditing = true on the tableView.
if you are use Tableview in your xib..so u want to give tableview's data source and delegate connection in file owner..
Since you say that tableView:accessoryButtonTappedForRowWithIndexPath: is getting called, we know that your tableView's delegate property is properly set. So tableView:didSelectRowAtIndexPath: should be getting called as well. If it isn't getting called, my best guess is that you have a typo somewhere in the method signature. Copy and paste this method signature into your code to make sure you didn't accidentally omit the "tableView:" part or make a capitalization error.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
EDIT: Secondary hypothesis: In the .xib where you've defined your custom cell, make sure "User Interaction Enabled" is checked.
check if you have a UITapGestureRecognizer set for myTableView's parent view ..that is probably over riding the touch event and consuming it.

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);
}