Objective c 如何以编程方式将UIScrollView移动到键盘上方控件中的焦点?

Objective c 如何以编程方式将UIScrollView移动到键盘上方控件中的焦点?,objective-c,iphone,user-interface,Objective C,Iphone,User Interface,我的UIScrollView上有6个UITextFields。现在,我可以按用户请求滚动。但当键盘出现时,一些文本字段被隐藏 这不是用户友好的 如何以编程方式滚动视图,以确保键盘不会隐藏文本字段?如果将文本字段的委托设置为程序中的控制器对象,则可以让该对象实现textfieldDiBeginEdit:和textFieldShouldReturn:方法。第一种方法可用于滚动到文本字段,第二种方法可用于向后滚动 您可以在我的博客中找到我用于此目的的代码:。我没有在UIScrollView中测试此代码

我的
UIScrollView
上有6个
UITextFields
。现在,我可以按用户请求滚动。但当键盘出现时,一些文本字段被隐藏

这不是用户友好的


如何以编程方式滚动视图,以确保键盘不会隐藏文本字段?

如果将文本字段的
委托
设置为程序中的控制器对象,则可以让该对象实现
textfieldDiBeginEdit:
textFieldShouldReturn:
方法。第一种方法可用于滚动到文本字段,第二种方法可用于向后滚动

您可以在我的博客中找到我用于此目的的代码:。我没有在
UIScrollView
中测试此代码的文本视图,但它应该可以工作。

最后,一个简单的修复:

UIScrollView* v = (UIScrollView*) self.view ;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:v];
rc.origin.x = 0 ;
rc.origin.y -= 60 ;

rc.size.height = 400;
[self.scroll scrollRectToVisible:rc animated:YES];

现在我想只有结合上面的链接,这是设置

以下是对我有用的东西。在为键盘调整视图之前,使用实例变量保存UIScrollView的偏移值,以便在UITextField返回后恢复以前的状态:

//header
@interface TheViewController : UIViewController <UITextFieldDelegate> {
    CGPoint svos;
}


//implementation
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    svos = scrollView.contentOffset;
    CGPoint pt;
    CGRect rc = [textField bounds];
    rc = [textField convertRect:rc toView:scrollView];
    pt = rc.origin;
    pt.x = 0;
    pt.y -= 60;
    [scrollView setContentOffset:pt animated:YES];           
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [scrollView setContentOffset:svos animated:YES]; 
    [textField resignFirstResponder];
    return YES;
}
//头
@视图控制器接口:UIViewController{
CGPoint-svos;
}
//实施
-(无效)textFieldDidBeginEditing:(UITextField*)textField{
svos=scrollView.contentOffset;
cgpt;
CGRect rc=[textField界限];
rc=[textField convertRect:rc-toView:scrollView];
pt=rc.原点;
pt.x=0;
pt.y-=60;
[scrollView setContentOffset:pt动画:是];
}
-(BOOL)textField应返回:(UITextField*)textField{
[scrollView setContentOffset:svos动画:是];
[textField resignFirstResponder];
返回YES;
}

我已经创建了一个通用的嵌入式UIScrollView和UITableView子类,负责将其中的所有文本字段移出键盘

当键盘即将出现时,子类将找到将要编辑的子视图,并调整其帧和内容偏移以确保视图可见,并使用与键盘弹出窗口匹配的动画。当键盘消失时,它会恢复原来的大小

它基本上适用于任何设置,无论是基于UITableView的界面,还是由手动放置的视图组成的界面

是的



到目前为止发布的答案对我不起作用,因为我有一个相当深的UIView嵌套结构。此外,我还有一个问题,就是其中一些答案只适用于特定的设备方向

这是我的解决方案,希望能让你在这件事上少浪费一些时间

我的UIViewTextView源于UIView,是一个UITextView委托,在从该UITextView的XML文件中读取了一些参数后,添加了一个UITextView(为了清晰起见,此处省略了该XML部分)

以下是专用接口定义:

#import "UIViewTextView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CGColor.h>

@interface UIViewTextView (/**/) {
  @private
  UITextView *tf;

  /*
   * Current content scroll view
   * position and frame
   */
  CGFloat currentScrollViewPosition;
  CGFloat currentScrollViewHeight;
  CGFloat kbHeight;
  CGFloat kbTop;

