Monday, November 23, 2009

Multi-line UITableViewCell using UILabel

No need for UITextViews or custom UITableViewCells. You can use standard UITableViewCellStyles and make the detailTextLabel accept multiple lines and specify its line break mode. The code would be:

static NSString *CellIdentifier = @"MyCell"; 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2
reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = @"Label';
cell.detailTextLabel.text = @"Multi-Line\nText";
cell.detailTextLabel.numberOfLines = 2;
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
You will also need to return a suitable height for the multi-line cell. A height of (44.0 + (numberOfLines - 1) * 19.0) should work fine.

Update: As Vaibhav mentions in the comments, you can use variants of sizeWithFont from the NSString UIKit Additions to get the required height. I guess sizeWithFont:forWidth:lineBreakMode is the one to use here. Thanks for your input!

Thursday, November 12, 2009

Moving UITextFields over the keyboard without a UIScrollView

I had a UIViewController with a couple of UITextFields attached to its default UIView and everything was fine. Later on, I added a couple more text fields, so I had to make sure all text fields are displayed properly when the keyboard shows up. I thought about using a UIScrollView or maybe a UITableView which scrolls naturally, but I thought I didn't have to change my controller just for that. I found these 2 posts [1, 2] on stackoverflow pretty useful, but I still had to tweak the code a little and here's what I got:

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[activeField resignFirstResponder];

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
activeField = textField;
[self setViewMovedUp:YES];
return YES;

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[activeField resignFirstResponder];
[self setViewMovedUp:NO];
activeField = nil;
return YES;

- (void)setViewMovedUp:(BOOL)movedUp {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];

CGRect viewFrame = self.view.frame;
CGRect textFieldFrame = [activeField convertRect:[activeField bounds] toView:self.view];

if (movedUp) {
viewFrame.origin.y = -textFieldFrame.origin.y/1.8;
} else {
viewFrame.origin.y = 0;
self.view.frame = viewFrame;

[UIView commitAnimations];

Let me explain a couple of things here, first: activeField is a UIControl* instance variable I added to my UIVeiwController. second: you can replace (textFieldFrame.origin.y/1.8) with any function of (textFieldFrame.origin.y) that would do the job. Maybe you can try assigning certain offsets for each range of y values, but I preferred this neat form and the result was neat too.