editable webView getting scrolled to top by itself - ipad

I have some text displayed in an editable webView. As soon as I scroll it down and touch somewhere to edit the rendered text, it scrolls to the top itself and the keyboard appears and hence I have to scroll it down again for editing. Is there a way to prevent webView from doing that?

Got the same problem and still looking for normal solution of this weird behavior.
We still cannot prevent UIWebView from doing this, and if you look at Evernote application on iPad, you'll see the same issue there, unfortunately :(
The only thing we could do on this is to save contentOffset of UIWebView when keyboard is shown and restore if after keyboard is opened.
This will look like:
//register your controller for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) UIKeyboardDidShowNotification object:nil];
Then you will need to handle keyboard notification like:
- (void)keyboardWillShow:(NSNotification *)aNotification {
// scroll view will scroll to beginning, but we save current offset
[_yourViewWithWebView saveOffset];
...
}
After that you will need to handle event when keyboard was shown:
- (void)keyboardWasShown:(NSNotification*)aNotification{
...
// scroll view scrolled to beginning, but we restore previous offset
[_yourViewWithWebView restoreOffset];
}
Accordingly in your view which contains UIWebView you'll need to implement:
static CGPoint editableWebViewOffsetPoint;
- (void) saveOffset{
editableWebViewOffsetPoint = yourWebView.scrollView.contentOffset;
}
- (void) restoreOffset{
//just use animation block to have scroll animated after jumping to top and back to old position
[UIView animateWithDuration:.2
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
yourWebView.scrollView.contentOffset = editableWebViewOffsetPoint;
}
completion:nil];
}
Hope in general this will help you to solve your problem at least partially.
If someone will help us to prevent UIWebView scrolling to top each time keyboard is displayed, I'd appreciate this deeply.
UIWebView.scrollView.scrollsToTop = NO; does not help.
Disabling scrolling before showing keyboard and enabling it after keyboard is displayed also didn't work.
Also in future you will face problem with editing text when cursor is not in visible area of UIWebView - and it does not scroll itself automatically to make cursor visible. We have solved that problem, but I am in progress of creating detailed and readable tutorial of how we've done this. If you already solved this problem, I'd appreciate to look at your solution :)
PS: http://www.cocoanetics.com/2011/01/uiwebview-must-die/
Thank you,
Sergey N.