  /*
   * contentScrollView is the UIScrollView
   * that contains ourselves.
   */
  UIScrollView contentScrollView;
}
@end
完成后,我们需要处理键盘显示事件。在调用TextViewBeginEdit之前调用它,因此我们可以使用它来查找键盘的一些属性。本质上,我们想知道键盘的高度。不幸的是,这需要从横向模式下的宽度属性中获取:

-(void)keyboardWasShown:(NSNotification*)aNotification {
  NSDictionary* info                 = [aNotification userInfo];
  CGRect kbRect                      = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
  CGSize kbSize                      = kbRect.size;

  CGRect screenRect                  = [[UIScreen mainScreen] bounds];
  CGFloat sWidth                     = screenRect.size.width;
  CGFloat sHeight                    = screenRect.size.height;

  UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

  if ((orientation == UIDeviceOrientationPortrait)
      ||(orientation == UIDeviceOrientationPortraitUpsideDown)) {
    kbHeight     = kbSize.height;
    kbTop        = sHeight - kbHeight;
  } else {
    //Note that the keyboard size is not oriented
    //so use width property instead
    kbHeight     = kbSize.width;
    kbTop        = sWidth - kbHeight;
  }
接下来,我们需要在开始编辑时实际滚动。我们在这里这样做:

- (void) textViewDidBeginEditing:(UITextView *)textView {
  /*
   * Memorize the current scroll position
   */
  currentScrollViewPosition = contentScrollView.contentOffset.y;

  /*
   * Memorize the current scroll view height
   */
  currentScrollViewHeight   = contentScrollView.frame.size.height;

  // My top position
  CGFloat myTop    = [self convertPoint:self.bounds.origin toView:[UIApplication sharedApplication].keyWindow.rootViewController.view].y;

  // My height
  CGFloat myHeight = self.frame.size.height;

  // My bottom
  CGFloat myBottom = myTop + myHeight;

  // Eventual overlap
  CGFloat overlap  = myBottom - kbTop;

  /*
   * If there's no overlap, there's nothing to do.
   */
  if (overlap < 0) {
    return;
  }

  /*
   * Calculate the new height
   */
  CGRect crect = contentScrollView.frame;
  CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight + overlap);

  /*
   * Set the new height
   */
  [contentScrollView setFrame:nrect];

  /*
   * Set the new scroll position
   */
  CGPoint npos;

  npos.x = contentScrollView.contentOffset.x;
  npos.y = contentScrollView.contentOffset.y + overlap;

  [contentScrollView setContentOffset:npos animated:NO];
}
键盘上没有什么可做的了,隐藏了事件处理程序;不管怎样,我们都会把它放在里面:

-(void)keyboardWasHidden:(NSNotification*)aNotification {
}
就这样

/*
   // Only override drawRect: if you perform custom drawing.
   // An empty implementation adversely affects performance during animation.
   - (void)drawRect:(CGRect)rect
   {
   // Drawing code
   }
 */

@end

我知道这已经很老了,但上面的解决方案中仍然没有一个具备“完美”无bug、向后兼容和无闪烁动画所需的所有高级定位功能。
让我分享一下我的解决方案(假设您已经设置了
UIKeyboardWill(Show | Hide)通知
):

//在发送UIKeyboardWillShowNotification时调用
-(无效)键盘将显示:(NSNotification*)通知
{
//如果我们没有视图或在任何窗口中都看不到,我们不在乎
如果(!self.isViewLoaded | |!self.view.window){
返回;
}
NSDictionary*userInfo=[notification userInfo];
CGRect键盘框架窗口;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]getValue:&keyboardFrameInWindow];
//键盘框是在窗口级坐标中指定的。这将计算框,就像它是我们视图的子视图一样,使其成为滚动视图的同级视图
CGRect keyboardFrameInView=[self.view convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollView KEYBOARDCROSSION=cGRECTCROSION(_scrollView.frame,keyboardFrameInView);
UIEdgeInsets newContentInsets=UIEdgeInsetsMake(0,0,scrollViewKeyboardIntersection.size.height,0);
//这是一种旧的动画方法,但是唯一一种在参数(持续时间、曲线)和userInfo字典中包含的值之间保持一致性的方法。
[UIView beginAnimations:nil上下文:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]doubleValue];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfo]intValue];
_scrollView.contentInset=newContentInsets;
_scrollView.ScrollIndicationSets=新内容插入集;
/*
*取决于视觉布局,\ u focusedControl应该是输入字段(UITextField,…)或其他元素
*应可见,例如金额文本字段下方的购买按钮
*在委托中设置_focusedControl是有意义的,例如-textFieldShouldBeginEditing:如果您有多个输入字段
*/
如果(_focusedControl){
CGRect controlFrameInScrollView=[\u scrollView convertRect:\u focusedControl.bounds fromView:\u focusedControl];//如果控件在滚动视图下面的层次结构中较深,这将计算帧,就像它是直接子视图一样
controlFrameInScrollView=CGRectInset(controlFrameInScrollView,0,-10);//用任何漂亮的视觉偏移替换10
- (void) textViewDidEndEditing:(UITextView *)textView {
  /*
   * Reset the scroll view position
   */
  CGRect crect = contentScrollView.frame;
  CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight);

  [contentScrollView setFrame:nrect];

  /*
   * Reset the scroll view height
   */
  CGPoint npos;

  npos.x = contentScrollView.contentOffset.x;
  npos.y = currentScrollViewPosition;

  [contentScrollView setContentOffset:npos animated:YES];
  [tf resignFirstResponder];

  // ... do something with your data ...

}
-(void)keyboardWasHidden:(NSNotification*)aNotification {
}
/*
   // Only override drawRect: if you perform custom drawing.
   // An empty implementation adversely affects performance during animation.
   - (void)drawRect:(CGRect)rect
   {
   // Drawing code
   }
 */

