Change UITableView row height without dismissing keyboard - objective-c

I have a TextView inside a UITableViewCell. The text inside the TextView can be edited right there in place.
The TextView grows and shrinks vertically depending on the number of lines the text contains. So far I haven't found a possibility to also let the row containing the TextView grow and shrink dynamically.
[tableView reloadData];
or
[tableView reloadRowsAtIndexPaths: withRowAnimation:];
don't work, because they dismiss the keyboard on every change.
Is there a way to change height of an individual row in a UITableView without dismissing keyboard?

Yes, you can have the row heights adjust as you type. It's not documented, and it defies reason, but all you need to do is set the new value in tableView.rowHeight (or have heightForRowAtIndexPath:ready to compute the new heights), then do this:
[tableView beginUpdates]; // updates the row heights ...
[tableView endUpdates]; // ... nothing needed in between

The task is to invoke row height recomputation without cell reuse (to save current responder).
One can call moveRowAtIndexPath:toIndexPath: for the same indexPath right after height change
- (void)textInputTableView:(UITableView *)tableView cellHeightDidChangeForIndexPath:(NSIndexPath *)indexPath
{
[tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
}

After reloadRowsAtIndexPaths the cell changes memory value, so if you call becomeFirstResponder just after the reloadRowsAtIndexPaths, you are trying to set as firstResponder the old cell, which does not exist (becomeFirstResponder returns false and keyboard exits).
The solution is to call again cellForRowAtIndexPath in order to get the new memory value of the reloaded cell, and then call becomeFirstResponder to that cell.

Related

iOS animate partial cells to show full cell

I've got an UITableViewCell with a UITextField in it. When I click the UITextField I want to make sure the entire cell is visible in the tableView. I did this in textfieldDidBeginEditing, like so:
- (void) textFieldDidBeginEditing:(id)textField {
TTTableViewCell *cell = (TTTableViewCell *)[self.tableView cellForRowAtIndexPath:objc_getAssociatedObject(textField, kIndexPathId)];
[self.tableView setContentOffset:CGPointMake(cell.frame.origin.x, cell.frame.origin.y - self.tableView.sectionHeaderHeight) animated:YES];
}
This works perfectly when the user goes down the list. Expect when to user taps a cell which is partial visible on the top it doesn't reveal the entire cell just moves a bit.
Does someone know a solution to this problem?
There are UITableView methods to solve exactly this. You can use scrollToRowAtIndexPath:atScrollPosition:animated: to ask the table view to bring a cell to the top by passing UITableViewScrollPositionTop.
(You might first want to check if it's already visible to ensure it doesn't scroll unnecessarily.)

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.

Can I add a disclosure Button to only one cell in my UITableView?

I have a UITableView and I want to add a disclosure button to a cell but only when the cell is selected.
How can i do it?
Thanks
in tableView:didSelectRowAtIndexPath: save the indexPath to a member variable.
in tableview:cellForRowAtIndexPath: check, if the indexPath is the saved one and set cell.accessoryType
Do it in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//bla bla bla
}
Remember to have a variable to remove the button from the previous selected cell
In the table view cell set the accessoryType or accessoryView property. You will want to do this in the -tableView:didSelectRowAtIndexPath: delegate method.
Doing it in didSelect means that the disclosureButton only appears after the user releases the selected cell. If you want ti to appear when the cell is highlighted, you need to subclass UITableViewCell and override setSelected: or setHighlighted:
However, the whole point of a disclosureButton is that the button can be pressed and does something different then simply selecting the cell. For that there is the disclosure indicator. The whole premise of making either of these show up only when the cell is highlighted/selected doesn't seem to serve any good UI design purpose in my opinion...but maybe for what you want it does make sense.

UITableView - Color a selectede cell in table (remain colored)

In my app i'm using a UITableViewController ("grouped style")
which in one of its section I want the user to be able to see what he had selected by making this cell colored and other "uncolored".
Doing it by updating all cells' background color and reloading table data, each time user touches a cell (in didSelectRowAtIndexPath:)
Problem is that there is some processing made in the didSelectRowAtIndexPath: so the color doesn't get changed right a way, rather in a bit delay after touch was made.
(I gusse the processing is the resone for the tiny delay)
Is there a better way of doing it?
Any help will be appreciated
Liron
P.S. I'm new to all of this...
In order to do this you need to override one of UITableViewCell's methods. You can subclass UITableViewCell and override a method like so:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
[super setHighlighted:highlighted animated:animated];
// Custom drawing code here
}
This gets fired as soon as a touch is made on a table view cell. If you would like to now have any default coloring on the cell make sure to do the following:
cell.selectionStyle = UITableViewCellSelectionStyleNone;