Trying to expand/collapse UITableViewCell from a UIButton on the custom cell - iphone

I have a UITableView populated with custom UITableViewCells. Within those custom cells, I have a UITextField and a "See More" UIButton. The purpose of the UIButton is to dynamically expand that particular UITableCell when the user wishes to read more of the text. In the same way, when the user wishes to return to the original size, the user clicks the Button again, and the UITableViewCell will shrink to the original size.
Since the cell isn't being selected, I setup an IBAction within the Custom Cell like such:
//Within CustomCell.m
- (IBAction)showMoreText:(id)sender
{
//instance bool variable to flag whether the cell has been resized
self.hasBeenResized = YES;
//turn off mask to bounds, otherwise cell doesnt seem to resize
[[self.cellView layer] setMasksToBounds:NO];
// Calculate the new sizes and positions for the textView and the button
CGRect newTextViewFrame = self.textView.frame;
newTextViewFrame.size.height = self.textView.contentSize.height;
self.textView.frame = newTextViewFrame;
CGFloat bottomYPos = self.textView.frame.origin.y + self.textView.frame.size.height;
CGRect buttonFrame = self.showMoreButton.frame;
buttonFrame.origin.y = bottomYPos;
self.showMoreButton.frame = buttonFrame;
// Call begin and end updates
[(UITableView*) self.superview beginUpdates];
[(UITableView*) self.superview endUpdates];
// Set mask and put rounded corners on the cell
[[self.cellView layer] setMasksToBounds:YES];
[[self.cellView layer] setCornerRadius:10.0];
}
Following this, I have this in my ViewController class:
// Within ViewController.m
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"heightForRowAtIndexPath");
CustomCell *cell = (CustomCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
if([cell hasBeenResized] == NO)
{
return cell.frame.size.height + 20;
}
else
{
return cell.frame.size.height + cell.textView.frame.origin.y + cell.textView.frame.size.height + cell.showMoreButton.frame.size.height + 20;
}
}
What happens now is I can see the custom cell change the size of its textview, however, the table does not update the row height for that particular cell. Checking on the If-else statement there, it appears that hasBeenResized is always false, even though I set it to YES within the IBACtion of the CustomCell.
I have looked at other solutions here, but they all seem to involve didSelectRowAtIndexPath, which I cannot use in this instance (I have another behavior for the cell when it is selected).
Am I doing this completely wrong? Ideally, what I would like to do is to have the "Show More" button animate downwards as the textview is expanded and vice versa when it's collapsed.
Thank you!

Method beginUpdates won't call reloadData for you - you have to do it manually.
For your case it would be best to call:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
And place your showMoreText code in tableView:cellForRowAtIndexPath: method (for selected cell only)

To Update your tableView, you have to reload it.
[tableView reloadData];

Related

Within cellForRowAtIndexPath getting height of cell when cells are variable height

I create custom cells within my tableview some have images and are tall some are just text. The height of the cells are calculated in heightForRowAtIndexPath, which I beleive is done before cellForRowAtIndexPath is called. I want to place an imageview at the bottom of the cell regardless of heigh, but I am not sure how to get the calculated height from within cellForRowAtIndexPath?
Too late for an answer..
But, like #user216661 pointed out, the problem with taking the height of the Cell or the ContentView is that it returns the cells original height. Incase of rows with Variable height, this is an issue.
A better solution is to get the Rect of the Cell (rectForRowAtIndexPath) and then get the Height from it.
- (UITableViewCell *)tableView:(UITableView *)iTableView cellForRowAtIndexPath:(NSIndexPath *)iIndexPath {
UITableViewCell *aCell = (UITableViewCell *)[iTableView dequeueReusableCellWithIdentifier:aCellIdentifier];
if (aCell == nil) {
CGFloat aHeight = [iTableView rectForRowAtIndexPath:iIndexPath].size.height;
// Use Height as per your requirement.
}
return aCell;
}
You can ask the delegate, but you'll be asking twice since the tableView already asks and sizes the cell accordingly. It's better to find out from the cell itself...
// in cellForRowAtIndexPath:, deque or create UITableViewCell *cell
// this makes the call to heightForRow... and sizes the cell
CGFloat cellHeight = cell.contentView.bounds.size.height;
// alter the imageView y position (assuming the rest of the frame is correct)
CGRect imageFrame = myImageView.frame;
imageFrame.y = cellHeight - imageFrame.size.height; // place the bottom edge against the cell bottom
myImageView.frame = imageFrame;
You are allowed to call heightForRowAtIndexPath yourself! Just pass the indexPath from cellForRowAtIndexPath as an argument and you can know the height of the cell you are setting up.
Assuming you are using a UITableViewController, just use this inside cellForRowAtIndexPath...
float height = [self heightForRowAtIndexPath:indexPath]

