可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm having trouble with my code. I'm trying to move the UIScrollView when I'm editing an UITextField that should be hidden by the keyboard pop.
I'm moving the main frame right now because I don't know how to 'scroll up' in the code. So, I did a little bit of code, it's working fine but when I edit an UItextfield and I switch to another UITextField without pressing on the 'return' button the main view goes waaayyyyy to far up.
I did an NSLog() with my variables size, distance and textFieldRect.origin.y as you can see below. When I put two UITextField at the same place (y origin) and I do this particular 'switch' (wihtout pressing return) , I get the same numbers, whereas my code worked fine for the first UITextField editing but not for the second editing.
Check this out:
- (void)textFieldDidBeginEditing:(UITextField *)textField { { int size; CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField]; size = textFieldRect.origin.y + textFieldRect.size.height; if (change == FALSE) { size = size - distance; } if (size < PORTRAIT_KEYBOARD_HEIGHT) { distance = 0; } else if (size > PORTRAIT_KEYBOARD_HEIGHT) { distance = size - PORTRAIT_KEYBOARD_HEIGHT + 5; // +5 px for more visibility } NSLog(@"origin %f", textFieldRect.origin.y); NSLog(@"size %d", size); NSLog(@"distance %d", distance); CGRect viewFrame = self.view.frame; viewFrame.origin.y -= distance; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION]; [self.view setFrame:viewFrame]; [UIView commitAnimations]; change = FALSE; } - (void)textFieldDidEndEditing:(UITextField *)textField { change = TRUE; CGRect viewFrame = self.view.frame; viewFrame.origin.y += distance; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION]; [self.view setFrame:viewFrame]; [UIView commitAnimations]; }
Any ideas ?
回答1:
The recommended way from Apple is to change the contentInset of the UIScrollView. It is a very elegant solution, because you do not have to mess with the contentSize. Following code is copied from the Keyboard Programming Guide, where the handling of this issue is explained. You should have a look into it.
// 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. - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible // Your application might not need or want this behavior. CGRect aRect = self.view.frame; aRect.size.height -= kbSize.height; if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height); [scrollView setContentOffset:scrollPoint animated:YES]; } } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; }
回答2:
I just implemented this with Swift 2.0 for iOS9 on Xcode 7 (beta 6), works fine here.
override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) registerKeyboardNotifications() } func registerKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil) } deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } func keyboardWillShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets var viewRect = view.frame viewRect.size.height -= keyboardSize.height if CGRectContainsPoint(viewRect, textField.frame.origin) { let scrollPoint = CGPointMake(0, textField.frame.origin.y - keyboardSize.height) scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = UIEdgeInsetsZero scrollView.scrollIndicatorInsets = UIEdgeInsetsZero }
Edited for Swift 3
Seems like you only need to set the contentInset and scrollIndicatorInset with Swift 3, the scrolling/contentOffset is done automatically..
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) registerKeyboardNotifications() } func registerKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } func keyboardWillShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue let keyboardSize = keyboardInfo.cgRectValue.size let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = .zero scrollView.scrollIndicatorInsets = .zero }
回答3:
All the answers here seem to forget about landscape possibilities. If you would like this to work when the device is rotated to a landscape view, then you will face problems.
The trick here is that although the view is aware of the orientation, the keyboard is not. This means in Landscape, the keyboards width is actually its height and visa versa.
To modify Apples recommended way of changing the content insets and get it support landscape orientation, I would recommend using the following:
// 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. - (void)keyboardWasShown:(NSNotification*)aNotification { UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) { CGSize origKeySize = keyboardSize; keyboardSize.height = origKeySize.width; keyboardSize.width = origKeySize.height; } UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0); scroller.contentInset = contentInsets; scroller.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible // Your application might not need or want this behavior. CGRect rect = scroller.frame; rect.size.height -= keyboardSize.height; NSLog(@"Rect Size Height: %f", rect.size.height); if (!CGRectContainsPoint(rect, activeField.frame.origin)) { CGPoint point = CGPointMake(0, activeField.frame.origin.y - keyboardSize.height); NSLog(@"Point Height: %f", point.y); [scroller setContentOffset:point animated:YES]; } } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; }
The part to pay attention to here is the following:
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) { CGSize origKeySize = keyboardSize; keyboardSize.height = origKeySize.width; keyboardSize.width = origKeySize.height; }
What is does, is detects what orientation the device is in. If it is landscape, it will 'swap' the width and height values of the keyboardSize variable to ensure that the correct values are being used in each orientation.
回答4:
For this stuff no need to lot's of coding it's very easy like below code:-
your all textfiled in UIScrollview from nib like this image:-

