I'm writing an iPhone app and I have some UITextfields that will get covered when the keyboard appears; therefore I put the UITextFields in a UIScrollView, and set myself as the delegate so that when the the keyboard becomes active, this method gets called:
-(void) textFieldDidBeginEditing:(UITextField *)textField
{
self.myScrollView.contentSize = CGSizeMake(self.myScrollView.contentSize.width, 560);
[self.myScrollView setContentOffset:CGPointMake(0, 200) animated:YES];
}
Note, that I am making the contentSize taller so that even once the textfields have been brought into focus, the user can still scroll.
Similarly, when the textfield resigns first responder status, this method gets called:
-(void) textFieldDidEndEditing:(UITextField *)textField
{
[self.myScrollView setContentOffset:CGPointMake(0, 0) animated:YES];
self.myScrollView.contentSize = CGSizeMake(self.myScrollView.contentSize.width,self.myScrollView.frame.size.height);
}
Note, that once the keyboard has been lowered, all the content is visible, thus there is no need for scrolling to be enabled (contentSize = frame.size).
However, my problem is because I am setting the contentSize right after the contentOffset is set, the setContentOffset animation does not have time to be completed. Instead, the animation looks extremely jerky. Any suggestions?
Working with UIKeyboardDidShowNotification and UIKeyboardWillHideNotification is a good idea:
Step 1: listen to the two notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
Step 2: do something while the keyboard did show
- (void)keyboardDidShow:(NSNotification*)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
BOOL Need_Resize; // judge by yourself
if (Need_Resize) {
double offset; // judge by yourself
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.view setCenter:CGPointMake(self.view.center.x, self.view.center.y - offset];
[UIView commitAnimations];
}
}
Step 3: do something while the keyboard did hide
// in animation code, set the view back to the original place
[self.view setCenter:CGPointMake(self.view.center.x, self.view.frame.size.height/2)];
This solution does not need UIScrollView, just adjust the view's place, with animation, it looks great enough.
What about this?
Don't know if it will work but just from the top of my head:
[UIView animateWithDuration:0.5
animations:^{
self.myScrollView.contentSize = CGSizeMake(self.myScrollView.contentSize.width, 560);
} completion:^(BOOL finished) {
[self.myScrollView setContentOffset:CGPointMake(0, 200) animated:YES];
}];
Good luck
Instead of setting the content offset you could try using scrollRectToVisible:animated: and pass it the rect of your textfield or CGRectZero depending on which direction you're going.
Related
I have a scrollview on my view in that i have all the sub views as shown bellow
UIView
Scroll View
1.ImageView
2.Table view
3.Text View
√√ To move the scroll view when keyboard appears / dismiss I have implemented logic in textview delegate metods as follows √√
- (void)textViewDidBeginEditing:(UITextView *)textView;
{
//To move the view down
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationBeginsFromCurrentState:YES];
scrollView.frame = CGRectMake(scrollView.frame.origin.x, (scrollView.frame.origin.y - 120), scrollView.frame.size.width, scrollView.frame.size.height);
[UIView commitAnimations];
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
//To move the view down
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationBeginsFromCurrentState:YES];
scrollView.frame = CGRectMake(scrollView.frame.origin.x, (scrollView.frame.origin.y + 120), scrollView.frame.size.width, scrollView.frame.size.height);
[UIView commitAnimations];
}
√√ This method helps alot to move the view up/down with respcet to keyboard,
But here is the problem of scrolling.
User can not scroll all the view in presensec of key board. The view scrolls up to some position as follows, we can't see the picture / first column of table.
If the user want to show the first column / profile pic in presence of keyboard doesn't possible. how to fix theis issue.
first set the scroll content like
scrollView.contentSize=CGSizeMake(320, 600);
and then animate there content when textView delegate method is called like
- (void)textViewDidBeginEditing:(UITextView *)textView;
{
[scrollView setContentOffset:CGPointMake(0, 120 ) animated:YES];
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
[scrollView setContentOffset:CGPointMake(0, 0 ) animated:YES];
}
First of all, you don't want to use the textViewDidBeginEditing: method to show/hide the keyboard. You should register for the appropriate notifications to do so. This could be placed in your viewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
Then if the keyboard is shown, make sure to not move your entire scroll view but shrink it's size.
- (void)keyboardWillShow:(NSNotification *)notif
{
CGSize kbSize = [[[notif userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
double duration = [[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Shrink the scroll view's content
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// Scroll the text field to be visible (use own animation with keyboard's duration)
CGRect textFieldRect = [_activeTextField convertRect:_activeTextField.bounds toView:self.scrollView];
[UIView animateWithDuration:duration animations:^{
[self.scrollView scrollRectToVisible:CGRectInset(textFieldRect, 0.0, -10.0) animated:NO];
}];
}
This code has some additional functionality because I also had to move the content of the scroll view, but works to get you started. Then when the keyboard hides...
- (void)keyboardWillHide:(NSNotification *)notif
{
double duration = [[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Set content inset back to default with a nice little animation
[UIView animateWithDuration:duration animations:^{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}];
}
Apply content offset when keyboard is shown. And revert back when its hidden.
[scrollView setContentOffset:CGPointMake(0.0f, 120.0f) animated:YES];
After hiding the keyboard call.
[scrollView setContentOffset:CGPointZero animated:YES];
Just Change The ContentOffset of ScrollView No need to change the origin of ScrollView as you were doing in your code doing so scrollview will always scroll.
Doing so you need to set the ContentSize of upur ScrollViw atleast 550 just check what would be the Appropriate ContentSize. I have created a method in which you just need to call my method and pass required parameter appropriately
And One more Thing Set The ContentSIze Of UISCrollView More than the Height of It's SuperView at where you have created theScrollViewor inViewViewAppearmethod.
(CurrentView)
[contentScrollView setContentSize:CGSizeMake(320, 620)];
And Call This Method As you click on TextField or on the Screen(In Touch Ended when you need to readjust ContentOffset)
- (void)adjustContetOffset:(BOOL)yes withOffSet:(CGFloat)offSet
{
contentOffSetY = offSet ;
//By this you can track more suppose you have change the Orientation of Device then set Appropriate Offset.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
if(yes){
isScrolledUp = TRUE;
contentScrollView.contentOffset = CGPointMake(0, contentOffSetY);
}
else
{
isScrolledUp = FALSE;
//by this flag you can Track that OffSet of ScrollView has changed
contentScrollView.contentOffset = CGPointMake(0, contentOffSetY);
}
[UIView commitAnimations];
}
//this methods 'll call whenever you 'll touch the SCreen
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if(isScrolledUp){
[self adjustContetOffset:NO withOffSet: 0.0];
}
[searchTextField resignFirstResponder];
}
I hope it'll helpful to you.
I currently have a UITextField on top of a keyboard. When you tap it, it should stick on top of the keyboard and move up smoothly. I don't know the exact duration and animation type of the keyboard, so it's really bumpy. Here's what I have:
[theTextView resignFirstResponder];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
// Frame changes go here (move down 216px)
[UIView commitAnimations];
[theTextView becomeFirstResponder];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
// Frame changes go here (move up 216px)
[UIView commitAnimations];
If anyone has done something like this before, I would like to know the settings you used to make the animation smooth and make it appear that the bar is "stuck" to the top of the keyboard.
UIKit posts UIKeyboardWillShowNotification when it shows the keyboard, and UIKeyboardWillHideNotification when it hides the keyboard. These notifications contain everything you need to properly animate your UITextField.
Let's say your UITextField is in a property called called myTextField.
First, you need to register for the notifications somewhere. Where you register depends on what object is responsible for moving myTextField. In my project, the field's superview is responsible, and since I load my UI from a nib, I do it in the superview's awakeFromNib:
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHideOrShow:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHideOrShow:) name:UIKeyboardWillShowNotification object:nil];
}
If you use a UIViewController to move the field around, you'll probably want to do it in viewWillAppear:animated:.
You should unregister in your dealloc or viewWillDisappear:animated::
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Of course the tricky bit is in the keyboardWillHideOrShow: method. First I extract the animation parameters from the notification:
- (void)keyboardWillHideOrShow:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
The keyboardFrame is in the global coordinate system. I need to convert the frame to the same coordinate system as myTextField.frame, and myTextField.frame is in the coordinate system of myTextField.superview:
CGRect keyboardFrameForTextField = [self.myTextField.superview convertRect:keyboardFrame fromView:nil];
Next, I compute the frame that I want myTextField to move to. The bottom edge of the new frame should be equal to the top edge of the keyboard's frame:
CGRect newTextFieldFrame = self.myTextField.frame;
newTextFieldFrame.origin.y = keyboardFrameForTextField.origin.y - newTextFieldFrame.size.height;
Finally, I animate myTextField to its new frame, using the same animation parameters that the keyboard is using:
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.myTextField.frame = newTextFieldFrame;
} completion:nil];
}
Here it is all put together:
- (void)keyboardWillHideOrShow:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrameForTextField = [self.myTextField.superview convertRect:keyboardFrame fromView:nil];
CGRect newTextFieldFrame = self.myTextField.frame;
newTextFieldFrame.origin.y = keyboardFrameForTextField.origin.y - newTextFieldFrame.size.height;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.myTextField.frame = newTextFieldFrame;
} completion:nil];
}
Set your text field (or a view holding the text field) as the inputAccessoryView of the field that you are editing. It will then automatically be attached to the top of the keyboard and animate appropriately.
in order to make the UITextField to dock to keyboard (with animation), you need to do offset calculations and apply offset changes on the scroll view (assuming UITextField is placed in UIScrollView) using setContentOffset:animation: method.
Use this code in your keyboard notification:
#objc func keyboardWillShowNotification(notification: Notification) {
let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height ?? 216
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0.25
let curve = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? UInt ?? 7) << 16
tableViewBottomConstraint.constant = keyboardHeight - submitButtonContainer.frame.size.height
UIView.animate(withDuration: duration, delay: 0, options: [UIViewAnimationOptions(rawValue: curve)], animations: {
// Animation
}, completion: nil)
}
I have several UITextFields listed below each other and would like to move the active field appropriately when the keyboard slides up.
I have added my controller as an observer to UIKeyboardWillShowNotifications and that works fine when the user first taps a text field. If he taps one of the other text fields without making the keyboard go away first no UIKeyboardWillShowNotifications appears though and I get no chance to adjust the position of the new active text field. I guess it somehow makes sense that no UIKeyboardWillShowNotification appears as the keyboard just stays there but...
What to do??
I do not see how I can add my adjusting code somewhere else as I need the keyboard size information which is kept in the userInfo in the keyboard notification.
Thanks a lot,
Stine
EDIT: Maybe it will be more clear what I need if I paste some of my code here:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cellInputViewWillOpen:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cellInputViewWillClose) name:UIKeyboardWillHideNotification object:nil];
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void) cellInputViewWillOpen:(NSNotification *)aNotification {
CGRect keyboardFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];
keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, cell.frame.origin.y - ((self.tableView.frame.size.height - keyboardFrame.size.height) / 2.0f));
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, keyboardFrame.size.height - 44.0f, 0.0f);
[UIView commitAnimations];
}
- (void) cellInputViewWillClose {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
self.tableView.contentInset = UIEdgeInsetsZero;
[UIView commitAnimations];
}
Use textField:didBeginEditing instead. You may need to keep track separately of if your keyboard is already showing so you don't scroll twice.
EDIT : I didn't realise you were talking about a table view, sorry. In that case you can adjust the scroll view contents as you are already doing, then use the table view scroll to index path methods when you change the selection using the table view delegate methods.
I have an editable UITextView with a couple pages worth of text. When the user taps inside and it brings up the keyboard, it hides the bottom part of the text and you can not scroll to see it.
Is there some obvious/easy/standard way to deal with this? I assume its a common issue. I assume that you have to resize the text view when the keyboard is up, or something like that?
Also, when they tap on the text view in the bottom half of the page, how to make it automatically scroll so that the line they tapped on is visible when the keyboard appears? Or will this be automatically taken care of if i resize the text view when the keyboard appears.
Thanks a lot guys
This has been discussed extensively here: How to make a UITextField move up when keyboard is present?
I personally have used Shiun's solution in the past and it works well.
UPDATE:
If you don't want to use that method, a slightly simpler method is to resize your text field when the keyboard shows. It would be better to follow the instructions on the link I posted above as the KeyboardWillShow notification will give you access to the keyboard height.
First set the delegate of the UITextField = self. Then:
-(void)textFieldDidBeginEditing:(UITextField *)textField { // This is where the keyboard becomes visible
textField.frame = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, textField.frame.size.width, textField.frame.size.height-100);
}
-(void)textFieldDidEndEditing:(UITextField *)textField { // This is where the keyboard hides itself
textField.frame = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, textField.frame.size.width, textField.frame.size.height+100);
}
You can tweak the 100 depending on your orientation etc. If you wanted to add some animations you could do:
-(void)textFieldDidBeginEditing:(UITextField *)textField { // This is where the keyboard becomes visible
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
textField.frame = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, textField.frame.size.width, textField.frame.size.height-100);
[UIView commitAnimations];
}
-(void)textFieldDidEndEditing:(UITextField *)textField { // This is where the keyboard hides itself
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
textField.frame = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, textField.frame.size.width, textField.frame.size.height+100);
[UIView commitAnimations];
}
This will nicely scroll your UITextView to the top of the keyboard when editing starts. This will also work if your UITextView have dynamic height (autogrow/autosize when typing). Tested in iOS 7.
Call your keyboard observer method and set UITextView delegate to current class:
- (void)viewDidLoad
{
...
[self observeKeyboard];
textView.delegate = (id)self;
}
Add keyboard observer for UIKeyboardDidShowNotification and UIKeyboardWillShowNotification:
- (void)observeKeyboard
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
}
Get keyboard size:
- (void)keyboardWillShow:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
_keyboardHeight = keyboardSize.height;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
_keyboardHeight = keyboardSize.height;
}
When UITextView did begin editing or value has changed call scrollKeyboardToTextView:
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[self scrollKeyboardToTextView:textView];
}
- (void)textViewDidChange:(UITextView *)textView
{
[self scrollKeyboardToTextView:textView];
}
Scroll UITextView with animation to the top of the keyboard:
- (void)scrollKeyboardToTextView:(UITextView *)textView
{
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, _keyboardHeight, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= _keyboardHeight;
CGPoint origin = textView.frame.origin;
origin.y -= self.scrollView.contentOffset.y;
origin.y += textView.frame.size.height;
CGPoint scrollPoint = CGPointMake(0.0, textView.frame.origin.y + textView.frame.size.height - (aRect.size.height));
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
I try to remove a view from it's superview after being animated offscreen. But it seems when I add the removeFromSuperview call after when the animation is supposed to end, the view would no animate at all but instead disappear instantly from the screen.
So why is there no animation when I add the [[self pickerView] removeFromSuperview]; to the method below ?
- (void) slidePickerOutView
{
[UIView beginAnimations:#"slidePickerOutView" context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDidStopSelector:#selector(slidePickerOutViewEnded:finished:context:)];
CGRect r = CGRectMake(0, 480, 320, 244);
[[self pickerView] setFrame:r];
[UIView commitAnimations];
}
- (void) slidePickerOutViewEnded:(NSString *)id finished:(BOOL) finished context:(void *) context
{
//Stop observing the 'Done' button
[[self pickerView] removeObserver:self forKeyPath:#"valueSelectDone"];
[[self pickerView] removeObserver:self forKeyPath:#"selectedValue"];
[[self pickerView] removeFromSuperview];
[self setPickerView:nil];
}
Ok, I eventually found what was causing the animation not to start. The pickerView was being observed and whenever one if it's instance variables would change of value, the parent view would get notified and would start the animation to slide the pickerview offscreen.
But since the parentView was observing both old and new value, it got notified two times, first for the old and then for the new changed value. Thus the parentView was starting the animation two times right after each other as well, apparently this was causing the animation not to start at all.
fixed like this, just by commenting out the firstline which was sending the old value to its observer each time ValueSelectDone was changing value:
//[self willChangeValueForKey:#"valueSelectDone"];
valueSelectDone = flag;
[self didChangeValueForKey:#"valueSelectDone"];