当键盘出现时,如何滚动 UIScrollView?

我的代码有点问题。我试图移动的 UIScrollView时,我编辑的 UITextField应该是隐藏的键盘弹出。

我现在正在移动主框架,因为我不知道如何在代码中“向上滚动”。 所以,我做了一点代码,它的工作很好,但当我编辑一个 UItextfield 和我切换到另一个 UITextField没有按下“返回”按钮的主视图去 waaayyyy 到远。

我用我的变量 size,length 和 textFieldRect.Orig.y 做了一个 NSLog(),如下所示。当我把两个 UITextField放在同一个地方(y 原点) ,我做这个特定的“开关”(没有按返回键) ,我得到相同的数字,而我的代码工作良好的第一个 UITextField编辑,但不是第二次编辑。

看看这个:

- (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];
}

有什么想法吗?

132287 次浏览

可以使用 UIScrollView中的属性 contentOffset滚动,例如,

CGPoint offset = scrollview.contentOffset;
offset.y -= KEYBOARD_HEIGHT + 5;
scrollview.contentOffset = offset;

还有一个方法来做动画滚动。

至于第二次编辑不能正确滚动的原因,可能是因为您似乎认为每次开始编辑时都会出现一个新键盘。您可以尝试检查,如果您已经调整了“键盘”可见的位置(同样检查键盘可见性在恢复它之前的时刻)。

一个更好的解决方案可能是监听键盘通知,例如:

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];

这是我一直在用的,很简单,效果也很好。

#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);
}];
}

使用 UITextField委托调用 textFieldDidBeginEditing:将视图向上移动,并添加一个通知观察者,以便在键盘隐藏时将视图恢复到正常状态:

-(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];
}

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){


}];
}

我会这么做。虽然有很多代码,但它确保了当前焦点的 textField 垂直居中于“可用空间”:

- (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;

请注意,- (IBAction)textFieldGotFocus:操作被连接到每个 textField 的 DidBeginEditing状态。

另外,从键盘通知中获得动画持续时间,并将其用于 scrollview 动画,而不是固定值,也会更好一些,但是告我吧,这对我来说已经足够好了;)

苹果推荐的方法是改变 UIScrollViewcontentInset。这是一个非常优雅的解决方案,因为您不必干扰 contentSize。 下面的代码是从 键盘编程指南中复制的,其中解释了该问题的处理方法。你应该去看看。

// 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;
}

快速版:

func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardAppear(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDisappear(_:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}


// Don't forget to unregister when done
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}


@objc func onKeyboardAppear(_ notification: NSNotification) {
let info = notification.userInfo!
let rect: CGRect = info[UIKeyboardFrameBeginUserInfoKey] as! CGRect
let kbSize = rect.size


let insets = UIEdgeInsetsMake(0, 0, kbSize.height, 0)
scrollView.contentInset = insets
scrollView.scrollIndicatorInsets = insets


// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
var aRect = self.view.frame;
aRect.size.height -= kbSize.height;


let activeField: UITextField? = [addressTextView, servicePathTextView, usernameTextView, passwordTextView].first { $0.isFirstResponder }
if let activeField = activeField {
if !aRect.contains(activeField.frame.origin) {
let scrollPoint = CGPoint(x: 0, y: activeField.frame.origin.y-kbSize.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}


@objc func onKeyboardDisappear(_ notification: NSNotification) {
scrollView.contentInset = UIEdgeInsets.zero
scrollView.scrollIndicatorInsets = UIEdgeInsets.zero
}

对于这些东西,不需要大量的编码,它很容易像下面的代码:-

您的所有文本归档在 UIScrollview 从笔尖像这样的形象:-

enter image description here

YourViewController.h

@interface cntrInquiryViewController : UIViewController<UIScrollViewDelegate,UITextFieldDelegate>
{
IBOutlet UITextField *txtName;
IBOutlet UITextField *txtEmail;
IBOutlet UIScrollView *srcScrollView;
}
@end

从 NIB 连接 IBOutlet,还可以从 NIB 连接 UItextfield 的每个委托和 scrollview 委托

-(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;
}

注意 如果文本文件委托没有连接,那么没有一个方法正常工作请确保所有 iBOular 和委托正确连接

我在苹果代码中唯一要更新的是 keyboardWillBeHidden: 方法,以提供平滑的转换。

// 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;


}

这里所有的答案似乎都忽略了景观的可能性。如果您希望在设备旋转到横向视图时这样做,那么您将面临问题。

这里的诀窍在于,尽管视图知道方向,但键盘不知道。这意味着在横向,键盘宽度实际上是它的高度,反之亦然。

为了修改 Apple 推荐的更改内容插入的方法并使其支持横向定位,我建议使用以下方法:

// 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;
}

这里需要注意的部分如下:

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;
}