@end
// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{

  // self.scrlViewUI.contentOffset = CGPointMake(0, textField.frame.origin.y);
    [_scrlViewUI setContentOffset:CGPointMake(0,textField.center.y-90) animated:YES];
    tes=YES;
    [self viewDidLayoutSubviews];
}
CGPoint bottomOffset = CGPointMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height);

[self.MainScrollView setContentOffset:bottomOffset animated:YES];
[self.MainScrollView scrollRectToVisible:CGRectMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height-30, MainScrollView.frame.size.width, MainScrollView.frame.size.height) animated:YES];
CGPoint contentOffset;
bool isScroll;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    contentOffset = self.myScroll.contentOffset;
    CGPoint newOffset;
    newOffset.x = contentOffset.x;
    newOffset.y = contentOffset.y;
    //check push return in keyboar
    if(!isScroll){
        //180 is height of keyboar
        newOffset.y += 180;
        isScroll=YES;
    }
   [self.myScroll setContentOffset:newOffset animated:YES];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField{
    //reset offset of content
    isScroll = NO;
    [self.myScroll setContentOffset:contentOffset animated:YES];
    [textField endEditing:true];
    return  true;
}
#import "UIResponder+FirstResponder.h"

static __weak id currentFirstResponder;

@implementation UIResponder (FirstResponder)

+(id)currentFirstResponder {
   currentFirstResponder = nil;
   [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
   return currentFirstResponder;
}

-(void)findFirstResponder:(id)sender {
   currentFirstResponder = self;
}

@end
-(void)keyboardWillShowNotification:(NSNotification*)aNotification{

    contentScrollView.delegate=nil;
    contentScrollView.scrollEnabled=NO;
    contentScrollViewOriginalOffset = contentScrollView.contentOffset;

    UIResponder *lc_firstResponder = [UIResponder currentFirstResponder];
    if([lc_firstResponder isKindOfClass:[UIView class]]){

        UIView *lc_view = (UIView *)lc_firstResponder;

        CGRect lc_frame = [lc_view convertRect:lc_view.bounds toView:contentScrollView];
        CGPoint lc_point = CGPointMake(0, lc_frame.origin.y-lc_frame.size.height);
        [contentScrollView setContentOffset:lc_point animated:YES];
    }
}
-(void)keyboardWillHideNotification:(NSNotification*)aNotification{

    contentScrollView.delegate=self;
    contentScrollView.scrollEnabled=YES;
    [contentScrollView setContentOffset:contentScrollViewOriginalOffset animated:YES];
}
class YourViewController: UIViewController, UITextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        _yourTextField.delegate = self //make sure you have the delegate set to this view controller for each of your textFields so textFieldDidBeginEditing can be called for each one
        ...

    }

    func textFieldDidBeginEditing(textField: UITextField) {
        var point = textField.convertPoint(textField.frame.origin, toView: _yourScrollView)
        point.x = 0.0 //if your textField does not have an origin at 0 for x and you don't want your scrollView to shift left and right but rather just up and down
        _yourScrollView.setContentOffset(point, animated: true)
    }

    func textFieldDidEndEditing(textField: UITextField) {
        //Reset scrollview once done editing
        scrollView.setContentOffset(CGPoint.zero, animated: true)
    }

}