A method that works rather well is temporarily disabling setContentOffset: on UIScrollView, while the keyboard is being shown. This is a little hackish though, so it may cause other issues instead, in some situations.
As in #Sergey N.'s response, register for the keyboard notifications, but instead of storing/restoring contentOffset, use these:
- (void)keyboardWillShow:(NSNotification *)aNotification {
[self disableMethod:#selector(setContentOffset:) onClass:[UIScrollView class]];
}
- (void)keyboardWasShown:(NSNotification *)aNotification {
[self enableMethod:#selector(setContentOffset:) onClass:[UIScrollView class]];
}
Somewhere else in the class (or in another class, as long as you replace self in above calls), place these:
-(void)swizzleMethod:(SEL)origSel from:(Class)origClass toMethod:(SEL)toSel from:(Class)toClass{
Method origMethod = class_getInstanceMethod(origClass, origSel);
Method newMethod = class_getInstanceMethod(toClass, toSel);
method_exchangeImplementations(origMethod, newMethod);
}
-(void)disableMethod:(SEL)sel onClass:(Class)cl{
[self swizzleMethod:sel from:cl toMethod:#selector(doNothing) from:[self class]];
}
-(void)enableMethod:(SEL)method onClass:(Class)cl{
[self swizzleMethod:#selector(doNothing) from:[self class] toMethod:method from:cl];
}
-(void)doNothing{
}
This prevents the webview from scrolling to top in the first place, so it won't show that bad animation, however, in some situations it may cause some problems (e.g have more input controls in the view holding the webview). Tested this successfully in iOS 5.0+.
In iOS 6.0 the scrolling to top seems to be fixed, so no workaround is necessary.

Functions for "editing text when cursor is not in visible area" problem.
- (void)keyboardWasShown:(NSNotification *)aNotification {
//if(self.navigationController.viewControllers objectAtIndex:([self.navigationController.viewControllers count]-1)==self.)
NSLog(#"keyboardshown");
if (keyboardshown)
return;
keyboardshown=YES;
NSDictionary* userInfo = [aNotification userInfo];
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
CGRect newFrame = self.textView.frame;
CGRect keyboardFrame = [self.textView convertRect:keyboardEndFrame toView:nil];
newFrame.size.height -= keyboardFrame.size.height;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3];
[self.textView setFrame:newFrame];
[UIView commitAnimations];
}
- (void)keyboardWasHidden:(NSNotification *)aNotification {
if (!keyboardshown)
return;
keyboardshown=NO;
NSDictionary* userInfo = [aNotification userInfo];
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
CGRect newFrame = self.textView.frame;
CGRect keyboardFrame = [self.textView convertRect:keyboardEndFrame toView:nil];
newFrame.size.height += keyboardFrame.size.height;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:0.3];
self.textView.frame = newFrame;
[UIView commitAnimations];
}

Actually keyboardWasShown will not always be invoked especially when user has BT keyboard connected and virtual one can be hidden/shown by Eject key. We have implemented our own class like:
#implementation KeyboardUtils
+ (CGRect) convertRect:(CGRect)rect toView:(UIView *)view {
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *) view : [view window];
return [view convertRect:[window convertRect:rect fromWindow:nil] fromView:nil];
}
/**
* This is working but deprecated solution
* Based on UIKeyboardCenterBeginUserInfoKey and UIKeyboardCenterEndUserInfoKey which are deprecated since iOS 3.2
*/
+ (BOOL)checkKeyboardOnDisplayCenterBegin:(CGRect)centerBegin centerEnd:(CGRect)centerEnd{
CGRect mainScreen = [UIApplication currentBounds];
BOOL isKeyboardOnDisplay = CGRectContainsPoint(mainScreen, centerEnd.origin);
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:isKeyboardOnDisplay] forKey:#"isKeyboardOnDisplay"];
[[NSUserDefaults standardUserDefaults] synchronize];
return isKeyboardOnDisplay;
}
/**
* This method allows to verify if software keyboard is currently present on screen for the application
* Allows to handle undocked, split states of keyboard, as well as connected Bluetooth keyboard.
* Needed to adjust UI - scrolling and insets for editable parts of the app, as well as avoid application be beneath open keyboard
*/
+ (BOOL)checkKeyboardOnDisplayBeginFrame:(CGRect)frameBegin endFrame:(CGRect)frameEnd{
CGRect mainScreen = [UIApplication currentBounds];
UIView *firstView = [[(AppDelegate *)[[UIApplication sharedApplication] delegate] window].subviews objectAtIndex:0];
CGRect convertedEndFrame = [KeyboardUtils convertRect:frameEnd toView:firstView];
BOOL isKeyboardOnDisplay = CGRectContainsRect(mainScreen, convertedEndFrame);
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:isKeyboardOnDisplay] forKey:#"isKeyboardOnDisplay"];
[[NSUserDefaults standardUserDefaults] synchronize];
return isKeyboardOnDisplay;
}
+ (BOOL)checkKeyboardOnDisplayFromNotification:(NSNotification *)aNotification{
BOOL isKeyboardOnDisplay = [KeyboardUtils checkKeyboardOnDisplayBeginFrame:[[aNotification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]
endFrame:[[aNotification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]];
return isKeyboardOnDisplay;
}
Then you can use it like:
- (void)keyboardWillChangeFrame:(NSNotification*)aNotification{
[KeyboardUtils checkKeyboardOnDisplayFromNotification:aNotification];
}
Where keyboardWillChangeFrame is selector-observer for:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
In such a way you are saving state of your keyboard (if it is shown as docked one and really present on display, and BT keyboard is not used) into NSUserDefaultSettings. In your handlers which listen to keyboard notifications or orientation changes you should check this key value from the defaults.
One more additional method is [UIApplication currentBounds];
It is present in the app by extending it with category like: (.h file)
#import <UIKit/UIKit.h>
#interface UIApplication (AppDimensions)
+(CGSize) currentSize;
+(CGRect) currentBounds;
+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation;
#end
.m file:
#import "UIApplication+AppDimensions.h"
#implementation UIApplication (AppDimensions)
+(CGSize) currentSize
{
return [UIApplication sizeInOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
+(CGRect) currentBounds{
CGRect bounds = [UIScreen mainScreen].bounds;
bounds.size = [UIApplication currentSize];
return bounds;
}
+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation
{
CGSize size = [UIScreen mainScreen].bounds.size;
UIApplication *application = [UIApplication sharedApplication];
if (UIInterfaceOrientationIsLandscape(orientation))
{
size = CGSizeMake(size.height, size.width);
}
if (application.statusBarHidden == NO)
{
size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height);
}
return size;
}
#end
Hope this will help anyone who is concerned about handling presence of keyboard on the screen.

Related

How to Preserve state of UIView Controller when it come back from Background

I have created one login Page which have two uiTextField and Login Button. When you try to login Keyboard hide textfield and button, So I have used below code to move up and down view controller from textfield delegate methods.
-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
int movementDistance = -130; // tweak as needed
float movementDuration = 0.3f; // tweak as needed
int movement = (up ? movementDistance : -movementDistance);
[UIView beginAnimations: #"animateTextField" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
It works fine. But when i close app by home button and restart again it not preserve view controller position, Keyboard is still display but position of view controller changed to default.
Declare a NSNotification in ViewDidLoad of your View Controller class,when the application will become active it will call your desire method.
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(refreshViewOnActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
// write rest of your code
}
- (void)refreshViewOnActive:(NSNotification *)notification {
if( [textField isFirstResponder]) // check whether keyboard is open or not / or editing is enabled for your textfeild
{
[self animateTextField:textField up:true]; //call your desired method
}
}

UIKeyboardWillChangeFrameNotification called BEFORE textViewDidBeginEditing, but AFTER textFieldDidBeginEditing? Why?

I have the following observer in my ViewController.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
This view controller also conforms to the UITextFieldDelegate and UITextViewDelegate and implements textFieldDidBeginEditing and textViewDidBeginEditing.
Now, here's the weird part.
If you tap on the UITextField, the order of calls is textFieldDidBeginEditing AND THEN keyboardWillChangeFrame:.
If you tap on the UITextView, the order of calls is keyboardWillChangeFrame AND THEN 'textViewDidBeginEditing'.
Anyone not see a problem with this? Shouldn't text_____DidBeginEditing be called first no matter whether it's a Field or View. Why is this?
It's leading to weird animation issues. I need it to be consistent one way or the other.
I believe you can use the UIKeyboardWillShowNotification, and something like the above code for what you are trying to do:
- (void)keyboardWillShowNotification:(NSNotification*)notification {
NSDictionary *info = notification.userInfo;
CGRect r = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [self.view convertRect:r fromView:nil];
UIViewAnimationCurve curve = [info[UIKeyboardAnimationCurveUserInfoKey] integerValue];
double duration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:curve];
[UIView setAnimationDuration:duration];
/* view changes */
[UIView commitAnimations];
}
Use UIKeyboardDidChangeFrameNotification instead of UIKeyboardWillChangeFrameNotification.

Hide inputAccessoryView if hardware keyboard is connected

Similar to this question: iPad: Detecting External Keyboard, I am developing an iPad app that is using text fields with a custom inputAccessoryView to provide additional functionality for the virtual keyboard.
However, if a hardware keyboard (e.g. bluetooth keyboard) is connected to the device, the software keyboard is not shown as expected, but for some reason the inputAccessoryView is still visible at the bottom of the screen. Additionally, this seems to cause firing the UIKeyboardDidShowNotification(and therefore moving my view up to avoid occlusion by the keyboard which is actually not present) even if the hardware keyboard is used for input.
I found several solutions to detect if a hardware keyboard is connected, but all of them check the state after receiving a UIKeyboardDidShowNotification, at which point the inputAccessoryView is already visible (e.g. How can I detect if an external keyboard is present on an iPad?).
I am looking for a way to only show a inputAccessoryView if there is no hardware keyboard connected. Therefore I need to know if a hardware keyboard is connected before a UIKeyboardDidShowNotification is fired.
The accepted solutions provided here How can I detect if an external keyboard is present on an iPad? are no option for me as they use private APIs which may cause my app to get rejected.
This is just an enhancement of the answer by #arlomedia. What I did was watch for the willShow and didShow.
The willShow I use to move my textview into position so that it moves at the same rate as the keyboard.
The didShow I use to check the apparent size of the keyboard using the aforementioned technique and hide/show the accessoryInputView accordingly.
It's important that I also set that view to be hidden by default, and that when a willHide event is received, it is hidden again then.
- (void) addKeyboardObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardHidden:) name:UIKeyboardWillHideNotification object:nil];
}
- (void) removeKeyboardObserver {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification*)notification {
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// If we're on iOS7 or earlier and landscape then the height is in the
// width.
//
if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
keyboardSize.height = keyboardSize.width;
}
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
CGRect textFieldFrame = self.textField.frame;
textFieldFrame.origin.y = ([Util screenHeight] - keyboardSize.height) - textFieldFrame.size.height - [Util scaledHeight:10.0];
// Move the text field into place.
//
[UIView animateWithDuration:rate.floatValue animations:^{
self.answerTextField.frame = textFieldFrame;
}];
keyboardShown = YES;
}
- (void)keyboardDidShow:(NSNotification*)notification {
CGRect keyboardBeginFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGSize keyboardSize = keyboardBeginFrame.size;
// If we're on iOS7 or earlier and landscape then the height is in the
// width.
//
if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
keyboardSize.height = ABS(keyboardBeginFrame.origin.x - keyboardEndFrame.origin.x); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
} else {
keyboardSize.height = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
}
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:rate.floatValue animations:^{
if (keyboardSize.height <= self.accessoryBar.frame.size.height) {
self.textField.inputAccessoryView.hidden = YES;
self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
} else {
self.textField.inputAccessoryView.hidden = NO;
self.answerTextField.inputAccessoryView.userInteractionEnabled = YES;
}
}];
keyboardShown = YES;
}
- (void)keyboardHidden:(NSNotification*)notification {
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
// Remove/hide the accessory view so that next time the text field gets focus, if a hardware
// keyboard is used, the accessory bar is not shown.
//
[UIView animateWithDuration:rate.floatValue animations:^{
self.textField.inputAccessoryView.hidden = YES;
self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
}];
keyboardShown = NO;
}
NOTE Edited to add change to userInteractionEnabled so that a hidden accessoryView doesn't eat taps.
IIRC, views don't resize themselves when the software keyboard appears. I am resizing my views in a keyboardDidShow method triggered by a UIKeyboardDidShow notification. So it should be enough to detect the hardware vs. software keyboard in that method and then you could skip the table resizing and hide the input accessory view (or adjust the table resizing to accommodate the input accessory view, if you prefer to leave that visible).
To resize the views correctly whether or not a hardware keyboard is present, I adapted the code from this answer:
- (void)keyboardDidShow:(NSNotification *)aNotification {
CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
// now you can resize your views based on keyboardHeight
// that will be the height of the inputAccessoryView if a hardware keyboard is present
}
That's all you need if you want to leave the inputAccessoryView visible. To also hide that, I think you will need to set an instance variable so you can access it in keyboardDidShow:
UIView *currentInputAccessoryView;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentInputAccessoryView = textField.inputAccessoryView;
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.currentInputAccessoryView = textView.inputAccessoryView;
}
- (void)keyboardDidShow:(NSNotification *)aNotification {
CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
if (keyboardHeight == 44) {
self.currentInputAccessoryView.hidden = YES;
keyboardHeight = 0;
}
// now you can resize your views based on keyboardHeight
// that will be 0 if a hardware keyboard is present
}
My final way to solve this issue was to simply add an observer for the UIKeyboardWillShowNotification ...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
.. and hide the inputAccessoryView previously stored in an instance variable.
// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillShow:(NSNotification*)notification
{
NSLog(#"keyboardWillShow");
// get the frame end user info key
CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// calculate the visible portion of the keyboard on the screen
CGFloat height = [[UIScreen mainScreen] bounds].size.height - kbEndFrame.origin.y;
// check if there is a input accessorry view (and no keyboard visible, e.g. hardware keyboard)
if (self.activeTextField && height <= self.activeTextField.inputAccessoryView.frame.size.height) {
NSLog(#"hardware keyboard");
self.activeTextField.inputAccessoryView.hidden = YES;
} else {
NSLog(#"software keyboard");
self.activeTextField.inputAccessoryView.hidden = NO;
}
}
It turned out the problem was caused by me dynamically creating the inputAccessoryView of my custom UITextField subclass in its getter method. I inadvertently recreated the view with every call of the getter instead of reusing an instance variable with lazy instantiation. This resulted in all my assignments to the view being ignored since apparently the getter method will be called multiple times when the text field is being accessed and the keyboard shown and therefore the view kept being overridden after my assignments. Reusing the view by saving it to an instance variable fixed this issue.
This is an old thread, but as of iOS 14 we now have proper APIs for tracking hardware keyboards via GameController framework using GCKeyboard and GCKeyboardDidConnect/GCKeyboardDidDisconnect notifications.
In this particular case of hiding and showing inputAccessoryView, you could do something like this:
import GameController
class ViewController: UIViewController {
...
var isHardwareKeyboardConnected: Bool {
didSet {
enableInputAccessoryIfNeeded()
}
}
func enableInputAccessoryIfNeeded() {
textField.inputAccessoryView = isHardwareKeyboardConnected ? nil : textFielInputAccessoryView
textField.reloadInputViews()
}
init() {
isHardwareKeyboardConnected = GCKeyboard.coalesced != nil
super.init(nibName: nil, bundle: nil)
startObservingHardwareKeyboard()
}
override func viewDidLoad() {
super.viewDidLoad()
enableInputAccessoryIfNeeded()
}
func startObservingHardwareKeyboard() {
NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidConnect), name: .GCKeyboardDidConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidDisconnect), name: .GCKeyboardDidDisconnect, object: nil)
}
#objc func hardwareKeyboardDidConnect(_ notification: Notification) {
print("[Keyboard] Hardware keyboard did connect")
isHardwareKeyboardConnected = true
}
#objc func hardwareKeyboardDidDisconnect(_ notification: Notification) {
print("[Keyboard] Hardware keyboard did disconnect")
isHardwareKeyboardConnected = false
}
}

