I have an instance of UIToolbar that contains a UITextField inside. I'd like to set the toolbar at an accessory view for the UITextField it contains.
The way I do this is as follows:
[myTextView setInputAccessoryView:myToolbar];
When I compile and run the code, the entire keyboard disappears when I press on the text field. I specifically made sure I am setting the inputAccessoryView and not the inputView. It seems like the whole input view just got replaced, without any explicit direction to do so.
Does anyone know a way to fix this?
It's generally not good to put a text field in an input accessory view... What would be better is if you put the toolbar along the bottom of your view and then use UIKeyboardWillChangeFrameNotification to move the toolbar with the keyboard...
In your viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
And somewhere in your view controller's code:
-(void) keyboardWillChange:(NSNotification*)notify {
CGRect endFrame;
float duration = [[[notify userInfo] valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
[[[notify userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&endFrame];
endFrame = [self.view convertRect:endFrame fromView:nil];
float y = (endFrame.origin.y > self.view.bounds.size.height ? self.view.bounds.size.height-44 : endFrame.origin.y-44);
[UIView animateWithDuration:duration animations:^{
toolbar.frame = CGRectMake(0, y, self.view.bounds.size.width, 44);
}];
}
Related
I have a screen within an iPhone app that consist of a UITextView. This text view is contained within a UIScrollView. The purpose of the screen is for the user to type in text, and to optionally attach an image to what he is writing. Therefore, the screen also has a UIToolbar with a camera button at the bottom of the screen. The structure of the screen is as follows:
-View
--UIScrollView
---UITextView
--UIToolbar
---UIButton
When the user navigates to this screen, the viewDidAppear method assigns first responder to the uitextview element, so the keyboard shows up, which hides the toolbar and the camera button.
I would like the entire toolbar to re-draw itself right above the keyboard, and to position itself again at the bottom of the screen when the keyboard hides.
I have found related posts on SO (like this one). However, such methods introduce undesired behaviours. For example, implementing the solution in the article above, the toolbar does move with the keyboard, but the UIScrollView gets its frame.origin.y coordinate shifted way above the top of the screen, so it's impossible for the user to see what he is typing.
I have also tried to reset the frame of the toolbar, by adding it as an IBOutlet and using cgrectmake to reposition it. However, after several tries, the toolbar remains stuck at the bottom of the screen and hidden by the keyboard:
- (void) liftMainViewWhenKeybordAppears:(NSNotification*)aNotification{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:<#(UIViewAnimationCurve)#>]
CGRect frame = self.keyboardToolbar.frame;
frame.origin.y = self.keyboardToolbar.frame.origin.y - 280;
self.keyboardToolbar.frame = frame;
[UIView commitAnimations];
}
I have tried several iterations similar to the code above and they all fail at repositioning the toolbar.
So in short, what is the right way to float a toolbar right on top of a keyboard in a screen whose space is completely utilised by a uitextview element?
Thanks to RoryMcKinnel for the pointer. As the article referenced is in Swift, I thought I might paste the solution that worked for be on ObjC
- (void)keyboardWillShowNotification:(NSNotification *)notification{
NSDictionary *userInfo = notification.userInfo;
double animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect convertedKeyboardFrame = [self.view convertRect:keyboardEndFrame fromView:self.view.window];
UIViewAnimationOptions rawAnimationCurve = (UIViewAnimationOptions)[userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] << 16;
_toolBarBottomGuide.constant = CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(convertedKeyboardFrame);
[UIView animateWithDuration:animationDuration delay:0.0 options:rawAnimationCurve animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
Bear in mind, this code did make the toolbar move as required, but the toolbar was not visible at all. It turned out that it was being hidden behind the UIScrollView. This was easily fixed by shifting the order between the scroll view and the toolbar element in the IB hierarchy.
The method above works for the keyboardWillShow event. You'll need to add the corresponding one for when the keyboard hides, like this:
- (void)keyboardWillHideNotification:(NSNotification *)notification{
NSDictionary *userInfo = notification.userInfo;
double animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIViewAnimationOptions rawAnimationCurve = (UIViewAnimationOptions)[userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] << 16;
_toolBarBottomGuide.constant = 0.0f;
[UIView animateWithDuration:animationDuration delay:0.0 options:rawAnimationCurve animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
I am working on a chat application similar to whatsapp etc. It has a tableview in view controller and a text field and button in the bottom toolbar. I came across the various question on sliding the view upwards and using this link I managed to slide the view upwards. However I want to dismiss the keyboard and the view comes down and fits the screen .I tried using tap gesture and click on return button but nothing seems to work. How do I do make the view slide down and keyboard disappear?
Moreover how can i change the width of text field so that multiple lines can appear when the user is writing the message?
you can add tap gesture event to tableview cell and also you can use touch event method when user click on tableview then according to keyboard previous state you can display or hide keyboard. Hope this will help to u.
Use textFieldShouldReturn to resign first responder status (dismiss the keyboard) and slide the view up.
Personally I do it this way:
I register for notifications to know when the keyboard will be shown, and when it will be hidden.
When the keyboard appears, I set the view insets to include the size of the keyboard.
Slide the view up
When the keyboard will disappear, I set the insets to zero.
TextField Delegate Method to hide the keyboard when the Return button is tapped
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
[textField resignFirstResponder];
return NO; // We do not want the UITextField to insert line-breaks.
}
Register for keyboard appear/disappear notifications
- (void)viewDidLoad
{
...
// Register for notifications for when the keyboard will appear and disappear
[self registerForKeyboardNotifications];
}
// 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(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
// Original code for this part here: http://stackoverflow.com/a/16044603/4518324
- (void)keyboardWasShown:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd fromView:nil];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y);
} completion:nil];
}
- (void)keyboardWillBeHidden:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd fromView:nil];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y);
} completion:nil];
}
I have created sample code that involves resizing the view when they keyboard is shown or dismissed.
https://github.com/gingofthesouth/KeyboardHideShow
I got it right . I had another method called out when keyboard is dismissed which fit the view frame as per requirement which is View.frame-keyboard.frame.height. Thanks anyways!:)
I am developing a chat app which has UITableView and a UIView containing a UITextField and a UIButton in it. I am using the following code to move the UIView up when keyboard appears.
(void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[UIView animateWithDuration:0.2f animations:^{
CGRect frame = self.inputView.frame;
UIInterfaceOrientation orientation = self.interfaceOrientation;
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
frame.origin.y -= kbSize.width;
else
frame.origin.y -= kbSize.height;
self.inputView.frame = frame;
;
}];
}
This code is working fine until iOS 7, but in iOS 8 UIView is not displaying above the keyboard.
Can anyone please suggest what could be the possible issue, or is there anything that has changed in iOS 8?
Your code seems to be correct but i will prefer using UIKeyboardDidChangeFrameNotification or UIKeyboardWillChangeFrameNotification because these will tell you the change in keyboard frame when predictive text bar gets up or down when keyboard is in view.
In your ViewDidLoad add this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardFrameDidChange:)
name:UIKeyboardDidChangeFrameNotification object:nil];
and then paste this method in your ViewController
-(void)keyboardFrameDidChange:(NSNotification*)notification{
NSDictionary* info = [notification userInfo];
CGRect kKeyBoardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[yourView setFrame:CGRectMake(0, kKeyBoardFrame.origin.y-yourView.frame.size.height, 320, yourView.frame.size.height)];
}
This will handle all your keyboard cases like when its up or down or change in its frame with predictive text bar
and also remove observer when you are leaving your view
The accepted Answer is almost right. To match your view's animation to that of the keyboard you want to use the UIKeyboardWillChangeFrameNotification rather than the UIKeyboardDidChangeFrameNotification. That way the animations you kick off will precisely match that of the keyboard. Here's some code to do the entire thing. I use the animation of the keyboard to drive the animation of my autolayout constraint constants, but you can easily adapt this to animate an entire view frame. (Note, we have to use the old school style animations to hook into the UIKeyboardCurveInfoKey which provides an animation curve exactly matching the keyboard animation.
In viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardFrameDidChange:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
In ViewController:
- (void)keyboardFrameDidChange:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
CGRect kKeyBoardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat height = kKeyBoardFrame.size.height;
[self.view removeConstraints:self.verticalButtonConstraints];
NSDictionary *metrics = #{#"height" : #(height)};
NSDictionary *views = #{#"nextButton" : self.nextButton};
self.verticalButtonConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V: [nextButton(52)]-(height)-|" options:0 metrics:metrics views:views];
[self.view addConstraints:self.verticalButtonConstraints];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.view layoutIfNeeded];
[UIView commitAnimations];
}
I just ran into this and made a discovery I thought I would share. In iOS 8 layout passes for the sub views of the main view will be done whenever the keyboard is about to appear or about to go away. These passes do not get done on iOS 7. So if you try to animate a sub view of the main view in keyBoardWillShow or keyboardWillChangeFrame the animation will get undone by the layout pass and your sub views that you tried to animate will move back to their original position. That is why keyboardDidChangeFrame works to animate the subviews and keyboardWillChangeFrame does not.
Something odd I noted as well is the timing of when these calls are made. It seems the first time that the keyboard appears after the app is launched, the call to keyboardDidChangeFrame happens too late to be able to animate with the keyboard so they slide up together, but on second and subsequent times the keyboard shows, the call to keyboardDidChangeFrame happens sooner and it seems you can actually animate the views along with the keyboard.
I must note that I am using C# and Xamarin as my development platform for iOS, so this may be different when using Swift or Obj-C.
You can use an accessoryView, which will attach itself to the top of the keyboard. Alternatively, if you want more power over customization, you can use notifications as explained by #pankaj_wadwha to fetch the frame information. Bonus: you can also get the animation information (such as speed) so your view moves alongside the keyboard perfectly.
I've just found an annoying bug with the new keyboardDismissMode property of the scroll view. When using this with a text view with the value UIScrollViewKeyboardDismissModeInteractive and the keyboard is dismissed the scroll view seems to jump up to the top before it continues to decelerate.
I've filed a bug report with Apple but need a workaround. I've tried the DAKeyboardControl without the new iOS7 support which behind the scenes is using the keyboardDismissMode and it still does it which to me indicates this is a much deeper problem.
Any suggestions?
for this issue better you code with scrollviewDelegete and simply mention when you want dismiss keyboard through ResignFirstResponder
Does seem to be a bug or just a non-ideal default state. But based on the code in the test project something like the below may work after some finer tuning.
There are two problems with the sample code, one is that you aren't doing anything about the size of the text when the keyboard does appear, so you can't use or see the text under the keyboard. There are other solutions but a quick and dirty solution is to change the frame size (in a submission app I would also grab the animation info and animate the view frame change to match the keyboard animation which is beyond the scope of this question). You do that in 'willShow' or the like, and bring it back in 'didHide' or the like.
Then, the content offset is fudged when its hidden and there does appear to be some strange states while you are dragging it offscreen before and around your callbacks for hiding and scroll view changes. I just save the state and "fix" it once the keyboard goes away and I've updated the text view.
I created a few properties and an outlet in the storyboard to fudge with the text view.
- (void) viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
}
- (void) keyboardWillShow:(NSNotification *)notification
{
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height -= size.height;
self.textView.frame = rect;
}
- (void)keyboardDidHide:(NSNotification *)notification
{
NSLog(#"====== keyboardDidHide =======");
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height += size.height;
self.textView.frame = rect;
self.hidingKeyboard = YES;
}
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"%f", scrollView.contentOffset.y);
if(self.hidingKeyboard == YES)
{
scrollView.contentOffset = self.lastOffset;
self.hidingKeyboard = NO;
NSLog(#"====== reset =======");
}
else
self.lastOffset = scrollView.contentOffset;
}
Im curious about a feature in iOS. Please help me out here if you can.
Scenario: I am using a text box where name is entered. Its on lower half of the screen. Just below the text box is a label which displays the number of characters remaining(e.g.like in a twitter feed).
Problem: When i place the text box in upper half of the screen. both the text field and label are visible. But when I place them in lower half, the apple keyboard covers the label part.
Is there a way where I control the area covered in such a way that the label below is also visible?
I hope I have made myself clear enough.
Thanks.
Here i have used delegate method for UITextView Same way you can do for UITextField
-Here in this code when user starts entering values in textview it makes your view's hight lesser then its original with animation
-When user Ends Entering values, it will make your view's size original size.
-(void)textViewDidBeginEditing:(UITextView *)textView
{
CGRect frame = self.view.frame;
frame.origin.y = -100;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void)textViewDidEndEditing:(UITextView *)textView
{
CGRect frame = self.view.frame;
frame.origin.y = 0;
[self.view setFrame:frame];
[UIView commitAnimations];
}
If you want to know about delegates this link helps you
Well in that case you have to move the textbox when the keyboard pops up.You can have the notification registered to know when the keyboard pops up and a scrollview to scroll the whole content up the screen can do the job for you
See this question,It explains well how to manage something like this
AFAIK You can't control the size of iOS native Keyboard, all you can and should be doing is, making them a subivew of a scroll view and scroll it up.
So the usual practice go something like this.
Subscribe to the Keyboard notification. UIKeyboardWillShowNotification
In the method which the Notification listener will be invoking, set the scrollView's content size accordingly and set the content offset.
self.scrollView.contentSize = CGSizeMake(320, 267);
self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, localKeyboardFrame.size.height, 0);
[self.scrollView scrollRectToVisible:<rect of view you want to scroll to> animated:YES];
Undo the changes when the keyboard hides, with the help of appropriate notification.UIKeyboardWillHideNotification
self.scrollView.contentInset = UIEdgeInsetsZero
And here is iOS Human Interface Guide's explanation on it.
Add following to your viewDidLoad Method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showKeyboard) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideKeyboard) name:UIKeyboardWillHideNotification object:nil];
And after that-- Declare the following 2 methods in your .m file
-(void)showKeyboard {
[UIView beginAnimations:#"slide" context:nil];
[UIView setAnimationDuration:0.3];
self.view.transform = CGAffineTransformMakeTranslation(0, -100);
[UIView commitAnimations]; }
-(void)hideKeyboard {
[UIView beginAnimations:#"slide" context:nil];
[UIView setAnimationDuration:0.1];
self.view.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations]; }