它的作用是检测设备的方向。如果它是横向的,它将“交换”键盘大小变量的宽度和高度值,以确保在每个方向使用正确的值。

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;

然后用 scrollView 和提交按钮的名称将其添加到视图控制器中

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[iOSTextFieldHandler TextboxKeyboardMover:containingScrollView tf:textField btn:btnSubmit];
}

以下是我的解决方案(5个步骤)

步骤1: 添加一个观察器来捕捉 UITEXTFIELD 或 UITEXTVIEW ShoudBeginEditing (其中对象被初始化或 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;
}

步骤3: (步骤1调用)分配当前 UITEXTFIELD 或 UITEXTVIEW 的方法

-(void) updateActiveField: (id) sender {
activeField = [sender object];
}

步骤4: 添加键盘观察器 UIKeyboardWillShowNotification (与步骤1相同的位置)

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification 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);


_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;
}
}

步骤5: 添加键盘观察器 UIKeyboardWillHideNotification (与步骤1相同的位置)

    [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];

方法:

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.layoutPanel.contentInset = _currentEdgeInsets;
self.layoutPanel.scrollIndicatorInsets = _currentEdgeInsets;
self.layoutPanel.scrollEnabled = YES;
}

记住要除掉观察者!

苹果公司的建议在 斯威夫特 + 中重新编码使用 UIScrollView 和 iOS 中的自动布局(基于以下链接: link 1连结2连结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)
}


}

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)
}
}

也许你想让你的 UITextField 始终可见:

func textViewDidChange(textView: UITextView) {
self.scrollView?.scrollSubviewToBeVisible(textView, animated: false)
}

我刚刚在 Xcode 7(beta 6)上用 Swift 2.0 for iOS9实现了这一点,在这里可以很好地工作。

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
}

为 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
}

我使用了 Sudheer Palhuri 提供的答案 https://stackoverflow.com/users/2873919/sudheer-palchuri Https://stackoverflow.com/a/32583809/6193496

在 ViewDidLoad 中,注册通知:

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil)

添加以下观察器方法,当键盘出现时自动滚动。

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
}

This is the final code with improvements in 斯威夫特

    //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
}

我发现上面的答案已经过时了,滚动的时候也不完美。

Here's a swift version.

它会滚动到 textField 的正下方,没有多余的空间。它会恢复到它最初出现时的样子。

//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)
}

这里有一个与 Swift 3兼容的答案,也将与导航控制器中的视图控制器一起工作-因为它们将更改滚动视图 contentInset.top属性。

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
}

My solution has 4 step:
- 步骤1: 当键盘出现时,该函数将监听

- (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;
}

- 第二步: 当键盘消失时,该函数会监听

- (void)keyboardWillHide:(NSNotification *)notification {
//top: 64 for navigatiob bar
UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, 0, 0);
[_editScrollView setContentInset: contentInsets];
[_editScrollView setScrollIndicatorInsets: contentInsets];
}

- 第3步: 将这些功能添加到通知中心:

- (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];
}

- 第4步: 删除监听时,视图控制器消失

- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

在 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)
}

解决方案:

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}


func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(notification:)),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide(notification:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}


@objc func keyboardWillShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardInfo = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] 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
}


@objc func keyboardWillHide(notification: NSNotification) {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
}

Swift 4.2 解决方案,考虑到 UITToolbar 和 UITabBar 的可能高度。

private func setupKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIControl.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIControl.keyboardWillHideNotification, object: nil)
}