move view to reset frame and keyboard at the same time

I have view to type message as shown below.
Now, when I type message, the keyboard appears and the box should move just above keyboard as shown in figure below.
my problem
They keyboard animation and view animation occur at different time. Keyboard appears first and then view appears. Even if i tried to set animation time to any, they occur at different time.
How should I solve my problem?
Please, suggest me way to solve it so that keyboard and view animates to show as if they are of same view. Both animation should occur at exact time so that they look like same view appeared at a time.
what i tried
my view did load has following code
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
now keyboard show function look like
- (void)keyboardWillShow:(NSNotification *)note{
NSDictionary* keyboardInfo = [note userInfo];
CGFloat duration = [[keyboardInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
[UIView animateWithDuration:duration animations:
^{
//chat_typingView is name for typing view
chat_typingView.frame = CGRectMake(chat_typingView.frame.origin.x,
238,
chat_typingView.frame.size.width,
chat_typingView.frame.size.height);
}
maybe you should check if your chat_typinfView is First responder before do the animation and disable Autolayout (very important).
if ([chat_typingView isFirstResponder]) {
// Do the animation
}
PS is recommendable to subscribe to the notification on viewWillApper instead of viewDidLoad
I have a similar setup in one of my apps and I do the following:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardShowed:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
/*** FOR AUTOLAYOUT MODIFICATIONS & ADDITIONS # RUNTIME ***/
self.defaultViewFrame = self.myView.frame
}
- (void) keyboardShowed:(NSNotification*)notification {
//GET KEYBOARD FRAME
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
//CONVERT KEYBOARD FRAME TO MATCH THE OUR COORDINATE SYSTEM (FOR UPSIDEDOWN ROTATION)
CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
//..... do something with the convertedFrame (in your case convertedFrame.origin.y)
}
- (void) keyboardHidden:(NSNotification*)notification {
//RESTORE ORIGINAL STATE
[UIView transitionWithView:self.view
duration:.3f
options:UIViewAnimationOptionCurveLinear
animations:^{
self.myView.frame = self.defaultViewFrame;
}
completion:nil];
}

iOS- Change of UITextField height and movement of Buttons based on keyboard display

I am having an view in iOS where there is an UITextfield and 3 UIbuttons below the UITextField. Please see below for the pic. When this view is launched, the default state is STATE1.(Please see image). The keyboard is visible by default. Now when I dispose the keyboard, I wish to have the edittext resized and occupy the whole screen as shown in STATE2.
I am not sure how to accomplish this. I have the height of the UITextfield hardcoded to some dp based on the target device. I believe this has to be changed and it has to dynamically occupy the screen based on the screen size.
Can anyone help me accomplish this. Please consider the button is like a tail to the edit text. This clings to the edit text no matter the keyboard is visible or not. Thanks
You can use TPKeyboardAvoiding library and achieve this what you want.
First you have to use UITextView instead of UITextField
The register your class for
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
Implement this methods
- (void)keyboardWillHide:(NSNotification *)notification {
[self resizeViewWithOptions:[notification userInfo]];
}
- (void)keyboardWillShow:(NSNotification *)notification {
[self resizeViewWithOptions:[notification userInfo]];
}
- (void)resizeViewWithOptions:(NSDictionary *)options {
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[options objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[options objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[options objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:animationCurve];
[UIView setAnimationDuration:animationDuration];
CGRect viewFrame = self.view.frame;
CGRect keyboardFrameEndRelative = [self.view convertRect:keyboardEndFrame fromView:nil];
NSLog(#"keyboardFrameEndRelative: %#", NSStringFromCGRect(keyboardFrameEndRelative));
// Now set Frame for UITextView and UIbuttons frame here
}
Try like this even simpler first add those controls what you want which looks like in state 1
in viewDidAppear add the following function
-(void)viewDidAppear:(BOOL)animated
{
[self.Textfield1 becomeFirstResponder];
}
so it will load keyboard when the view loads
then add this lines to .m to close the keyboard press return it will hide keyboard
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
//Write your coding for resizing size dynamically to show look like state 2
//Here check version of device to set as per height
textfield.frame=CGRectMake(0,0,320,400);
button1.frame=CGRectMake(0,400,320,400);
[textField resignFirstResponder];
return YES;
}
don forget to add delegate for textfield

Resources