Every time you create a form you’re gonna probably have some UITextField in it. When you have a lot of them, it’s very commom to have the UITextField hidden behind the Keyboard. I’ve searched all over for a solution to the problem of textfields hidden behind the keyboard and found that the solution is to use a UIScrollView. It’s possible to scroll some items over the keyboard and animate them so it goes up as smooth as Apple’s own applications
You can solve the problem by implementing UIKeyboardDidShowNotificationand UIKeyboardDidHideNotification. These are the main notification events a keyboard will fire. There is also UIKeyboardWillHideNotification that can be used for our purpose.
The problem with his solution is that the field only pops when you actually start typing, not when you touch/click the UITextField. This one was kinda easy to solve, just using scrollRectToVisible, but it still put the UITextField right over the keyboard.
Creating Base App
From XCode menu choose New Project, IPhone OS Application and finally View Base Application. Let’s name it sample-scrollview. Under Classes, there should be a file named sample_scrollviewViewController.h, open it and create three IBOutlets we’re gonna need to demonstrate this sample.
01
|
// sample_scrollviewViewController.h
|
02
|
@interface sample_scrollviewViewController : UIViewController {
|
03
|
IBOutlet UIScrollView *scrollview;
|
04
|
IBOutlet UITextField *tx1;
|
05
|
IBOutlet UITextField *tx2;
|
06
|
}
|
07
|
08
|
@property(nonatomic,retain) IBOutlet UIScrollView *scrollview;
|
09
|
@property(nonatomic,retain) IBOutlet UITextField *tx1;
|
10
|
@property(nonatomic,retain) IBOutlet UITextField *tx2;
|
And then add the synthesize directive in the implementation file:
1
|
// sample_scrollviewViewController.m
|
2
|
@implementation sample_scrollviewViewController
|
3
|
4
|
@synthesize scrollview,tx1,tx2;
|
Create Interface Using Interface Builder
Next we’re gonna create the User Interface using XCode’s Interface Builder. Browse the project looking for sample_scrollviewViewController.xib inside the Resources folder.
Double-click sample_scrollviewViewController.xib to open interface builder. From the Object Library drag a Scroll View (UIScrollView) into the main view, it should resize appropriately when you drop it over the existing view. Now drag two UITextFields over theUIScrollView. Now it’s time to connect the pieces on the Interface Builder to the IBOutlets inside your viewcontroller. Now right-click the File’s Owner Icon and associate the IBOutlets. You should associate the scrollview with the UIScrollView object, and tx1 and tx2 with the respective UITextFields on the screen. Then you should drag the Referencing Outlets to the TextFields and choose the delegate. Now you may close Interface Builder as we’re not gonna need it again for now.
Implementing Notifications for Keyboard Activity
Now open up again the implementation file and let’s code what we need to receive notifications from keyboard activity. First of all let’s define some constants that we’re gonna need to determine the size of the content. It’s important to note, that if you define a content height taller than the iphone screen you’ll and up with scrolling on the screen when the keyboard is hidden. Our objective is to keep the scroll of when the keyboard is hidden. Next to the top of the implementation file, right before the import statement, let’s define the following:
1
|
#define SCROLLVIEW_CONTENT_HEIGHT 460
|
2
|
#define SCROLLVIEW_CONTENT_WIDTH 320
|
3
|
4
|
#import "sample_scrollviewViewController.h"
|
Then we need to implement the methods to listen to the notification of keyboard activity. But first, let’s get back to the header file and create a variable to keep state information about the visibility of the keyboard. You can put it right after the declaration of the IBOutlets.
1
|
IBOutlet UITextField *tx2;
|
2
|
3
|
BOOL keyboardVisible;
|
Let’s implement code to handle notifications from keyboard. These methods will allow us to receive the notifications sent from the events.
01
|
- (void) viewWillAppear:(BOOL)animated {
|
02
|
[super viewWillAppear:animated];
|
03
|
NSLog(@"Registering for keyboard events");
|
04
|
05
|
// Register for the events
|
06
|
[[NSNotificationCenter defaultCenter]
|
07
|
addObserver:self
|
08
|
selector:@selector (keyboardDidShow:)
|
09
|
name: UIKeyboardDidShowNotification
|
10
|
object:nil];
|
11
|
[[NSNotificationCenter defaultCenter]
|
12
|
addObserver:self
|
13
|
selector:@selector (keyboardDidHide:)
|
14
|
name: UIKeyboardDidHideNotification
|
15
|
object:nil];
|
16
|
17
|
// Setup content size
|
18
|
scrollview.contentSize = CGSizeMake(SCROLLVIEW_CONTENT_WIDTH,
|
19
|
SCROLLVIEW_CONTENT_HEIGHT);
|
20
|
21
|
//Initially the keyboard is hidden
|
22
|
keyboardVisible = NO;
|
23
|
}
|
24
|
25
|
-(void) viewWillDisappear:(BOOL)animated {
|
26
|
NSLog (@"Unregister for keyboard events");
|
27
|
[[NSNotificationCenter defaultCenter]
|
28
|
removeObserver:self];
|
29
|
}
|
Then implement the code to handle these notifications, but first we need to create another state variable, this one, offset, will hold the distance of the content from the top of the scroll, we are gonna need it to restore the scroller to its original position. Just put it right after the keyboardVisible.
1
|
BOOL keyboardVisible;
|
2
|
CGPoint offset;
|
Now the code for the keyboardDidShow, put it in the implementation file.
01
|
-(void) keyboardDidShow: (NSNotification *)notif {
|
02
|
NSLog(@"Keyboard is visible");
|
03
|
// If keyboard is visible, return
|
04
|
if (keyboardVisible) {
|
05
|
NSLog(@"Keyboard is already visible. Ignore notification.");
|
06
|
return;
|
07
|
}
|
08
|
09
|
// Get the size of the keyboard.
|
10
|
NSDictionary* info = [notif userInfo];
|
11
|
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
|
12
|
CGSize keyboardSize = [aValue CGRectValue].size;
|
13
|
14
|
// Save the current location so we can restore
|
15
|
// when keyboard is dismissed
|
16
|
offset = scrollview.contentOffset;
|
17
|
18
|
// Resize the scroll view to make room for the keyboard
|
19
|
CGRect viewFrame = scrollview.frame;
|
20
|
viewFrame.size.height -= keyboardSize.height;
|
21
|
scrollview.frame = viewFrame;
|
22
|
23
|
CGRect textFieldRect = [tx1 frame];
|
24
|
textFieldRect.origin.y += 10;
|
25
|
[scrollview scrollRectToVisible:textFieldRect animated:YES];
|
26
|
27
|
NSLog(@"ao fim");
|
28
|
// Keyboard is now visible
|
29
|
keyboardVisible = YES;
|
30
|
}
|
At this point, if you run the application, you can see that the textfield you clicked (tx1) moved over the keyboard. Explaining a little bit the code above, on line 4, we check if the keyboard is already visible, it may happen if for example you click in another TextField. Later, on lines 9-12 we get the size of the keyboard. On line 16 we save the original position (offset) of the content frame, so as to restore it later when the keyboard hides. On lines 18-21 we resize the UIScrollView frame, subtracting the size of the keyboard. Lines 23-25 get the position of the first UITextField and tell the UIScrollView to scroll until it’s visible, before, on line 24 we add 10 points as a margin from the UITextField to the Keyboard. At last on line 29 we configure the keyboard state variable as visible.
Implementing the Keyboard Handling Events
01
|
-(void) keyboardDidHide: (NSNotification *)notif {
|
02
|
// Is the keyboard already shown
|
03
|
if (!keyboardVisible) {
|
04
|
NSLog(@"Keyboard is already hidden. Ignore notification.");
|
05
|
return;
|
06
|
}
|
07
|
08
|
// Reset the frame scroll view to its original value
|
09
|
scrollview.frame = CGRectMake(0, 0, SCROLLVIEW_CONTENT_WIDTH, SCROLLVIEW_CONTENT_HEIGHT);
|
10
|
11
|
// Reset the scrollview to previous location
|
12
|
scrollview.contentOffset = offset;
|
13
|
14
|
// Keyboard is no longer visible
|
15
|
keyboardVisible = NO;
|
16
|
17
|
}
|
But again, if you run the code right now, you will notice that the keyboard does not close, and that you cannot see the same effect on the second UITextField. Now we’re gonna fix that. Just add another variable to hold the state of which textfield is active, put it in your header file, which at last will look like the following:
1
|
@interface sample_scrollviewViewController : UIViewController {
|
2
|
IBOutlet UIScrollView *scrollview;
|
3
|
IBOutlet UITextField *tx1;
|
4
|
IBOutlet UITextField *tx2;
|
5
|
6
|
BOOL keyboardVisible;
|
7
|
CGPoint offset;
|
8
|
UITextField *activeField;
|
9
|
}
|
Making the Keyboard Close
Now we need to implement UITextField method delegates to make the keyboard close and set the active UITextField. The first code snippet, sets the current active UITextField:
1
|
-(BOOL) textFieldShouldBeginEditing:(UITextField*)textField {
|
2
|
activeField = textField;
|
3
|
return YES;
|
4
|
}
|
The second one, makes the keyboard close
1
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
2
|
{
|
3
|
[textField resignFirstResponder];
|
4
|
return YES;
|
5
|
}
|
Also we need to change the implementation method of keyboardDidShow, and change the line that recover the frame of the UITextField to the following:
1
|
scrollview.frame = viewFrame;
|
2
|
3
|
CGRect textFieldRect = [activeField frame];
|
4
|
textFieldRect.origin.y += 10;
|
As you can see it’s very simple to achieve good results, if you want to go further, you should calculate the middle of the visible area, so as to have a broader margin from the keyboard, this will mimic even better Apple Behavior in their applications.
No comments:
Post a Comment