access text in cell.textLabel.text - objective-c

This seems like a simple thing but I'm stuck.
In my storyboard, I have a cell with text in it's label (using Static Cells). In didSelectRowAtIndexPath, I'm trying to access the cell's textLabel's text. Here's my code, with NSLogging:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.textLabel.text) {
NSLog(#"cell.textLabel.text = %#",cell.textLabel.text);
} else {
NSLog(#"!cell.textLabel.text");
}
NSLog(#"just for fun: cell.textLabel.text = %#", cell.textLabel.text);
The if statement always returns "!cell.textLabel.text" and the last NSLog is always (null) although the storyboard's cell has text in it.
Is cell.textLabel not correct? Should I be looking at another subview of UITableViewCell?

The default cell style of table cells in the Storyboard are of type 'Custom'. Which gives you a blank cell where you can add other types of controls to it.
If you dragged a label onto the cell and changed its text, then you are likely using a Custom style of UITableViewCell.
The property textLabel is only available on cells of types other than Custom, such as Basic, or Detailed.
Therefore you should first check out if these styles meet your requirements and use them instead.
If you really do require a Custom type of cell, you will need to make a subclass of UITableViewCell and create outlets to access your label.

Related

Two classes reference each other / access UITableViewCell from UITextField inside it's delegate methods?

I have a custom UITableViewCell and a custom UITextField, with the text field inside the table view cell.
I need to do some validation on text entered in the field, and display a little tick or cross image in the cell depending on if the text entered was okay.
My table view cell class imports the text field's header, and declares a .textField property which I can use.
My text field class has a class extension (#class) of my table view cell, and declares a .cell property, which I'd like to be able to use to access the cell's image, and display / update it to the tick or cross.
The text field's delegate is my view controller, and I'm using textFieldDidEndEditing in the following way to attempt to set that image (validationConfirmation). However, it's not working, nothing's happening, no error, etc.
Any idea what I'm doing wrong? This is a puzzler.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
SignUpTextField *tf = (SignUpTextField *)textField;
if (textField.text.length < 6) {
tf.cell.validationConfirmation.hidden = NO;
tf.cell.validationConfirmation.image = [UIImage imageNamed:#"cross.png"];
}
}
So here, I'm getting the text field subclass (SignUpTextField, or I'm attempting to), and trying to set its cell property's image property. Is this wrong? Messy? Bad practise?
You don't need to (and you shouldn't) have a cell reference inside textField class. I believe your textField is added as a subview of the cell. Hence in the textFieldDidEndEditing: method, you can get the cell using textField.superview. Or alternately, you can assign a tag to the textField corresponding to the tableview row that it appears in and access the cell using cellForRowAtIndexPath: method of tableview where you can use the tag to create your indexpath.

Two UITableViewCells in my XIB for iPad/iPhone

I am trying to use two differently formatted UITableViewCells depending on if it's an iPad or iPhone. Both the cells show the same information, but the iPad cell has half the height and double the width. So the information is shown on one line vs two lines.
In my cellforRowatIndexPath,
I am putting the code:
HistoryCell *cell;
if (self.isiPad) {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"historyiPadCellType" forIndexPath:indexPath];
}
else{
cell = [self.tableView dequeueReusableCellWithIdentifier:#"historyCellType" forIndexPath:indexPath];
}
However it seems like if I have 2 UItableViewCells in the same XIB, it will give me an error:
invalid nib registered for identifier (historyCellType) - nib must contain exactly one top level object which must be a UITableViewCell instance'
Is there anyway to fix this?
Thanks!
The best way to use differetnt cell for iPad and iPhone it is using 2 nibs:
HistoryCell~iphone.xib and HistoryCell~ipad.xib
And you must register you nib:
[self.tableView registerNib:[UINib nibWithNibName:#"HistoryCell" bundle:nil] forCellReuseIdentifier:#"historyCellType"];
System automatically loads xib depending on current device.

Why dequeue reusable cell twice in making custom table view cell

I am following a tutorial of making custom table view cell with storyboard. I drag a UILabel as subview of the cell and set its tag to 1. I have two questions regarding the data source code.
What's the purpose of the second dequeue statement? I know it's an init method while not using storyboard to make the custom cell.
What's the difference between tableview and self.tableview?
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
NSDictionary *dToAccess = (self.tableView==tableView)?[self.arForTable objectAtIndex:indexPath.row] : [self.arForSearch objectAtIndex:indexPath.row];
[(UILabel*)[cell viewWithTag:1] setText:[dToAccess valueForKey:#"name"]];
[(UILabel*)[cell viewWithTag:2] setText:[dToAccess valueForKey:#"value"]];
return cell;
}
For your first question, the second dequeueReusableCellWithIdentifier: looks like a mistake.
Here is how a UITableView works:
You might have 50 rows in your table, but if only 10 rows are visible at a time, you only need to make 10 cells, and then when the user scrolls, you can reuse cells that have gone offscreen instead of always releasing them and init'ing new cells that come onscreen. A UITableView keeps a list of cells that have gone offscreen and when you call dequeueReusableCellWithIdentifier:, it removes it from the list of offscreen cells and returns it to you. From here you can customize the cell for re-use (change its text, color, etc) and return it. Again, this is not an "init" method, this is returning a pre-existing cell.
So, let's look at what happens when this UITableView is first displayed -- in this example there are 10 visible cells, so the tableView will call tableView:cellForRowAtIndexPath: 10 times to get cells to display in these 10 slots. Every time this is called, you will need to initialize and return a new UITableViewCell to display. (At this point dequeueReusableCellWithIdentifier: will return nil, because you don't have any offscreen cells to re-use yet)
When a user scrolls your list, cells will begin to go offscreen, and new cells will need to appear. You don't need to make new cells, because you have already created as many as will need to be onscreen at a time. You should call dequeueReusableCellWithIdentifier: to get a reference to a cell that has gone offscreen, which you can then re-use.
I would alter your code like this:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: CellIdentifier] autorelease];
}
Now you are checking for reusable cells before creating new ones.
For your second question,
In your example, tableView refers to the tableView that was passed in (see the "tableView" in your method signature). Separately, if your class has defined a property called tableView, then self.tableView will call the getter for this property.
When apple developed the UITableView for the first iphone they had a problem in performance when scrolling through it. Then one clever engineer discovered that the cause of this was that allocation of objects comes with a price, so he came up with a way to reuse cells.
dequeueReusableCellWithIdentifier method is used to returns a cell if it has been marked as ready for reuse.
So Whenever there are many number of rows in a table view and you are going to scroll it, then the cells which are just passed away from your previous screen before scrolling are get reused instead of creating new one.
And to know the ans of your second que. I think you should refer this link :
http://www.iphonedevsdk.com/forum/iphone-sdk-development/17669-when-use-self-objectname-just-objectname.html
To dequeue twice is not necessary, this block of code is broken.

UITableViewCell text filed disappear data on scrolling the tableview

I have a UITableView with custom cells, those cells contain some textFields. Here when I enter some data in textFields and I scroll the table view data it disappears, I think because every time it's creating new cell.
I resolved this issue by using an array and inserting every cell in that, but here I am not able to reuse cells, so we are wasting memory.
Can you tell me prefect way how I should handle this behavior?
Use an array to store the value of the every text field and set the value of the desired text field in the
- (UITableViewCell *)tableView:(UITableView *)tTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method
It sounds as though it has something to do with how you are creating cells in the tableView:cellForRowAtIndexPath: method. But without seeing your implementation I can only make general suggestions (add your implementation to your question so people can be a bit more specific with their answers).
If you are worried about memory then use the UITableView's inbuilt cell reuse feature by creating your cells in the following way:
NSString *identifier = #"reuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = #"text relating to index path";
The above will check the tableView to see if there are any available cells for reuse. If there are none then nil will be returned. This is where you create a cell using the initWithStyle:reuseIdentifier: method which marks the cell as being suitable for reuse if it is scrolled out of view. This means that you will only ever instantiate, at maximum, the total number of cells that are visible at once, which keeps your table's memory footprint nice and low.
Note that we set the cell's textField.text outside of the nil check - this is to ensure that if we have retrieved a reusable cell we will overwrite its old text content with the text content relevant to the indexPath being passed into the method.

Why can't I define IBOutlets when using custom "prototype tableviewcells"

I have my own table view cell which is defined in my storyboard. I have also defined a custom UITableViewCell class for this special cell. So when I want to create an Outlet for my custom prototype cell I get an error that the Outlet cant be created.
Since this is not possible I have to do some ugly workarounds and use the tags in IB to reference the individual labels and buttons later on in my code.
I don't really see why this is not possible and I wonder if working with tags and [myCell viewWithTag:] is the best possible way to go here?
Because the outlet is a one-to-one connection between your controller and a specific item within the view. In the case of a prototype cell, it is simply a description of a cell that can have an arbitrary number of different items (i.e. rows in your table view). How would the controller know which item you are referring to (e.g. row 5 or 500)? That is why you are receiving the error message.
Lucas provided one method to refer to your connection via tags which works perfectly well.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentInIB"];
UIImageView *img = (UIImageView*) [cell.contentView viewWithTag:1];
//img.image = ...
//Access you prototype cell here to alter its style, example:
[[cell layer] setCornerRadius:10];
[cell setClipsToBounds:YES];
return cell;}
I assume you are using dynamic prototypes - in the attribute inspector of the tableview in the storyboard there is an option to select "static cells" or "dynamic prototypes". You can do what you are trying to do if you select "static cells" as there is only one cell in your tableview at run time for each cell in the storyboard. Using this approach you will only be able to use the cells you create in storyboard i.e. you will not be able to select the number of cells in your code.