YourViewController.h
@interface cntrInquiryViewController : UIViewController<UIScrollViewDelegate,UITextFieldDelegate> { IBOutlet UITextField *txtName; IBOutlet UITextField *txtEmail; IBOutlet UIScrollView *srcScrollView; } @end
connect IBOutlet from nib and also Connect each delegate of UItextfiled and scrollview delegate from NIB
-(void)viewWillAppear:(BOOL)animated { srcScrollView.contentSize = CGSizeMake(320, 500); [super viewWillAppear:YES]; } -(void)textFieldDidBeginEditing:(FMTextField *)textField { [srcScrollView setContentOffset:CGPointMake(0,textField.center.y-140) animated:YES];//you can set your y cordinate as your req also } -(BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; [srcScrollView setContentOffset:CGPointMake(0,0) animated:YES]; return YES; }
NOTE if Text-filed delegate not connected then no one method working please insure that all iBOulate and delegate connected correctly
回答5:
Apple's recommendation recoded in Swift + Using UIScrollView with Auto Layout in iOS (basing on these following links: link 1, link 2, link 3):
import UIKit class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet var t1: UITextField! @IBOutlet var t2: UITextField! @IBOutlet var t3: UITextField! @IBOutlet var t4: UITextField! @IBOutlet var srcScrollView: UIScrollView! @IBOutlet var contentView: UIView! var contentViewCoordinates: CGPoint! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. /* Constraints on content view */ let leftConstraint = NSLayoutConstraint(item:self.contentView, attribute:NSLayoutAttribute.Leading, relatedBy:NSLayoutRelation.Equal, toItem:self.view, attribute:NSLayoutAttribute.Left, multiplier:1.0, constant:0) self.view.addConstraint(leftConstraint) let rightConstraint = NSLayoutConstraint(item:self.contentView, attribute:NSLayoutAttribute.Trailing, relatedBy:NSLayoutRelation.Equal, toItem:self.view, attribute:NSLayoutAttribute.Right, multiplier:1.0, constant:0) self.view.addConstraint(rightConstraint) /* Tap gesture */ let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard") // prevents the scroll view from swallowing up the touch event of child buttons tapGesture.cancelsTouchesInView = false srcScrollView.addGestureRecognizer(tapGesture) /* Save content view coordinates */ contentViewCoordinates = contentView.frame.origin } func hideKeyboard() { t1.resignFirstResponder() t2.resignFirstResponder() t3.resignFirstResponder() t4.resignFirstResponder() } var activeField: UITextField? func textFieldDidBeginEditing(textField: UITextField) { activeField = textField } func textFieldDidEndEditing(textField: UITextField) { activeField = nil } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) let center = NSNotificationCenter.defaultCenter() center.addObserver(self, selector: "keyboardOnScreen:", name: UIKeyboardDidShowNotification, object: nil) center.addObserver(self, selector: "keyboardOffScreen:", name: UIKeyboardDidHideNotification, object: nil) } func keyboardOnScreen(notification: NSNotification){ // Retrieve the size and top margin (inset is the fancy word used by Apple) // of the keyboard displayed. let info: NSDictionary = notification.userInfo! let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize!.height, 0.0) srcScrollView.contentInset = contentInsets srcScrollView.scrollIndicatorInsets = contentInsets var aRect: CGRect = self.view.frame aRect.size.height -= kbSize!.height //you may not need to scroll, see if the active field is already visible if (CGRectContainsPoint(aRect, activeField!.frame.origin) == false) { let scrollPoint:CGPoint = CGPointMake(0.0, activeField!.frame.origin.y - kbSize!.height) srcScrollView.setContentOffset(scrollPoint, animated: true) } } // func keyboardOnScreen(aNotification: NSNotification) { // let info: NSDictionary = aNotification.userInfo! // let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size // // var bkgndRect: CGRect! = activeField?.superview?.frame // // bkgndRect.size.height += kbSize!.height // // activeField?.superview?.frame = bkgndRect // // srcScrollView.setContentOffset(CGPointMake(0.0, activeField!.frame.origin.y - kbSize!.height), animated: true) // } func keyboardOffScreen(notification: NSNotification){ let contentInsets:UIEdgeInsets = UIEdgeInsetsZero srcScrollView.contentInset = contentInsets srcScrollView.scrollIndicatorInsets = contentInsets self.srcScrollView.setContentOffset(CGPointMake(0, -self.view.frame.origin.y/2), animated: true) } }
回答6:
The only thing I would update in Apple code is the keyboardWillBeHidden: method, to provide smooth transition.
// Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; [UIView animateWithDuration:0.4 animations:^{ self.scrollView.contentInset = contentInsets; }]; self.scrollView.scrollIndicatorInsets = contentInsets; }
回答7:
Here is a Swift 3 compatible answer, that will also work with view controllers within a navigation controller - as they will change the scroll views contentInset.top property.
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.registerKeyboardNotifications() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.unregisterKeyboardNotifications() } func registerKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func unregisterKeyboardNotifications() { NotificationCenter.default.removeObserver(self) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue let keyboardSize = keyboardInfo.cgRectValue.size // Get the existing contentInset for the scrollView and set the bottom property to be the height of the keyboard var contentInset = self.scrollView.contentInset contentInset.bottom = keyboardSize.height self.scrollView.contentInset = contentInset self.scrollView.scrollIndicatorInsets = contentInset } func keyboardWillHide(notification: NSNotification) { var contentInset = self.scrollView.contentInset contentInset.bottom = 0 self.scrollView.contentInset = contentInset self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero }
回答8:
I've found the above answers have been outdated. Also not perfect when scroll.
Here's a swift version.
It will scroll right below the textField, no spare space. And It will restore to the way it was like its first appear.
//add observer override func viewDidLoad() { super.viewDidLoad() NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidHide(_:)), name: UIKeyboardDidHideNotification, object: nil) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)!.CGRectValue.size let difference = keyboardSize.height - (self.view.frame.height - inputTextField.frame.origin.y - inputTextField.frame.size.height) if difference > 0 { var contentInset:UIEdgeInsets = self.scrollView.contentInset contentInset.bottom = difference self.scrollView.contentInset = contentInset let scrollPoint = CGPointMake(0, difference) self.scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardDidHide(notification: NSNotification) { let contentInset:UIEdgeInsets = UIEdgeInsetsZero self.scrollView.contentInset = contentInset } //remove observer deinit { NSNotificationCenter.defaultCenter().removeObserver(self) }
回答9:
This is what I've been using. It's simple and it works well.
#pragma mark - Scrolling -(void)scrollElement:(UIView *)view toPoint:(float)y { CGRect theFrame = view.frame; float orig_y = theFrame.origin.y; float diff = y - orig_y; if (diff < 0) [self scrollToY:diff]; else [self scrollToY:0]; } -(void)scrollToY:(float)y { [UIView animateWithDuration:0.3f animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; self.view.transform = CGAffineTransformMakeTranslation(0, y); }]; }
Use the UITextField delegate call textFieldDidBeginEditing: to shift your view upwards, and also add a notification observer to return the view to normal when the keyboard hides:
-(void)textFieldDidBeginEditing:(UITextField *)textField { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; if (self.view.frame.origin.y == 0) [self scrollToY:-90.0]; // y can be changed to your liking } -(void)keyboardWillHide:(NSNotification*)note { [self scrollToY:0]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; }
回答10:
This is the final code with improvements in Swift
//MARK: UITextFieldDelegate func textFieldDidBeginEditing(textField: UITextField!) { //delegate method self.textField = textField } func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method textField.resignFirstResponder() return true } //MARK: Keyboard handling override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) unregisterKeyboardNotifications() } func registerKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil) } func unregisterKeyboardNotifications() { NSNotificationCenter.defaultCenter().removeObserver(self) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets var viewRect = self.view.frame viewRect.size.height -= keyboardSize.height let relativeFieldFrame: CGRect = textField.convertRect(textField.frame, toView: self.view) if CGRectContainsPoint(viewRect, relativeFieldFrame.origin) { let scrollPoint = CGPointMake(0, relativeFieldFrame.origin.y - keyboardSize.height) scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = UIEdgeInsetsZero scrollView.scrollIndicatorInsets = UIEdgeInsetsZero }
回答11:
I would do that like this. It's a lot of code but it ensures, that the textField currently in focus is is vertically centered in the 'available space':
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [[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 { [super viewWillDisappear:animated]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (void)keyboardWillShow:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; NSValue *keyBoardEndFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey]; CGSize keyboardSize = [keyBoardEndFrame CGRectValue].size; self.keyboardSize = keyboardSize; [self adjustScrollViewOffsetToCenterTextField:self.currentTextField]; } - (void)keyboardWillHide:(NSNotification *)notification { self.keyboardSize = CGSizeZero; } - (IBAction)textFieldGotFocus:(UITextField *)sender { sender.inputAccessoryView = self.keyboardAccessoryView; self.currentTextField = sender; [self adjustScrollViewOffsetToCenterTextField:sender]; } - (void)adjustScrollViewOffsetToCenterTextField:(UITextField *)textField { CGRect textFieldFrame = textField.frame; float keyboardHeight = MIN(self.keyboardSize.width, self.keyboardSize.height); float visibleScrollViewHeight = self.scrollView.frame.size.height - keyboardHeight; float offsetInScrollViewCoords = (visibleScrollViewHeight / 2) - (textFieldFrame.size.height / 2); float scrollViewOffset = textFieldFrame.origin.y - offsetInScrollViewCoords; [UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, scrollViewOffset); }completion:NULL]; } you'll need these two properties in your @interface... @property (nonatomic, assign) CGSize keyboardSize; @property (nonatomic, strong) UITextField *currentTextField;
Note that the - (IBAction)textFieldGotFocus: action is hooked up to the every textField's DidBeginEditing state.
Also it would be a little better to get the animation duration from the keyboard notification and use that for the scrollview animation instead of a fixed value, but sue me, this was good enough for me ;)
回答12:
Try this code in Swift 3:
override func viewDidAppear(_ animated: Bool) { setupViewResizerOnKeyboardShown() } func setupViewResizerOnKeyboardShown() { NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShowForResizing), name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHideForResizing), name: Notification.Name.UIKeyboardWillHide, object: nil) } func keyboardWillShowForResizing(notification: Notification) { if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let window = self.view.window?.frame { // We're not just minusing the kb height from the view height because // the view could already have been resized for the keyboard before self.view.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: window.origin.y + window.height - keyboardSize.height) } else { debugPrint("We're showing the keyboard and either the keyboard size or window is nil: panic widely.") } } func keyboardWillHideForResizing(notification: Notification) { if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { let viewHeight = self.view.frame.height self.view.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: viewHeight) //viewHeight + keyboardSize.height } else { debugPrint("We're about to hide the keyboard and the keyboard size is nil. Now is the rapture.") } } deinit { NotificationCenter.default.removeObserver(self) }
回答13:
You actually don't need a UIScrollView to do this. I used this code and it works for me:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { if (textField==_myTextField) { [self keyBoardAppeared]; } return true; } -(void)textFieldDidEndEditing:(UITextField *)textField { if (textField==_myTextField) { [self keyBoardDisappeared]; } } -(void) keyBoardAppeared { CGRect frame = self.view.frame; [UIView animateWithDuration:0.3 delay:0 options: UIViewAnimationCurveEaseOut animations:^{ self.view.frame = CGRectMake(frame.origin.x, frame.origin.y-215, frame.size.width, frame.size.height); } completion:^(BOOL finished){ }]; } -(void) keyBoardDisappeared { CGRect frame = self.view.frame; [UIView animateWithDuration:0.3 delay:0 options: UIViewAnimationCurveEaseOut animations:^{ self.view.frame = CGRectMake(frame.origin.x, frame.origin.y+215, frame.size.width, frame.size.height); } completion:^(BOOL finished){ }]; }
回答14:
You can scroll by using the property contentOffset in UIScrollView, e.g.,
CGPoint offset = scrollview.contentOffset; offset.y -= KEYBOARD_HEIGHT + 5; scrollview.contentOffset = offset;
There's also a method to do animated scrolling.
As for the reason why your second edit is not scrolling correctly, it could be because you seem to assume that a new keyboard will appear every time editing starts. You could try checking if you've already adjusted for the "keyboard" visible position (and likewise check for keyboard visibility at the moment before reverting it).
A better solution might be to listen for the keyboard notification, e.g.:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
回答15:
I know it's an old question now but i thought it might help others. I wanted something a little easier to implement for a few apps i had, so i made a class for this. You can download it here if you want: https://github.com/sdernley/iOSTextFieldHandler
It's as simple as setting all of the UITextFields to have a delegate of self
textfieldname.delegate = self;
And then adding this to your view controller with the name of your scrollView and submit button
- (void)textFieldDidBeginEditing:(UITextField *)textField { [iOSTextFieldHandler TextboxKeyboardMover:containingScrollView tf:textField btn:btnSubmit]; }
回答16:
The following is my solutions which works ( 5 steps )
Step1: Add an observer to catch which UITEXTFIELD or UITEXTVIEW ShoudBeginEditing ( where object is inited or ViewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateActiveField:) name:@"UPDATE_ACTIVE_FIELD" object:nil];
Step2: Post a notification when ..ShouldBeginEditing with OBJECT of UITEXTFIELD or UITEXTVIEW
-(BOOL)textViewShouldBeginEditing:(UITextView *)textView { [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_ACTIVE_FIELD" object:textView]; return YES; }
Step3: The method that (Step1 calles ) assigns the current UITEXTFIELD or UITEXTVIEW
-(void) updateActiveField: (id) sender { activeField = [sender object]; }
Step4: Add Keyboard observer UIKeyboardWillShowNotification ( same place as Step1 )
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
and method:
// Called when the UIKeyboardDidShowNotification is sent. - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); _currentEdgeInsets = self.layoutPanel.contentInset; // store current insets to restore them later self.layoutPanel.contentInset = contentInsets; self.layoutPanel.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible CGRect aRect = self.view.frame; aRect.size.height -= kbSize.height; UIWindow *window = [[UIApplication sharedApplication] keyWindow]; CGPoint p = [activeField convertPoint:activeField.bounds.origin toView:window]; if (!CGRectContainsPoint(aRect, p) ) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y +kbSize.height); [self.layoutPanel setContentOffset:scrollPoint animated:YES]; self.layoutPanel.scrollEnabled = NO; } }
Step5: Add Keyboard observer UIKeyboardWillHideNotification ( same place as step 1 )
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
and method:
// Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { self.layoutPanel.contentInset = _currentEdgeInsets; self.layoutPanel.scrollIndicatorInsets = _currentEdgeInsets; self.layoutPanel.scrollEnabled = YES; }
Remember to remove observers!
回答17:
Use following extension if you don't want to calculate too much:
func scrollSubviewToBeVisible(subview: UIView, animated: Bool) { let visibleFrame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset) let subviewFrame = subview.convertRect(subview.bounds, toView: self) if (!CGRectContainsRect(visibleFrame, subviewFrame)) { self.scrollRectToVisible(subviewFrame, animated: animated) } }
And maybe you want keep your UITextField always visible:
func textViewDidChange(textView: UITextView) { self.scrollView?.scrollSubviewToBeVisible(textView, animated: false) }
回答18:
I used this answer supplied by Sudheer Palchuri https://stackoverflow.com/users/2873919/sudheer-palchuri https://stackoverflow.com/a/32583809/6193496
In ViewDidLoad, register the notifications:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil)
Add below observer methods which does the automatic scrolling when keyboard appears.
func textFieldShouldReturn(textField: UITextField) -> Bool { textField.resignFirstResponder() return true } func keyboardWillShow(notification:NSNotification){ var userInfo = notification.userInfo! var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue() keyboardFrame = self.view.convertRect(keyboardFrame, fromView: nil) var contentInset:UIEdgeInsets = self.scrollView.contentInset contentInset.bottom = keyboardFrame.size.height self.scrollView.contentInset = contentInset } func keyboardWillHide(notification:NSNotification){ var contentInset:UIEdgeInsets = UIEdgeInsetsZero self.scrollView.contentInset = contentInset }
回答19:
My solution has 4 step:
- Step 1: the function listens when the keyboard appears
- (void)keyboardWasShown:(NSNotification *)notification { // Get the size of the keyboard. CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; //top: 64 for navigation bar, 0 for without navigation UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, keyboardSize.height, 0); _scrollView.contentInset = contentInsets; _scrollView.scrollIndicatorInsets = contentInsets; }
- Step 2: the function listens when the keyboard disappears
- (void)keyboardWillHide:(NSNotification *)notification { //top: 64 for navigatiob bar UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, 0, 0); [_editScrollView setContentInset: contentInsets]; [_editScrollView setScrollIndicatorInsets: contentInsets]; }
- Step 3: add these functions to the notification center:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; }
- Step 4: remove listen when view controller disappers
- (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil]; }