Events inside UITableViewCell not working below y value

I'm having some trouble getting my buttons to work below a certain y value in my table cells. I'm using a custom UITableViewCell class named "RowWhiskyContent". The default height is 44px and it's below that point my events don't seem to trigger anymore. The button displays just fine and so does everything else below that point, the event however don't seem to trigger. If i place my button half way (like at y=35) only the top part of the button triggers the event and the bottom part doesn't do a thing.
Here's the code trimmed down to the esentials:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
if(![self createView:&cell])
{
UIImage *bottle = [UIImage imageNamed:#"icon_add.png"]; //image size: 22x22
UIButton *bottleButton = [[UIButton alloc] initWithFrame:CGRectMake(60, 70, bottle.size.width, bottle.size.height)];
[bottleButton setImage:bottle forState:UIControlStateNormal];
[cell.contentView addSubview:bottleButton];
[bottleButton addTarget:self action:#selector(addToCollection:) forControlEvents:UIControlEventTouchUpInside];
cell.contentView.frame = CGRectMake(cell.contentView.frame.origin.x, cell.contentView.frame.origin.y, cell.contentView.frame.size.width, 160);
//cell.frame = cell.contentView.frame; // Tried this, didn't work.
//[tableView reloadData]; // Tried this too, didn't work either.
}
return cell;
}
// Check if cell exists and create the cell if it doesn't.
-(BOOL) createView: (UITableViewCell**) cell
{
BOOL cellExists = YES;
*cell = (RowWhiskyContent *) [myTableView dequeueReusableCellWithIdentifier:#"ContentIdentifier"];
if(*cell == nil)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"RowWhiskyContent" owner:self options:nil];
*cell = [topLevelObjects objectAtIndex:0];
cellExists = NO;
}
return cellExists;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
return 160;
}
Since I'm setting the height of the cell and the contentView both at 160 I'm not sure what's going wrong here. Reloading the data didn't work and neither did setting the cell.frame.
Could anybody please tell me what I'm doing wrong?
Thanks in advance.
EDIT:
Added a screenshot:
Red button works fine but if I place it at the position of the green button it stops working. The contentview's background is set to purple so that explains the purple area. When clicking the cell it triggers the didSelectRowAtIndexPath so I'm guessing the the cell itself is also big enough.
This is definitely a content size issue. Your button will display outside of the frame similar to overflow in CSS, however they will not respond to events. So whatever UIView is containing your UIButton you need to make sure that it's content/frame/bounds are all set tall enough. You can also use [cell.contentView sizeToFit] to adjust it automatically to it's content.
You should definitely NOT reload data inside of the protocol methods for your UITableView.

UITextView in a UITableViewCell smooth auto-resize shows and hides keyboard on iPad, but works on iPhone