@objc func keyboardWillShow(_ notification: Notification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size


let tabbarHeight = tabBarController?.tabBar.frame.size.height ?? 0
let toolbarHeight = navigationController?.toolbar.frame.size.height ?? 0
let bottomInset = keyboardSize.height - tabbarHeight - toolbarHeight


scrollView.contentInset.bottom = bottomInset
scrollView.scrollIndicatorInsets.bottom = bottomInset
}


@objc func keyboardWillHide(_ notification: Notification) {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
}

而且,如果你的目标是 < iOS9,你必须在某个时候注销观察者(感谢 )

最简单的解决方案之一是使用以下协议:

protocol ScrollViewKeyboardDelegate: class {
var scrollView: UIScrollView? { get set }


func registerKeyboardNotifications()
func unregisterKeyboardNotifications()
}


extension ScrollViewKeyboardDelegate where Self: UIViewController {
func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillChangeFrameNotification,
object: nil,
queue: nil) { [weak self] notification in
self?.keyboardWillBeShown(notification)
}


NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillHideNotification,
object: nil,
queue: nil) { [weak self] notification in
self?.keyboardWillBeHidden(notification)
}
}


func unregisterKeyboardNotifications() {
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}


func keyboardWillBeShown(_ notification: Notification) {
let info = notification.userInfo
let key = (info?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)
let aKeyboardSize = key?.cgRectValue


guard let keyboardSize = aKeyboardSize,
let scrollView = self.scrollView else {
return
}


let bottomInset = keyboardSize.height
scrollView.contentInset.bottom = bottomInset
scrollView.scrollIndicatorInsets.bottom = bottomInset
if let activeField = self.view.firstResponder {
let yPosition = activeField.frame.origin.y - bottomInset
if yPosition > 0 {
let scrollPoint = CGPoint(x: 0, y: yPosition)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}


func keyboardWillBeHidden(_ notification: Notification) {
self.scrollView?.contentInset = .zero
self.scrollView?.scrollIndicatorInsets = .zero
}
}


extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }
return subviews.first(where: {$0.firstResponder != nil })
}
}

当您想要使用这个协议时,您只需要遵守它,并在您的控制器中分配您的滚动视图,如下所示:

class MyViewController: UIViewController {
@IBOutlet var scrollViewOutlet: UIScrollView?
var scrollView: UIScrollView?


public override func viewDidLoad() {
super.viewDidLoad()


self.scrollView = self.scrollViewOutlet
self.scrollView?.isScrollEnabled = true
self.registerKeyboardNotifications()
}


extension MyViewController: ScrollViewKeyboardDelegate {}


deinit {
self.unregisterKeyboardNotifications()
}


}

基于以上 Masa 溶液的 Swift 5 解决方案-相关变化:

  • 使用 keyboardFrameEndUserInfoKey代替 因为 keyboardFrameBeginUserInfoKey可以先显示返回的其他值 as described for example here: keyboard height varies when appearing
  • using "will" instead of "did" notifications plus changing it to Swift 5 keys names: UIResponder.keyboardWillShowNotification/UIResponder.keyboardWillHideNotification instead of NSNotification.Name.UIKeyboardDidShow/NSNotification.Name.UIKeyboardDidHide

密码:

override func viewDidLoad() {
super.viewDidLoad()
registerForKeyboardNotifications()
}


func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardAppear(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDisappear(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}


@objc func onKeyboardAppear(_ notification: NSNotification) {
guard let info = notification.userInfo, let kbSize = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size else { return }


let insets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)


scrollView.contentInset = insets
scrollView.scrollIndicatorInsets = insets


//Other changes if needed
}


deinit {
NotificationCenter.default.removeObserver(self)
}
    self.stackView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: height, right: 0)
self.stackView.scrollIndicatorInsets = self.stackView.contentInset
self.stackView.scrollRectToVisible(CGRect(x: 0, y: yPosition , width: Int(self.view.bounds.width), height: 80), animated: true)

键盘高度键盘高度

y position - cursor position

高度: 80-从键盘顶部光标应该在哪里。 在视图中添加键盘通知已加载,如果键盘存在,请调用此函数,并显示键盘高度和光标高度。我在 scrollView 中使用它来实现 richEditorTextView。