I'm currently working on an iPhone application with a single view, which has multiple UITextFields for input. When the keyboard shows, it overlays the bottom textfields. So I added the corresponding textFieldDidBeginEditing: method, to move the view up, which works great:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if ( ( textField != inputAmount ) && ( textField != inputAge ) ) {
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y -= kOFFSET_FOR_KEYBOARD;
frame.size.height += kOFFSET_FOR_KEYBOARD;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
}
}
This method checks, if the source of the message is one of the textfields that are visible when the keyboard shows, and if not, it moves the view up.
I also added the textFieldDidEndEnditing: method, which moves the view down again (and updates some model objects according to the changed input):
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) {
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y += kOFFSET_FOR_KEYBOARD;
frame.size.height -= kOFFSET_FOR_KEYBOARD;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
}
// Additional Code
}
However, this solution has a simple flaw: When I finish editing one of the "hidden" textfields and touch another textfield, the keyboard vanishes, the view moves down, the view moves up again and the keyboard reappears.
Is there any possibility to keep the keyboard from vanishing and reappearing between two edits (of the "hidden" textfields - so that the view only moves when the selected textfield changes from one that would be hidden by the keyboard to one that would not be hidden)?
This solution is based on ComSubVie's one.
Advantages:
It supports device rotation - works for all orientations;
It doesn't hardcode the values for animation duration and curve, it reads them from the keyboard notification;
It utilizes UIKeyboardWillShowNotification instead of UIKeyboardDidShowNotification to sync keyboard animation and custom actions;
It doesn't use the deprecated UIKeyboardBoundsUserInfoKey;
It handles keyboard resize due to pressing the International key;
Fixed memory leak by unregistering for keyboard events;
All keyboard handling code is encapsulated in a separate class - KBKeyboardHandler;
Flexibility - KBKeyboardHandler class may be easy extended / modified to better suit specific needs;
Limitations:
Works for iOS 4 and above, it needs small modifications to support older versions;
It works for applications with a single UIWindow. If you use multiple UIWindows, you may need to modify retrieveFrameFromNotification: method.
Usage:
Include KBKeyboardHandler.h, KBKeyboardHandler.m and KBKeyboardHandlerDelegate.h in your project. Implement the KBKeyboardHandlerDelegate protocol in your view controller - it consists of a single method, which will be called when keyboard is shown, hidden or its size is changed. Instantiate the KBKeyboardHandler and set its delegate (typically self). See sample MyViewController below.
KBKeyboardHandler.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol KBKeyboardHandlerDelegate;
#interface KBKeyboardHandler : NSObject
- (id)init;
// Put 'weak' instead of 'assign' if you use ARC
#property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate;
#property(nonatomic) CGRect frame;
#end
KBKeyboardHandler.m:
#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"
#implementation KBKeyboardHandler
- (id)init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#synthesize delegate;
#synthesize frame;
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect oldFrame = self.frame;
[self retrieveFrameFromNotification:notification];
if (oldFrame.size.height != self.frame.size.height)
{
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.frame.size.height > 0.0)
{
[self retrieveFrameFromNotification:notification];
CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
self.frame = CGRectZero;
}
- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
CGRect keyboardRect;
[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}
- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
UIViewAnimationOptions curve;
[[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
NSTimeInterval duration;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
void (^action)(void) = ^{
[self.delegate keyboardSizeChanged:delta];
};
[UIView animateWithDuration:duration
delay:0.0
options:curve
animations:action
completion:nil];
}
#end
KBKeyboardHandlerDelegate.h:
#protocol KBKeyboardHandlerDelegate
- (void)keyboardSizeChanged:(CGSize)delta;
#end
Sample MyViewController.h:
#interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
#end
Sample MyViewController.m:
#implementation MyViewController
{
KBKeyboardHandler *keyboard;
}
- (void)dealloc
{
keyboard.delegate = nil;
[keyboard release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
keyboard = [[KBKeyboardHandler alloc] init];
keyboard.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
keyboard.delegate = nil;
[keyboard release];
keyboard = nil;
}
- (void)keyboardSizeChanged:(CGSize)delta
{
// Resize / reposition your views here. All actions performed here
// will appear animated.
// delta is the difference between the previous size of the keyboard
// and the new one.
// For instance when the keyboard is shown,
// delta may has width=768, height=264,
// when the keyboard is hidden: width=-768, height=-264.
// Use keyboard.frame.size to get the real keyboard size.
// Sample:
CGRect frame = self.view.frame;
frame.size.height -= delta.height;
self.view.frame = frame;
}
UPDATE: Fixed iOS 7 warning, thanks #weienv.
I just solved this problem. The solution is a combination of a UIKeyboardDidShowNotification and UIKeyboardDidHideNotification observer with the above textFieldDidBeginEditing: and textFieldDidEndEditing: methods.
You need three additional variables, one to store the current selected UITextField (which I have named activeField), one to indicate if the current view has been moved, and one to indicate if the keyboard is displayed.
This is how the two UITextField delegate methods look now:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeField = nil;
// Additional Code
}
When the view is loaded, the following two observers are created:
- (void)viewDidLoad {
// Additional Code
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification
object:nil];
}
And the corresponding methods are implemented as follows:
- (void)keyboardWasShown:(NSNotification *)aNotification {
if ( keyboardShown )
return;
if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) {
NSDictionary *info = [aNotification userInfo];
NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y -= keyboardSize.height-44;
frame.size.height += keyboardSize.height-44;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
viewMoved = YES;
}
keyboardShown = YES;
}
- (void)keyboardWasHidden:(NSNotification *)aNotification {
if ( viewMoved ) {
NSDictionary *info = [aNotification userInfo];
NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y += keyboardSize.height-44;
frame.size.height -= keyboardSize.height-44;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
viewMoved = NO;
}
keyboardShown = NO;
}
This code works now as expected. The keyboard is only dismissed when the Done button is pressed, otherwise it stays visible and the view is not moved around.
As an additional note, I think it is possible to get the animationDuration dynamically by asking the NSNotification object, since I have already played with a similar solution but didn't get it to work (which it does now).
This view controller must be UITextView Delegate and you must set self.textview.delegate = self in viewdidload
-(void) textViewDidBeginEditing:(UITextView *)textView
{
NSLog(#"%f",self.view.frame.origin.y);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25f];
CGRect frame = self.view.frame;
frame.origin.y =frame.origin.y -204;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void) textViewDidEndEditing:(UITextView *)textView
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25f];
CGRect frame = self.view.frame;
frame.origin.y = frame.origin.y + 204;
[self.view setFrame:frame];
[UIView commitAnimations];
}
I got your Problem just do simple thing
just give outlet to UIScrollview.
set unique Tag property for each textfield in view.
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
switch (textField.tag)
{
case 2: //can be your textfiled tag
{ CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150);
//set figure y-150 as per your comfirt
[scrollview setContentOffset:scrollPoint animated:YES];
}break;
case 3:
{ CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180);
//set figure y-180 as per your comfirt
[scrollview setContentOffset:scrollPoint animated:YES];
}break;
...
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField.tag==3){
[scrollview setContentOffset:CGPointZero animated:YES];
}
//set the last textfield when you want to disappear keyboard.
}
Write below code in your view controller. tbl is your table view.
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
-(void) viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
#pragma mark - Keyboard Methods
-(void)keyboardWillShow:(NSNotification *)notification
{
// 375 × 667 ( 750 × 1334 ) iPhone 6
//414 × 736
CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
[UIView animateWithDuration:0.5 animations:^{
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
}];
}
-(void)keyboardWillChangeFrame:(NSNotification *)notification
{
CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height;
int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
[UIView animateWithDuration:0.5 animations:^{
// scroll.frame = rect;
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
}];
}
-(void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.3 animations:^{
// scroll.frame = rect;
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right);
}];
}
Fairly easy solution, works with all screen sizes
First you have to embed you UITextFields to a UIScrollView. In my case, I had several UITextFields and a UITextView.
Then you have to inherit from UITextFieldDelegate, UITextViewDelegate.
class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate
Assign textfield's and textview's delegates to self.
fullNameTextField.delegate = self
usernameTextField.delegate = self
websiteTextField.delegate = self
profileDescription.delegate = self
Then use this code:
var editingTextInput: UIView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardShown(notification:)),
name: NSNotification.Name.UIKeyboardDidShow,
object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
}
func keyboardShown(notification: NSNotification) {
if let infoKey = notification.userInfo?[UIKeyboardFrameEndUserInfoKey],
let rawFrame = (infoKey as AnyObject).cgRectValue {
let keyboardFrame = view.convert(rawFrame, to: view)
let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view)
if editingTextInputFrame.maxY > keyboardFrame.minY{
let diff = keyboardFrame.minY - editingTextInputFrame.maxY
containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.editingTextInput = textField
}
func textViewDidBeginEditing(_ textView: UITextView) {
self.editingTextInput = textView
}
func textFieldDidEndEditing(_ textField: UITextField) {
containerScrollView.setContentOffset(CGPoint.zero, animated: true)
}
func textViewDidEndEditing(_ textView: UITextView) {
containerScrollView.setContentOffset(CGPoint.zero, animated: true)
}
In short, you subscribe to UIKeyboardDidShow notification.
When you tap on textField or textView keyboard is shown and you grab keyboard's frame and frame of input element you have tapped on. Convert them to viewController's coordinate system and compare input element's lowest point to a keyboard's highest. If element's lower part is lower than keyboard's highest, than set offset of containerScrollView to the difference between them.
if editingTextInputFrame.maxY > keyboardFrame.minY{
let diff = keyboardFrame.minY - editingTextInputFrame.maxY
containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
}
Like I mentioned in this answer:
I've developed a framework for my own need to solve this issue better, and made it public now. It's not just for UITextField and UITextView, It works for any custom UIView that adopts UITextInput protocol like UITextField and UITextView and offers many useful features. You can install it via Carthage, CocoaPods or Swift Package Manager.
ODScrollView GitHub
ODScrollView Medium
ODScrollView is just a UIScrollView that automatically moves editable text areas like UITextField and UITextView vertically depending on keyboard visibility to offer better user experience.
Features
Automatically moves first responder UIViews that adopt the UITextInput protocol up/down when the keyboard appears/disappears, e.g., UITextField, UITextView, UISearchTextField or any custom UIView that adopts UITextInput protocol.
Note that if UITextInput's frame does NOT fits the remaining area between ODScrollView and keyboard, then ODScrollView adjusts UITextInput based on cursor position instead of frame. In such cases, "trackTextInputCursor" feature can be used. Example
Adjustment margin can be applied for each UITextInput seperately for .Top and .Bottom adjustment direction setting. 20 CGFloat by default.
Adjustment can be enabled/disabled for each UITextInput seperately. true by default.
Adjustment directon - .Top, .Center, .Bottom - can be applied for each UITextInput seperately. .Bottom by default. Example
Adjustment options determines how ODScrollView adjusts. .Always by default.
.Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView regardless UITextInput overlaps or not with shown keyboard. Example
.IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard. Example
Besides UIScrollView.keyboardDismissModes, the keyboard can be dismissed by tapping a UIView which is provided by ODScrollViewDelegate. After the keyboard is dismissed, ODScrollView can return its original position. nil and false by default. Example
Usage
1 - First thing you need to do is setting up ODScrollView and its content view properly. Since ODScrollView is just a UIScrollView, you can implement ODScrollView same way you do for UIScrollView. It's up to you to create ODScrollView by using storyboard or programmatically.
If you're creating ODScrollView programmatically, you can continue from step 4.
Suggested way to create UIScrollView in Storyboard
- If you are using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to Content Layout Guide's constraints.
1.4 - Set contentView's width equal to Frame Layout Guide's width.
1.5 - Set contentView's height equal to Frame Layout Guide's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
- If you are NOT using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to 0.
1.4 - Set contentView's width equal to scrollView's width.
1.5 - Set contentView's height equal to scrollView's superview's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
2 - Change the scrollView's class from UIScrollView to ODScrollView in the identity inspector on Storyboard.
3 - Create IBOutlets for scrollView and contentView on ViewController.
4 - Call the following methods inside ViewDidLoad() on ViewController:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
}
5 - Optional: You still can use UIScrollView's features:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
// UIScrollView setup
scrollView.delegate = self // UIScrollView Delegate
scrollView.keyboardDismissMode = .onDrag // UIScrollView keyboardDismissMode. Default is .none.
UITextView_inside_contentView.delegate = self
}
6 - Adopt ODScrollViewDelegate from ViewController and decide ODScrollView options:
extension ViewController: ODScrollViewDelegate {
// MARK:- State Notifiers: are responsible for notifiying ViewController about what is going on while adjusting. You don't have to do anything if you don't need them.
// #Optional
// Notifies when the keyboard showed.
func keyboardDidShow(by scrollView: ODScrollView) {}
// #Optional
// Notifies before the UIScrollView adjustment.
func scrollAdjustmentWillBegin(by scrollView: ODScrollView) {}
// #Optional
// Notifies after the UIScrollView adjustment.
func scrollAdjustmentDidEnd(by scrollView: ODScrollView) {}
// #Optional
// Notifies when the keyboard hid.
func keyboardDidHide(by scrollView: ODScrollView) {}
// MARK:- Adjustment Settings
// #Optional
// Specifies the margin between UITextInput and ODScrollView's top or bottom constraint depending on AdjustmentDirection
func adjustmentMargin(for textInput: UITextInput, inside scrollView: ODScrollView) -> CGFloat {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return 20
} else {
return 40
}
}
// #Optional
// Specifies that whether adjustment is enabled or not for each UITextInput seperately.
func adjustmentEnabled(for textInput: UITextInput, inside scrollView: ODScrollView) -> Bool {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return true
} else {
return false
}
}
// Specifies adjustment direction for each UITextInput. It means that some of UITextInputs inside ODScrollView can be adjusted to the bottom, while others can be adjusted to center or top.
func adjustmentDirection(selected textInput: UITextInput, inside scrollView: ODScrollView) -> AdjustmentDirection {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return .bottom
} else {
return .center
}
}
/**
- Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView.
- IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard.
*/
func adjustmentOption(for scrollView: ODScrollView) -> AdjustmentOption {
.Always
}
// MARK: - Hiding Keyboard Settings
/**
#Optional
Provides a view for tap gesture that hides keyboard.
By default, keyboard can be dismissed by keyboardDismissMode of UIScrollView.
keyboardDismissMode = .none
keyboardDismissMode = .onDrag
keyboardDismissMode = .interactive
Beside above settings:
- Returning UIView from this, lets you to hide the keyboard by tapping the UIView you provide, and also be able to use isResettingAdjustmentEnabled(for scrollView: ODScrollView) setting.
- If you return nil instead of UIView object, It means that hiding the keyboard by tapping is disabled.
*/
func hideKeyboardByTappingToView(for scrollView: ODScrollView) -> UIView? {
self.view
}
/**
#Optional
Resets the scroll view offset - which is adjusted before - to beginning its position after keyboard hid by tapping to the provided UIView via hideKeyboardByTappingToView.
## IMPORTANT:
This feature requires a UIView that is provided by hideKeyboardByTappingToView().
*/
func isResettingAdjustmentEnabled(for scrollView: ODScrollView) -> Bool {
true
}
}
7 - Optional: You can adjust the ODScrollView when the cursor overlaps with keyboard while typing in multiline UITextInput. trackTextInputCursor(for UITextInput) must be called by UITextInput functions that is fired while typing.
/**
## IMPORTANT:
This feature is not going to work unless textView is subView of _ODScrollView
*/
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
_ODScrollView.trackTextInputCursor(for textView)
return true
}
Related
I am working on a app which contains UITextField and UITextView in static UITableView. I am facing two issues
When I select UITextField it moved correctly but it is not working for UITextView.
When the keyboard disappears the UITableView not shown properly.
All I want is to adjust UITextField and UITextView accordingly when keyboard appears and disappears. Here is my code.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
//get the end position keyboard frame
NSDictionary *keyInfo = [notification userInfo];
CGRect keyboardFrame = [[keyInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
//convert it to the same view coords as the tableView it might be occluding
keyboardFrame = [self.tableView convertRect:keyboardFrame fromView:nil];
//calculate if the rects intersect
CGRect intersect = CGRectIntersection(keyboardFrame, self.tableView.bounds);
if (!CGRectIsNull(intersect)) {
//yes they do - adjust the insets on tableview to handle it
//first get the duration of the keyboard appearance animation
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//change the table insets to match - animated to the same duration of the keyboard appearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
}];
}
}
- (void) keyboardWillHide: (NSNotification *) notification{
NSDictionary *keyInfo = [notification userInfo];
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//clear the table insets - animated to the same duration of the keyboard disappearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
Try with the UITableView's instance method: scrollToRowAtIndexPath:atScrollPosition:animated:
In keyBoardWillShow use the tableview's or the textfield's index path and set the scroll position to UITableViewScrollPositionTop.
If it doesn't work as you want it to, try with scrollToRowAtIndexPath:atScrollPosition:animated:
Hope this helps, good luck! :))
For further information, check out Apple's UITableView reference: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html
You can use a third party library for avoiding keyboard scrolling above of text field or text views.
Use https://github.com/cokecoffe/ios-demo/blob/master/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m
OR
You can use UITableviewController as your view controller class.Change the Tableview content property to Static. Then you can add any UI controls to the static cells of the table view from story board.
If you select any text field or text view in your tableview, then key board automatically shows below of your text field or text view without any programming logic.
Im working on getting my UIscroll to scroll when ever a textfield is blocked by the keyboard by following this documentation
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW7
However sadly...there is a variable mentioned, activeField and i cannot figure out how it is declared. I would really like if some probably advise how/where it is declared or even a solution to scrolling when the keyboard is activated will help.
Thank you
To answer your specific question, since the Apple Documentation is only ever using activeField for size data, you can simply declare it as a private global UIView *activeField and it will work for textFields, textViews, etc all the same.
However, their code actually doesn't work very well at all. I had to make some changes to their code to get mine to work correctly. This code is basically theirs with some minor tweaks to handle all of the cases listed below:
1) If you have a smaller scrollView nested inside of a view, not full screen scrollView and it still works.
2) If you want to move text fields down to the keyboard focus area as well as move them up from behind the keyboard.
3) Works on textViews and textFields of various sizes
4) If the keyboard is currently showing, it will move any newly tapped fields into the focus area
5) Works if you have your content already scrolled and invoke the keyboard
6) Works on all keyboard sizes for all devices (no hardcoded constants)
First off, create private variables for these:
UIView *_activeField;
CGFloat _keyboardHeight;
BOOL _isShowingKeyboard;
Next, just cut and paste this code into your ViewController (it looks like a lot but it's not that bad).
#pragma mark TextFieldKeyboardScrolling
- (void)registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)adjustInputFieldsForKeyboard {
CGFloat keyBoardTopInScrollView = self.view.frame.size.height - _keyboardHeight - self.scrollView.frame.origin.y;
CGFloat inputFieldBottomInVisibleScrollView = _activeField.frame.origin.y - self.scrollView.contentOffset.y + 30 /* small buffer for cursor size */;
CGPoint scrollPoint;
if (keyBoardTopInScrollView > inputFieldBottomInVisibleScrollView) {
scrollPoint = CGPointMake(0.0, self.scrollView.contentOffset.y - (keyBoardTopInScrollView - inputFieldBottomInVisibleScrollView));
} else {
scrollPoint = CGPointMake(0.0, self.scrollView.contentOffset.y + (inputFieldBottomInVisibleScrollView - keyBoardTopInScrollView));
}
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
- (void)keyboardWasShown:(NSNotification*)aNotification {
_isShowingKeyboard = YES;
NSDictionary* info = [aNotification userInfo];
_keyboardHeight = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, _keyboardHeight, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
[self adjustInputFieldsForKeyboard];
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
_isShowingKeyboard = NO;
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
#pragma mark UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
_activeField = textField;
if (_isShowingKeyboard) {
[self adjustInputFieldsForKeyboard];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
_activeField = nil;
}
That's it, just call [self registerForKeyboardNotifications]; in your viewDidLoad method, hook up your scrollView outlet and textField/textView delegates in storyboard and you're all set.
The keyboard is only active when one of the texfields become active(Became first responder).So you should listen to UITextField delegate method to see when and which uitextfield became first responder.
- (void)textFieldDidBeginEditing:(UITextField *)textField
if you assign a tag to your textfield or make it ivar you can also understand which uitextfield has become active and also get its frame.
I don't use the scroll view or follow the above documentation.
Here is how without using the scroll view
In my
textFieldShouldBeginEditing
CGRect frame = self.view.frame;
frame.origin.y = <some negative value>;
self.view.frame = frame
and in my
textFieldShouldEndEditing
CGRect frame = self.view.frame;
frame.origin.y = <some positive value>; // absolute value of your above negative value
self.view.frame = frame
The above works for me. Remember these are the text field delegate methods. So you need to setup your delegate.
I am doing a project on gpsnote. if anyone have any information abt this application plz guide me...
I know this code bt i want textfield should come along with my keyboard
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
}
thanks
First set your content over a scroll view and set the scroll view in viewDidLoad:
[scrollView setContentSize : CGSizeMake (320, 1040)];
then in viewWillAppear add following notification :
For keyboard shown
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
For keyboard hiding
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
Following are the two function which are called by the notifications
- (void)keyboardWasShown:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
double keyboardHeight = kbSize.height;
double screenHeight = [UIScreen mainScreen].bounds.size.height - 20;
if(textOrigin > screenHeight - keyboardHeight)
{
double sofset = textOrigin - (screenHeight - keyboardHeight);
CGPoint offset = scrollBackGround.contentOffset;
offset.y += sofset;
[scrollBackGround setContentOffset:offset animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
[scrollBackGround setContentOffset:CGPointMake(0, 0) animated:YES];
}
In keyboardWasShown function what we are doing is just getting the height of the keyboard and checking if the textField y axis (i.e textOrigin in the function) are greater than Y axis of keyboard than slide up the content of the scrollview which contain our text field.
NOw How to get the textfield Y axis. For this you have to use the textfield delegate, the following delegate will trigger when your textfield will become first responder
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
textOrigin = scrollBackGround.frame.origin.y + textField.frame.origin.y + 20(it is status bar height) + yourNavigationBarheight;
// Make sure to make textOrigin an ivar in your .h file
}
And finally in keyboardWillBeHidden we are reseting the scrollview contentview
I have a few text fields and text views on my application's view. The keyboard comes up when I click in anyone of them. I have a method that gets called when any one of these objects is tapped in. However, I would like the method to execute its code only if the a certain Text Field(s) or a certain Text View(s) is tapped in. I would therefore like to have something like this in the method body:
{
if(currentField != mySpecialField)
{return;}
//Rest of the method code...
}
Now, my question is, how do I get a reference to the currently tapped in field so that I can perform the if-check.
Update:
In my situation, I am using the code I got from Apple's website that makes use of these methods:
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
if (keyboardShown)
return;
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [scroller frame];
viewFrame.size.height -= keyboardSize.height;
scroller.frame = viewFrame;
// Scroll the active text field into view.
CGRect textFieldRect = [activeField frame];
[scroller scrollRectToVisible:textFieldRect animated:YES];
keyboardShown = YES;
}
// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scroller frame];
viewFrame.size.height += keyboardSize.height;
scroller.frame = viewFrame;
keyboardShown = NO;
}
Can I modify these methods in such a way that I can get a reference to the caller UITextField and / or the caller UITextView?
Update:
Here's the method that's used for registration:
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
Is there any way I can achieve what I'm trying to achieve? Maybe if I modify the code above to pass 'object:self' instead of 'object:nil'?
Controls that are subclasses of UIControl (for example UIButton or UITextField) automatically sends a reference to themselves along to their target.
When creating your text fields, just specify a selector that takes one argument. In this example, "myTextField" is an instance variable:
myTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 40)];
[myTextField addTarget:self action:#selector(textFieldTapped:) forControlEvents:UIControlEventTouchUpInside];
And then the method we specified, textFieldTapped:, to handle the event:
- (void)textFieldTapped:(UITextField *)sender {
// Ignore any events that doesn't come from myTextField
if (sender != myTextField) return;
// TODO: Do stuff
}
In the case of UITextField you can also check out the UITextFieldDelegate protocol (the "delegate" value of your text field). It sends out various events that can be used to register the same kind of things, for example:
- (void)textFieldDidBeginEditing:(UITextField *)textField
I'm using a UITextView to roughly replicate the SMS text box above the keyboard. I'm using a UITextView instead of a field so that it can expand with multiple lines.
The problem is that, in my UITextView, the correction suggestions pop up below the text, causing them to be partially obscured by the keyboard.
In the SMS app, the suggestions pop up above the text. The placement does not appear to be a property of UITextView, or UITextInputTraits.
Any idea how to replicate this behavior? Thanks!
The problem is that the Keyboard is implemented as a separate UIWindow, rather than as a view within the main UIWindow, so layout with it is tricky. Here are some pointers in the right direction:
Hunt through the application's -windows property to find the private UITextEffectsWindow window and figure out its frame. This is the keyboard
Hunt through the TextView's subviews to find the private UIAutocorrectInlinePrompt view. This is the autocorrect bubble.
Move that subview into a separate wrapper view (added to the TextView) and then move that wrapper view so it's above the above-mentioned keyboard window.
You'll notice two mentions of "private" above. That carries all the relevant caveats. I have no idea why Apple has allowed the problem to persist when even their apps have had to work around it.
By doing the search for the UIAutocorrectInlinePrompt in an overridden or swizzled layoutSubViews it is possible to alter the layout of the correction so that it appears above. You can do this without calling any private APIs by looking for the subs views of particular classes positioned in a way you'd expect them. This example works out which view is which, checks to see that the correction is not already above the text and moves the correction above, and draws it on the window so that it is not bounded by the UITextView itself. Obviously if apple change the underlying implementation then this will fail to move correction. Add this to your overriden or swizzled layoutSubViews implementation.
- (void) moveSpellingCorrection {
for (UIView *view in self.subviews)
{
if ([[[view class] description] isEqualToString:#"UIAutocorrectInlinePrompt"])
{
UIView *correctionShadowView = nil; // [view correctionShadowView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectShadowView"])
{
correctionShadowView = subview;
break;
}
}
if (correctionShadowView)
{
UIView *typedTextView = nil; //[view typedTextView];
UIView *correctionView = nil; //[view correctionView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectTextView"])
{
if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
{
correctionView = subview;
}
else
{
typedTextView = subview;
}
}
}
if (correctionView && typedTextView)
{
CGRect textRect = [typedTextView frame];
CGRect correctionRect = [correctionView frame];
if (textRect.origin.y < correctionRect.origin.y)
{
CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0);
[correctionView setTransform: moveUp];
[correctionShadowView setTransform: moveUp];
CGRect windowPos = [self convertRect: view.frame toView: nil ];
[view removeFromSuperview];
[self.window addSubview: view];
view.frame = windowPos;
}
}
}
}
}
}
Actually doing
textview.scrollEnabled = NO;
will set the bubble on top of the text... the caveat is that you lose scrolling, in my case it wasn't a problem due to havinng a textfield only for input purposes with character limit
Actually, the keyboard simply uses the result of -[UITextInput textInputView] to determine where to put the correction view (and to ask if your view supports correction). So all you need to do is this:
- (UIView *)textInputView {
for (UIWindow *window in [UIApplication sharedApplication].windows) {
if ([window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")] &&
window != self.window) {
return window;
}
}
// Fallback just in case the UITextEffectsWindow has not yet been created.
return self;
}
Note that you'll likely also need to update -[UITextInput firstRectForRange:] to use the coordinate system of the window / device, so you can do this:
- (CGRect)firstRectForRange:(CoreTextTokenTextRange *)range {
CGRect firstRect = [self firstRectForRangeInternal:range];
return [self convertRect:firstRect toView:[self textInputView]];
}
(In the above context, self is a class that implements UITextInput).
If the bottom of your UITextView clears the keyboard, you should be able to just resize your UITextView to be tall enough to see the corrections. The corrections themselves don't display outside of the UITextView's frame.
If you want to mimic what you are getting in the SMS app (corrections above), you'll probably have to roll your own.
Putting the below method, adjustAutocorrectPromptView in layoutSubviews worked for me in portrait and landscape. I have a category that provides the bottom and top methods on view but you get the idea.
NSArray * subviewsWithDescription(UIView *view, NSString *description)
{
return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"class.description == '%#'", description]]];
}
- (void) adjustAutocorrectPromptView;
{
UIView *autocorrectPromptView = [subviewsWithDescription(self, #"UIAutocorrectInlinePrompt") lastObject];
if (! autocorrectPromptView)
{
return;
}
UIView *correctionShadowView = [subviewsWithDescription(autocorrectPromptView, #"UIAutocorrectShadowView") lastObject];
if (! correctionShadowView)
{
return;
}
UIView *typedTextView = nil; //[view typedTextView];
UIView *correctionView = nil; //[view correctionView];
for (UIView *subview in subviewsWithDescription(autocorrectPromptView, #"UIAutocorrectTextView"))
{
if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
{
correctionView = subview;
}
else
{
typedTextView = subview;
}
}
if (correctionView && typedTextView)
{
if (typedTextView.top < correctionView.top)
{
correctionView.bottom = typedTextView.top;
correctionShadowView.center = correctionView.center;
}
}
}
Make sure your view controller delegate is listening to the notification when the keyboard pops up so that you resize your UITextView so that the keyboard doesn't obscure the UITextView. Then your correction won't be obscured by the keyboard. See:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html
Here is a copy of the code from that page in case the original link is broken:
// the amount of vertical shift upwards keep the Notes text view visible as the keyboard appears
#define kOFFSET_FOR_KEYBOARD 140.0
// the duration of the animation for the view shift
#define kVerticalOffsetAnimationDuration 0.50
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
- (IBAction)backgroundClick:(id)sender
{
[latitudeField resignFirstResponder];
[longitudeField resignFirstResponder];
[notesField resignFirstResponder];
if (viewShifted)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:kVerticalOffsetAnimationDuration];
CGRect rect = self.view.frame;
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
self.view.frame = rect;
[UIView commitAnimations];
viewShifted = FALSE;
}
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if (!viewShifted) { // don't shift if it's already shifted
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:kVerticalOffsetAnimationDuration];
CGRect rect = self.view.frame;
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
self.view.frame = rect;
[UIView commitAnimations];
viewShifted = TRUE;
}
return YES;
}