I have implemented a custom UITableViewCell which includes a UITextView that auto-resizes as the user types, similar to the "Notes" field in the Contacts app. It is working properly on my iPhone, but when I am testing it in the iPad, I am getting some very strange behavior: When you get to the end of a line, the keyboard hides for a millisecond and then shows itself again immediately. I would write it off as just a quirky bug, but it actually causes some data loss since if you are typing, it loses a character or two. Here's my code:
The Code
// returns the proper height/size for the UITextView based on the string it contains.
// If no string, it assumes a space so that it will always have one line.
- (CGSize)textViewSize:(UITextView*)textView {
float fudgeFactor = 16.0;
CGSize tallerSize = CGSizeMake(textView.frame.size.width-fudgeFactor, kMaxFieldHeight);
NSString *testString = #" ";
if ([textView.text length] > 0) {
testString = textView.text;
}
CGSize stringSize = [testString sizeWithFont:textView.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
return stringSize;
}
// based on the proper text view size, sets the UITextView's frame
- (void) setTextViewSize:(UITextView*)textView {
CGSize stringSize = [self textViewSize:textView];
if (stringSize.height != textView.frame.size.height) {
[textView setFrame:CGRectMake(textView.frame.origin.x,
textView.frame.origin.y,
textView.frame.size.width,
stringSize.height+10)]; // +10 to allow for the space above the text itself
}
}
// as per: https://stackoverflow.com/questions/3749746/uitextview-in-a-uitableviewcell-smooth-auto-resize
- (void)textViewDidChange:(UITextView *)textView {
[self setTextViewSize:textView]; // set proper text view size
UIView *contentView = textView.superview;
// (1) the padding above and below the UITextView should each be 6px, so UITextView's
// height + 12 should equal the height of the UITableViewCell
// (2) if they are not equal, then update the height of the UITableViewCell
if ((textView.frame.size.height + 12.0f) != contentView.frame.size.height) {
[myTableView beginUpdates];
[myTableView endUpdates];
[contentView setFrame:CGRectMake(0,
0,
contentView.frame.size.width,
(textView.frame.size.height+12.0f))];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int height;
UITextView *textView = myTextView;
[self setTextViewSize:textView];
height = textView.frame.size.height + 12;
if (height < 44) { // minimum height of 44
height = 44;
[textView setFrame:CGRectMake(textView.frame.origin.x,
textView.frame.origin.y,
textView.frame.size.width,
44-12)];
}
return (CGFloat)height;
}
The Problems
So, here's what's happening
This code is working 100% properly on my iPhone and in the iPhone simulator. As I type the text, the UITextView grows smoothly, and the UITableViewCell along with it.
On the iPad simulator, however, it gets screwy. It works fine while you are typing on the first line, but when you get to the end of a line, the keyboard disappears and then reappears immediately, so that if the user continues typing the app misses a character or two.
Here are some additional notes on the weird behaviors that I have noticed which may help explain it:
Also, I have found that removing the lines [myTableView beginUpdates]; [myTableView endUpdates]; in the function textViewDidChange:(UITextView *)textView makes the UITextView grow properly and also doesn't show and hide the keyboard, but unfortunately, then the UITableViewCell doesn't grow to the proper height.
UPDATE: Following these instructions, I am now able to stop the strange movement of the text; but the keyboard is still hiding and showing, which is very strange.
Does anyone have any ideas as to how to get the keyboard to continually show, rather than hide and show when you get to the end of the line on the iPad?
P.S.: I am not interested in using ThreeTwenty.
you should return NO in:
-(BOOL) textViewShouldEndEditing:(UITextView *)textView
if you would like to show keyboard at all times. You should handle cases, which keyboard should be hidden, by returning YES to this delegate function.
edit:
I dug a little more, when [tableView endUpdates] called, it basically does 3 things :
Disables user interaction on the tableView
Updates cell changes
Enables user interaction on the tableView
The difference between SDKs(platforms) is at [UIView setUserInteractionEnabled] method. As UITableView does not overrite setUserInteractionEnabled method, it is called from super (UIView).
iPhone when setUserInteractionEnabled called, looks for a private field _shouldResignFirstResponderWithInteractionDisabled which returns NO as default, so does not resign the first responder (UITextView)
But on iPad there is no such check AFAIK, so it resignes UITextView on step 1, and sets focus and makes it first responder on step 3
Basically, textViewShouldEndEditing, which allows you to keep focus, according to SDK docs, is your only option ATM.
This method is called when the text
view is asked to resign the first
responder status. This might occur
when the user tries to change the
editing focus to another control.
Before the focus actually changes,
however, the text view calls this
method to give your delegate a chance
to decide whether it should.
I had the same issue for an iPad app and came up with another solution without having calculating the height of the text itself.
First create a custom UITableViewCell in IB with an UITextField placed in the cell's contentView. It's important to set the text view's scrollEnabled to NO and the autoresizingMask to flexibleWidth and flexibleHeight.
In the ViewController implement the text view's delegate method -textViewDidChanged: as followed, where textHeight is a instance variable with type CGFloat and -tableViewNeedsToUpdateHeight is a custom method we will define in the next step.
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat newTextHeight = [textView contentSize].height;
if (newTextHeight != textHeight)
{
textHeight = newTextHeight;
[self tableViewNeedsToUpdateHeight];
}
}
The method -tableViewNeedsToUpdateHeight calls the table view's beginUpdates and endUpdates, so the table view itself will call the -tableView:heightForRowAtIndexPath: delegate method.
- (void)tableViewNeedsToUpdateHeight
{
BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
[table beginUpdates];
[table endUpdates];
[UIView setAnimationsEnabled:animationsEnabled];
}
In the table view's -tableView:heightForRowAtIndexPath: delegate method we need to calculate the new height for the text view's cell based on the textHeight.
First we need to resize the text view cells height to the maximum available height (after subtracting the height of all other cells in the table view). Then we check if the textHeight is bigger than the calculated height.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat heightForRow = 44.0;
if ([indexPath row] == kRowWithTextViewEmbedded)
{
CGFloat tableViewHeight = [tableView bounds].size.height;
heightForRow = tableViewHeight - ((kYourTableViewsNumberOfRows - 1) * heightForRow);
if (heightForRow < textHeight)
{
heightForRow = textHeight;
}
}
return heightForRow;
}
For a better user experience set the table view's content insets for bottom to e.g. 50.0.
I've tested it on the iPad with iOS 4.2.1 and works as expected.
Florian

