1.20. Displaying Long Lines of Text with UITextView

Problem

You want to display multiple lines of text in your UI inside one scrollable view.

Solution

Use the UITextView class.

Discussion

The UITextView class can display multiple lines of text and contain scrollable content, meaning that if the contents run off the boundaries of the text view, the text view’s internal components allow the user to scroll the text up and down to see different parts of the text. An example of a text view in an iOS app is the Notes app on the iPhone (Figure 1-54).

The Notes app on the iPhone uses a text view to render text

Figure 1-54. The Notes app on the iPhone uses a text view to render text

Let’s create a text view and see how it works. We start off by declaring the text view in our view controller’s implementation file:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) UITextView *myTextView;
@end

@implementation ViewController

Now it’s time to create the text view itself. We will make the text view as big as the view controller’s view:

- (void)viewDidLoad{
    [super viewDidLoad];

    self.myTextView = [[UITextView alloc] initWithFrame:self.view.bounds];
    self.myTextView.text = @"Some text here...";
    self.myTextView.contentInset = UIEdgeInsetsMake(10.0f, 0.0f, 0.0f, 0.0f);
    self.myTextView.font = [UIFont systemFontOfSize:16.0f];
    [self.view addSubview:self.myTextView];

}

Now let’s run the app in iOS Simulator and see how it looks (Figure 1-55).

A text view consuming the entire boundary of the screen

Figure 1-55. A text view consuming the entire boundary of the screen

If you tap on the text field, you will notice a keyboard pop up from the bottom of the screen, concealing almost half the entire area of the text view. That means if the user starts typing text and gets to the middle of the text view, the rest of the text that she types will not be visible to her (Figure 1-56).

Keyboard concealing half the size of a text view

Figure 1-56. Keyboard concealing half the size of a text view

To remedy this, we have to listen for certain notifications:

UIKeyboardWillShowNotification

Gets sent by the system whenever the keyboard is brought up on the screen for any component, be it a text field, a text view, etc.

UIKeyboardDidShowNotification

Gets sent by the system when the keyboard has already been displayed.

UIKeyboardWillHideNotification

Gets sent by the system when the keyboard is about to hide.

UIKeyboardDidHideNotification

Gets sent by the system when the keyboard is now fully hidden.

Note

The keyboard notifications contain a dictionary, accessible through the userInfo property, that specifies the boundaries of the keyboard on the screen. This property is of type NSDictionary. One of the keys in this dictionary is UIKeyboardFrameEndUserInfoKey, which contains an object of type NSValue that itself contains the rectangular boundaries of the keyboard when it is fully shown. This rectangular area is denoted with a CGRect.

So our strategy is to find out when the keyboard is getting displayed and then somehow resize our text view. For this, we will use the contentInset property of UITextView to specify the margins of contents in the text view from top, left, bottom, and right:

- (void) handleKeyboardDidShow:(NSNotification *)paramNotification{

    /* Get the frame of the keyboard */
    NSValue *keyboardRectAsObject =
    [[paramNotification userInfo]
     objectForKey:UIKeyboardFrameEndUserInfoKey];

    /* Place it in a CGRect */
    CGRect keyboardRect = CGRectZero;

    [keyboardRectAsObject getValue:&keyboardRect];

    /* Give a bottom margin to our text view that makes it
     reach to the top of the keyboard */
    self.myTextView.contentInset =
    UIEdgeInsetsMake(0.0f,
                     0.0f,
                     keyboardRect.size.height,
                     0.0f);
}

- (void) handleKeyboardWillHide:(NSNotification *)paramNotification{
    /* Make the text view as big as the whole view again */
    self.myTextView.contentInset = UIEdgeInsetsZero;
}

- (void) viewWillAppear:(BOOL)paramAnimated{
    [super viewWillAppear:paramAnimated];

    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(handleKeyboardDidShow:)
     name:UIKeyboardDidShowNotification
     object:nil];

    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(handleKeyboardWillHide:)
     name:UIKeyboardWillHideNotification
     object:nil];

    self.myTextView = [[UITextView alloc] initWithFrame:self.view.bounds];
    self.myTextView.text = @"Some text here...";
    self.myTextView.font = [UIFont systemFontOfSize:16.0f];
    [self.view addSubview:self.myTextView];

}

- (void) viewWillDisappear:(BOOL)paramAnimated{
    [super viewWillDisappear:paramAnimated];

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

In this code, we start looking for keyboard notifications in viewWillAppear: and we stop listening to keyboard notifications in viewWillDisappear:. Removing your view controller as the listener is important, because when your view controller is no longer displayed, you probably don’t want to receive keyboard notifications fired by any other view controller. There may be times when a view controller in the background needs to receive notifications, but these are rare, and you must normally make sure to stop listening for notifications in viewWillDisappear:. I’ve seen many programmers break their apps by not taking care of this simple logic.

Note

If you intend to change your UI structure when the keyboard gets displayed and when the keyboard is dismissed, the only method that you can rely on is to use the keyboard notifications. Delegate messages of UITextField get fired when the text field is getting edited, whether there is a soft keyboard on the screen or not. Remember, a user can have a Bluetooth keyboard connected to his iOS device and use it to edit the content of text fields and any other data entry in your apps. In the case of a Bluetooth keyboard, no soft keyboard will be displayed on the screen—and if you change your UI when your text fields start to get edited, you might unnecessarily change the UI while the Bluetooth keyboard user is editing text.

Now, if the user tries to enter some text into the text view, the keyboard will pop up, and we take the height of the keyboard and assign that value as the bottom margin of the contents inside the text view. This makes our text view’s contents smaller in size and allows the user to enter as much text as she wishes without the keyboard blocking her view.

Get iOS 7 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.