代码之家  ›  专栏  ›  技术社区  ›  ComSubVie

uitextfield:键盘出现时移动视图

  •  52
  • ComSubVie  · 技术社区  · 16 年前

    我目前正在使用一个单一视图的iPhone应用程序,它有多个用于输入的uitextfields。当键盘显示时,它覆盖底部文本字段。所以我添加了相应的 textFieldDidBeginEditing: 方法,向上移动视图,效果很好:

    - (void)textFieldDidBeginEditing:(UITextField *)textField {
        if ( ( textField != inputAmount ) && ( textField != inputAge ) ) {
            NSTimeInterval animationDuration = 0.300000011920929;
            CGRect frame = self.view.frame;
            frame.origin.y -= kOFFSET_FOR_KEYBOARD;
            frame.size.height += kOFFSET_FOR_KEYBOARD;
            [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
            [UIView setAnimationDuration:animationDuration];
            self.view.frame = frame;
            [UIView commitAnimations];      
        }
    }
    

    此方法检查消息源是否是键盘显示时可见的文本字段之一,如果不是,则向上移动视图。

    我还添加了 textFieldDidEndEnditing: 方法,再次向下移动视图(并根据更改的输入更新一些模型对象):

    - (void)textFieldDidEndEditing:(UITextField *)textField {
        if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) {
            NSTimeInterval animationDuration = 0.300000011920929;
            CGRect frame = self.view.frame;
            frame.origin.y += kOFFSET_FOR_KEYBOARD;
            frame.size.height -= kOFFSET_FOR_KEYBOARD;
            [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
            [UIView setAnimationDuration:animationDuration];
            self.view.frame = frame;
            [UIView commitAnimations];      
        }
        // Additional Code
    }
    

    但是,这个解决方案有一个简单的缺陷:当我编辑完一个“隐藏”的文本字段并触摸另一个文本字段时,键盘消失,视图下移,视图再次上移,键盘重新出现。

    是否有可能防止键盘在两次编辑(即“隐藏”文本字段的编辑)之间消失和重新出现,以便仅当所选文本字段从键盘隐藏的文本字段更改为不隐藏的文本字段时,视图才会移动?

    6 回复  |  直到 8 年前
        1
  •  47
  •   Vlad Grigorov    9 年前

    此解决方案基于ComSubfie的解决方案。

    优势:

    • 它支持设备旋转-适用于所有方向;
    • 它不会对动画持续时间和曲线的值进行硬编码,而是从键盘通知中读取这些值;
    • 它利用 UIKeyboardWillShowNotification 而不是 UIKeyboardDidShowNotification 同步键盘动画和自定义动作;
    • 它不使用已弃用的 UIKeyboardBoundsUserInfoKey (二)
    • 按国际键处理键盘大小调整;
    • 通过注销键盘事件来修复内存泄漏;
    • 所有键盘处理代码都封装在一个单独的类中。- KBKeyboardHandler (二)
    • 灵活性- 键盘处理程序 课程可以很容易地扩展/修改,以更好地适应特定的需求;

    局限性:

    • 适用于iOS 4及以上版本,需要进行小修改以支持旧版本;
    • 它适用于单一 UIWindow . 如果使用多个uiwindows,可能需要修改 retrieveFrameFromNotification: 方法。

    用法:

    在项目中包括kbkeyboardhandler.h、kbkeyboardhandler.m和kbkeyboardhandlerDelegate.h。实施 KBKeyboardHandlerDelegate 视图控制器中的协议-它由一个方法组成,当显示、隐藏或更改键盘大小时,将调用该方法。实例化 键盘处理程序 并设置其委托人(通常是自己)。见样品 MyViewController 下面。

    键盘处理程序.h :

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @protocol KBKeyboardHandlerDelegate;
    
    @interface KBKeyboardHandler : NSObject
    
    - (id)init;
    
    // Put 'weak' instead of 'assign' if you use ARC
    @property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate; 
    @property(nonatomic) CGRect frame;
    
    @end
    

    键盘处理程序.m :

    #import "KBKeyboardHandler.h"
    #import "KBKeyboardHandlerDelegate.h"
    
    @implementation KBKeyboardHandler
    
    - (id)init
    {
        self = [super init];
        if (self)
        {
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(keyboardWillShow:)
                                                         name:UIKeyboardWillShowNotification
                                                       object:nil];
    
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(keyboardWillHide:)
                                                         name:UIKeyboardWillHideNotification
                                                       object:nil];
        }
    
        return self;
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        [super dealloc];
    }
    
    @synthesize delegate;
    @synthesize frame;
    
    - (void)keyboardWillShow:(NSNotification *)notification
    {
        CGRect oldFrame = self.frame;    
        [self retrieveFrameFromNotification:notification];
    
        if (oldFrame.size.height != self.frame.size.height)
        {
            CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
                                      self.frame.size.height - oldFrame.size.height);
            if (self.delegate)
                [self notifySizeChanged:delta notification:notification];
        }
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification
    {
        if (self.frame.size.height > 0.0)
        {
            [self retrieveFrameFromNotification:notification];
            CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);
    
            if (self.delegate)
                [self notifySizeChanged:delta notification:notification];
        }
    
        self.frame = CGRectZero;
    }
    
    - (void)retrieveFrameFromNotification:(NSNotification *)notification
    {
        CGRect keyboardRect;
        [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
        self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
    }
    
    - (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
    {
        NSDictionary *info = [notification userInfo];
    
        UIViewAnimationOptions curve;
        [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
    
        NSTimeInterval duration;
        [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
    
        void (^action)(void) = ^{
            [self.delegate keyboardSizeChanged:delta];
        };
    
        [UIView animateWithDuration:duration
                              delay:0.0
                            options:curve
                         animations:action
                         completion:nil];    
    }
    
    @end
    

    kbkeyboardhandler委派.h :

    @protocol KBKeyboardHandlerDelegate
    
    - (void)keyboardSizeChanged:(CGSize)delta;
    
    @end
    

    样品 我的视图控制器.h :

    @interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
    ...
    @end
    

    样品 我的视图控制器.m :

    @implementation MyViewController
    {
        KBKeyboardHandler *keyboard;
    }
    
    - (void)dealloc
    {
        keyboard.delegate = nil;
        [keyboard release];
        [super dealloc];
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        keyboard = [[KBKeyboardHandler alloc] init];
        keyboard.delegate = self;
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
        keyboard.delegate = nil;
        [keyboard release];
        keyboard = nil;
    }
    
    - (void)keyboardSizeChanged:(CGSize)delta
    {
        // Resize / reposition your views here. All actions performed here 
        // will appear animated.
        // delta is the difference between the previous size of the keyboard 
        // and the new one.
        // For instance when the keyboard is shown, 
        // delta may has width=768, height=264,
        // when the keyboard is hidden: width=-768, height=-264.
        // Use keyboard.frame.size to get the real keyboard size.
    
        // Sample:
        CGRect frame = self.view.frame;
        frame.size.height -= delta.height;
        self.view.frame = frame;
    }
    

    更新:修正了iOS 7警告,谢谢@weienv。

        2
  •  30
  •   ComSubVie    16 年前

    我刚刚解决了这个问题。解决方案是 UIKeyboardDidShowNotification UIKeyboardDidHideNotification 上面的观察者 textFieldDidBeginEditing: textFieldDidEndEditing: 方法。

    您需要三个附加变量,一个用于存储当前选定的uitextfield(我已将其命名为activefield),一个用于指示当前视图是否已移动,另一个用于指示是否显示键盘。

    这就是两个 UITextField 委托方法现在看:

    - (void)textFieldDidBeginEditing:(UITextField *)textField {
        activeField = textField;
    }
    
    - (void)textFieldDidEndEditing:(UITextField *)textField {
        activeField = nil;
        // Additional Code
    }
    

    加载视图时,将创建以下两个观察者:

    - (void)viewDidLoad {
        // Additional Code
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWasShown:)
                                                     name:UIKeyboardDidShowNotification
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWasHidden:)
                                                     name:UIKeyboardDidHideNotification
                                                   object:nil];
    }
    

    相应的方法如下:

    - (void)keyboardWasShown:(NSNotification *)aNotification {
        if ( keyboardShown )
            return;
    
        if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) {
            NSDictionary *info = [aNotification userInfo];
            NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
            CGSize keyboardSize = [aValue CGRectValue].size;
    
            NSTimeInterval animationDuration = 0.300000011920929;
            CGRect frame = self.view.frame;
            frame.origin.y -= keyboardSize.height-44;
            frame.size.height += keyboardSize.height-44;
            [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
            [UIView setAnimationDuration:animationDuration];
            self.view.frame = frame;
            [UIView commitAnimations];
    
            viewMoved = YES;
        }
    
        keyboardShown = YES;
    }
    
    - (void)keyboardWasHidden:(NSNotification *)aNotification {
        if ( viewMoved ) {
            NSDictionary *info = [aNotification userInfo];
            NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
            CGSize keyboardSize = [aValue CGRectValue].size;
    
            NSTimeInterval animationDuration = 0.300000011920929;
            CGRect frame = self.view.frame;
            frame.origin.y += keyboardSize.height-44;
            frame.size.height -= keyboardSize.height-44;
            [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
            [UIView setAnimationDuration:animationDuration];
            self.view.frame = frame;
            [UIView commitAnimations];
    
            viewMoved = NO;
        }
    
        keyboardShown = NO;
    }
    

    此代码现在按预期工作。只有在按下“完成”按钮时,键盘才会关闭,否则它将保持可见,并且视图不会移动。

    作为附加说明,我认为有可能 animationDuration 通过询问 NSNotification 对象,因为我已经使用了类似的解决方案,但没有使其工作(它现在这样做)。

        3
  •  2
  •   Rishil Patel Rams    8 年前

    此视图控制器必须 UITextView 委托,您必须设置 self.textview.delegate = self 在里面 viewdidload

     -(void) textViewDidBeginEditing:(UITextView *)textView
        {
            NSLog(@"%f",self.view.frame.origin.y);
            [UIView beginAnimations:nil context:NULL];
            [UIView setAnimationDuration:0.25f];
            CGRect frame = self.view.frame;
            frame.origin.y =frame.origin.y -204;
            [self.view setFrame:frame];
            [UIView commitAnimations];
        }
    
    -(void) textViewDidEndEditing:(UITextView *)textView
    {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.25f];
        CGRect frame = self.view.frame;
        frame.origin.y = frame.origin.y + 204;
        [self.view setFrame:frame];
        [UIView commitAnimations];
    }
    
        4
  •  0
  •   utkal patel    12 年前

    我有你的问题,做简单的事 只需为uiscrollview提供出口。 为视图中的每个文本字段设置唯一标记属性。

    -(void)textFieldDidBeginEditing:(UITextField *)textField
        {   
            switch (textField.tag)
            {
                case 2:    //can be your textfiled tag
                  { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150); 
                                               //set figure y-150 as per your comfirt
                      [scrollview setContentOffset:scrollPoint animated:YES]; 
                 }break;
    
                  case 3:   
                  { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180); 
                                               //set figure y-180 as per your comfirt
                      [scrollview setContentOffset:scrollPoint animated:YES]; 
                 }break;
    
                 ...
    
             }
        }
    
        -(void)textFieldDidEndEditing:(UITextField *)textField{
    
            if(textField.tag==3){
                [scrollview setContentOffset:CGPointZero animated:YES];
                    }
             //set the last textfield when you want to disappear keyboard.
        }
    
        5
  •  0
  •   Vibhooti    11 年前
    Write below code in your view controller. tbl is your table view. 
    
    -(void)viewWillAppear:(BOOL)animated{
    
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
        [[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
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
    }
    
    
    
    #pragma mark - Keyboard Methods
    -(void)keyboardWillShow:(NSNotification *)notification
    {
    //    375 × 667  ( 750 × 1334 ) iPhone 6
        //414 × 736
        CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
        int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
        [UIView animateWithDuration:0.5 animations:^{
            tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
        }];
    }
    
    -(void)keyboardWillChangeFrame:(NSNotification *)notification
    {
        CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    //    int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height;
         int H = [[UIScreen mainScreen] bounds].size.height - 64- 20  -keyboardRect.size.height;
        [UIView animateWithDuration:0.5 animations:^{
            //        scroll.frame = rect;
            tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
        }];
    }
    
    
    -(void)keyboardWillHide:(NSNotification *)notification
    {
    
        [UIView animateWithDuration:0.3 animations:^{
            //        scroll.frame = rect;
            tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right);
        }];
    }
    
        6
  •  0
  •   Alexey Savchenko    9 年前

    相当简单的解决方案,适用于所有屏幕尺寸

    首先,必须将UIExtFields嵌入到UIScrollView中。在我的例子中,我有几个uitextfields和一个uitextview。

    enter image description here

    然后您必须从UItextfieldDelegate、UItextViewDelegate继承。

    class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate

    将textfield和textview的委托分配给self。

    fullNameTextField.delegate = self usernameTextField.delegate = self websiteTextField.delegate = self profileDescription.delegate = self

    然后使用此代码:

    var editingTextInput: UIView!
    
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.keyboardShown(notification:)),
                                               name: NSNotification.Name.UIKeyboardDidShow,
                                               object: nil)
        
    
      }
      
        override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
      }
      
      func keyboardShown(notification: NSNotification) {
        
        if let infoKey  = notification.userInfo?[UIKeyboardFrameEndUserInfoKey],
          let rawFrame = (infoKey as AnyObject).cgRectValue {
          
          let keyboardFrame = view.convert(rawFrame, to: view)
          let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view)
    
          
          if editingTextInputFrame.maxY > keyboardFrame.minY{
            
            let diff = keyboardFrame.minY - editingTextInputFrame.maxY
            containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
            
          }
        }
      }
      
      func textFieldDidBeginEditing(_ textField: UITextField) {
        self.editingTextInput = textField
      }
      
      func textViewDidBeginEditing(_ textView: UITextView) {
        self.editingTextInput = textView
      }
      
      func textFieldDidEndEditing(_ textField: UITextField) {
        containerScrollView.setContentOffset(CGPoint.zero, animated: true)
      }
      
      func textViewDidEndEditing(_ textView: UITextView) {
        containerScrollView.setContentOffset(CGPoint.zero, animated: true)
      }

    简而言之,您订阅uikeyboarddidshow通知。 当你点击textfield或textview时,键盘就会显示出来,你抓取键盘的框架和你点击的输入元素的框架。将它们转换为ViewController的坐标系,并将输入元素的最低点与键盘的最高点进行比较。如果元素的下半部分低于键盘的最高部分,则设置ContainerScrollView的偏移量,使其与键盘的不同。

    if editingTextInputFrame.maxY > keyboardFrame.minY{
                
                let diff = keyboardFrame.minY - editingTextInputFrame.maxY
                containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
                
              }

    res1 res2