How to make a UITableViewCell with a UITextView inside, that dynamically adjust its height, on the basis of the UITextView?

I would like to have a tablew view with a behaviour similar to the iPhone Contacts app by Apple: a uitableviewcell with a uitextview inside, so that when I write in the uitextview, the uitextview increases its height, and so accordingly the uitableviewcell dynamically adjusts its height. I searched over the whole web, finding only partial solutions and lack of sample code!
please help me I am desperate
Tony
Looking at this,you need to be somewhat tricky. You need to calculate the height of the textView dynamically and based on the Height of the TextView,you need to return the Height for the cell..
It's very easy & somewhat Tricky..
This is the code by which you can calculate the size of string....
First get the size of String
NSString *label = #"Sample String to get the Size for the textView Will definitely work ";
CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:CGSizeMake(320, 9999)
lineBreakMode:UILineBreakModeWordWrap];
over here ....
NSLog(#"%f",stringSize.height);
Secondly dynamically create the textView in the cell..giving the stringSize.height
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//}
NSDictionary *d=(NSDictionary *)[self.menuArray objectAtIndex:indexPath.section];
NSString *string = [d valueForKey:#"Description"];
CGSize stringSize = [string sizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:UILineBreakModeWordWrap];
UITextView *textV=[[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height+10)];
textV.font = [UIFont systemFontOfSize:15.0];
textV.text=string;
textV.textColor=[UIColor blackColor];
textV.editable=NO;
[cell.contentView addSubview:textV];
[textV release];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *d=(NSDictionary *)[self.menuArray objectAtIndex:indexPath.section];
NSString *label = [d valueForKey:#"Description"];
CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:CGSizeMake(320, 9999)
lineBreakMode:UILineBreakModeWordWrap];
return stringSize.height+25;
}
After giving so much pain to my fingers,......I think this is enough code...& will surely help to solve your problem..
Good Luck
Create a custom UITableViewCell and add your UITextView to the cell's contentView.
In LayoutSubviews, set textView.frame to the cell's contentView.bounds (or do some other custom layout).
When the textView contents change (discovered via UITextViewDelegate), do two things:
1) call [tableView beginUpdates]; [tableView endUpdates]; This will force the table view to recalculate the height for all cells (will call tableView:heightForRowAtIndexPath:). If your cell is the delegate for the textView then you'll have to figure out how to get a pointer to the tableView, but there are a few ways to achieve that. Or you could make the delegate your view controller...
2) when tableView:heightForRowAtIndexPath: is called for this cell, return the textView.contentSize.height. You can get your cell from here by calling [tableView cellForRowAtIndexPath]; Or if you just have one of these cells then cache a pointer to it in your viewController.
The only issue I found with the accepted answer was that we are allocating the UITextView each and every time. I found that this raised issues with typing into the view and having the text updating immediately and also keeping the view as first responder. When I tried to reload the cell with the new height it would then try to add a new textView.
Because of this I found a slightly different method to achieve the same goal. Hopefully this different take might help people who are struggling to implement the above code.
1) In the header file define a variable for the height of the text and the textView:
UITextView * _textView;
NSInteger _textHeight;
Setting a variable means that we can load the view to be a certain height if we are loading text into the textView and also reduces the complexity.
2) Load the text view and add it to our cell
_textView = [[UITextView alloc] init];
_textView.delegate = self;
_textView.returnKeyType = UIReturnKeyDone;
_textView.font = [UIFont fontWithName:#"Helvetica" size:16];
if (![_user metaStringForKey:bBioKey].length) {
_textView.text = #"Placeholder text";
_textView.textColor = [UIColor lightGrayColor];
}
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath == 0) { // Add logic to choose the correct cell
if (_textView.superview != cell) {
[cell addSubview:_textView];
_textView.keepTopInset.equal = KeepRequired(7);
_textView.keepBottomInset.equal = KeepRequired(7);
_textView.keepRightInset.equal = KeepRequired(10);
_textView.keepLeftInset.equal = KeepRequired(70);
}
}
}
}
Using keeplayout has enabled us to keep our textfield to always stay the same height as the cell. We are also only ever adding our UITextView once.
3) Add the code to calculate the height of the text
- (NSInteger)getHeightOfBio: (NSString *)text {
UILabel * gettingSizeLabel = [[UILabel alloc] init];
gettingSizeLabel.font = [UIFont fontWithName:#"Helvetica" size:16];
gettingSizeLabel.text = text;
gettingSizeLabel.numberOfLines = 0;
CGSize maximumLabelSize = CGSizeMake(240, 9999); // this width will be as per your requirement
CGSize expectedSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];
return expectedSize.height;
}
I have tried many and found this to work the best
4) Add some logic in the cell height to make use of this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) { // Set the height for our changing textView
return 30 + [self getHeightOfBio:_textView.text];
}
return 44;
}
Obvious we need a bit more height than our text height so I added an extra cushion amount which can be experimented with.
5) Refresh the view each time a character is typed to check if we need to increase the size:
- (void)textViewDidChange:(UITextView *)textView {
if ([self getHeightOfText:textView.text] != _textHeight) {
_textHeight = [self getHeightOfText:textView.text];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
}
In this section we get the height of the text each time the user types.
Then though we use our stored value and compare the current value to the stored value. Obviously if they are the same then there is no point in refreshing the view. If they are different we update the value and then refresh our table.
This bit I found a good answer on stackOverflow showing how we can refresh only the heights of the table instead of the cell itself. Why refresh the cell when we don't need to? This means that once this is called the cell height is updated and it increases nicely.
Anyway I found this worked really nicely and was simple enough that it can be put together or have different parts taken and put into other peoples pieces of code.
Props to the accepted answer which was pillaged for various pieces along the way but I also hope that this answer helps some people who are having the same difficulties that I had.
Finally I got it working. The main problem is how to get cell's contentView correct width. Hardcoded width does not work for all cases since it may vary accordingly plain/grouped table style, added accessories, or landscape/portrait layout. The only way to get 100% correct width is to ask it from cell object. So I create cell right in heightForRowAtIndexPath and store it in cache, then this cached cell will be returned by cellForRowAtIndexPath method.
Another problem is how to force cell to layout its subviews before it is used. It can be done if we temporary add cell to the tableView and update cell's frame with tabelView's width. After that all subviews will be layouted in the right way. Here is how I got it working in my TableKit library.
I've written up my learnings and solution for calculating the UITableViewCell height based on an inner UITextView on my blog. The post contains the code that works for universal apps, both table view styles and autorotation.
I edit TomSwift response which is the best way to do it :
here is my cell code (in swift)
class CommentaireTextViewCell: UITableViewCell,UITextViewDelegate {
#IBOutlet weak var textViewCom: UITextView!
weak var parentTableView:UITableView?
#IBOutlet weak var constraintTextViewTopMargin: NSLayoutConstraint!
#IBOutlet weak var constraintTextViewHeight: NSLayoutConstraint!
#IBOutlet weak var constraintTextViewBottomMargin: NSLayoutConstraint!
override func awakeFromNib() {
self.textViewCom.delegate = self;
}
func textViewDidChange(textView: UITextView) {
self.parentTableView?.beginUpdates();
self.parentTableView?.endUpdates();
}
func getHeight() -> CGFloat
{
constraintTextViewHeight.constant = self.textViewCom.contentSize.height;
// cell height = textview marge top+ textview height+ textView Marge bottom
return constraintTextViewTopMargin.constant+constraintTextViewHeight.constant+constraintTextViewBottomMargin.constant+8
// add 8 because it seems to have an inset inside the textView
}
override func setSelected(selected: Bool, animated: Bool) {
self.textViewCom.becomeFirstResponder();
}
}
for the explaination, the cell table view is a weak propertie of the cell so I can tell it to update layout when user enter text.
The height of the cell is the sum of its top constraint to the contentView, its bottom constraint to the contentView and the textView.contantSize.height which is also equal to the textView constant height
You need to return the correct height in the heightForRowAtIndexPath delegate method.
Try the following code:
CGSize constraintSize;
constraintSize.height = MAXFLOAT;
constraintSize.width = yourTextView.frame.size.width;
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"yourFontName" size:yourFontSize], NSFontAttributeName,
nil];
CGRect frame = [yourTextView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize stringSize = frame.size;//The string size can be set to the UITableViewCell
you can get the UITextView size programmatically.According to the size,set the height of the cell using the following delegate
tableView:heightForRowAtIndexPath:
Guys this is really cool stuff but you can use tableview:didSelectRowForIndexPath: to add the UITextView to the cell as it's subview when the user taps it, works very well since you need to use only 1 UITextView which you can reuse and won't interfere with the tableview receiving touches! Release it when done.
Details? just ask!

How can a create a automatically resizing table cell for entering texts like in the iPhone's "mail" app?

I'm try to emulated something just like the "new message" page in Apple's mail app on the iphone. I've implemented it with a tableview and I've successfully gotten the "To", "CC", and "Subject" rows to behave correctly, but I'm not sure how to implement the actual message portion of the page.
There are several issues that I'm having. I'm currently trying to implement it by placing a UITextView in the cell (I turn off the scroll bars on the text view). I have the text view resize itself when it is changed, by modifying its frame to the new height of the content. The first problem is that I also need to do this for the cell height itself. Since heightForRowAtIndexPath seems to only get called when the row is first loaded, I can't modify the height there. I suppose I could call reload data on the table but this seems like it would be really inefficient to do on the whole table every time text is entered. What is the best way to get the table cell to autoresize as the user types? I've found lots of examples on how to do it on lone table views and how to resize table cells at initialization but I can't find any that let you do both at the same time.
Finally, I would like the bottom border of the table cell to be invisible. If you look at the mail app, you'll notice there is no line at the bottom of the message space, implying that you can just keep typing. I always have one in my table view (even when I add a footer) and I can't figure out how to get rid of it. (Perhaps should I make my message body be the footer itself?)
I would recommend using a UIScrollView yourself instead of a UITableView. UITableView isn't really built to support such a thing.
Mail.app doesn't seem to use UITableView.
It looks like there custom items (labels and text fields) with UITextView on bottom.
You could try my answer to a question similar to this...the key is to use
[self.tableView beginUpdates];
[self.tableView endUpdates];
To do this without reloading the data.
First off, of course, you're going to want to create your UITextView and add it to your cell's contentView. I created an instance variable of UITextView called "cellTextView" Here is the code that I used:
- (UITableViewCell *)tableView:(UITableView *)tableView fileNameCellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (!cellTextView) {
cellTextView = [[UITextView alloc] initWithFrame:CGRectMake(5.0, 5.0, cell.bounds.size.width - 30.0, cell.bounds.size.height - 10.0)]; // I use these x and y values plus the height value for padding purposes.
}
[cellTextView setBackgroundColor:[UIColor clearColor]];
[cellTextView setScrollEnabled:FALSE];
[cellTextView setFont:[UIFont boldSystemFontOfSize:13.0]];
[cellTextView setDelegate:self];
[cellTextView setTextColor:[UIColor blackColor]];
[cellTextView setContentInset:UIEdgeInsetsZero];
[cell.contentView addSubview:cellTextView];
return cell;
}
Then, create an int variable called numberOfLines and set the variable to 1 in your init method. Afterwards, in your textViewDelegate's textViewDidChange method, use this code:
- (void)textViewDidChange:(UITextView *)textView
{
numberOfLines = (textView.contentSize.height / textView.font.lineHeight) - 1;
float height = 44.0;
height += (textView.font.lineHeight * (numberOfLines - 1));
CGRect textViewFrame = [textView frame];
textViewFrame.size.height = height - 10.0; //The 10 value is to retrieve the same height padding I inputed earlier when I initialized the UITextView
[textView setFrame:textViewFrame];
[self.tableView beginUpdates];
[self.tableView endUpdates];
[cellTextView setContentInset:UIEdgeInsetsZero];
}
Finally, paste this code into your heightForRowAtIndexPath method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float height = 44.0;
if (cellTextView) {
height += (cellTextView.font.lineHeight * (numberOfLines - 1));
}
return height;
}