【问题标题】:Generic UITableView keyboard resizing algorithm通用 UITableView 键盘大小调整算法
【发布时间】:2012-11-30 11:48:00
【问题描述】:

我已经搜索了很多代码来调整表格视图的大小以适应键盘显示和隐藏,但我遇到的几乎每一篇文章都假设表格视图占据了其视图控制器的整个视图。我有一个 iPad 应用程序,其中表格视图只占屏幕的一部分。在这种情况下调整表格视图大小的正确方法是什么? (我上面提到的帖子中的所有代码都失败了)

【问题讨论】:

  • 你没有提到任何失败的代码。
  • 为什么不调整您发现的代码以解决表格视图框架底部和完整视图框架之间的差异?

标签: iphone ios ipad uitableview


【解决方案1】:

以下代码可以满足您的需求,并且适用于任何设备和任何布局。代码由Sensible TableView framework提供(许可复制和使用)。

- (void)keyboardWillShow:(NSNotification *)aNotification
{
if(keyboardShown) 
    return;

keyboardShown = YES;

// Get the keyboard size
UIScrollView *tableView;
if([self.tableView.superview isKindOfClass:[UIScrollView class]])
    tableView = (UIScrollView *)self.tableView.superview;
else
    tableView = self.tableView;
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil];

// Get the keyboard's animation details
NSTimeInterval animationDuration;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
UIViewAnimationCurve animationCurve;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];

// Determine how much overlap exists between tableView and the keyboard
CGRect tableFrame = tableView.frame;
CGFloat tableLowerYCoord = tableFrame.origin.y + tableFrame.size.height;
keyboardOverlap = tableLowerYCoord - keyboardRect.origin.y;
if(self.inputAccessoryView && keyboardOverlap>0)
{
    CGFloat accessoryHeight = self.inputAccessoryView.frame.size.height;
    keyboardOverlap -= accessoryHeight;

    tableView.contentInset = UIEdgeInsetsMake(0, 0, accessoryHeight, 0);
    tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, accessoryHeight, 0);
}

if(keyboardOverlap < 0)
    keyboardOverlap = 0;

if(keyboardOverlap != 0)
{
    tableFrame.size.height -= keyboardOverlap;

    NSTimeInterval delay = 0;
    if(keyboardRect.size.height)
    {
        delay = (1 - keyboardOverlap/keyboardRect.size.height)*animationDuration;
        animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height;
    }

    [UIView animateWithDuration:animationDuration delay:delay 
                        options:UIViewAnimationOptionBeginFromCurrentState 
                     animations:^{ tableView.frame = tableFrame; } 
                     completion:^(BOOL finished){ [self tableAnimationEnded:nil finished:nil contextInfo:nil]; }];
}
}

- (void)keyboardWillHide:(NSNotification *)aNotification
{
if(!keyboardShown)
    return;

keyboardShown = NO;

UIScrollView *tableView;
if([self.tableView.superview isKindOfClass:[UIScrollView class]])
    tableView = (UIScrollView *)self.tableView.superview;
else
    tableView = self.tableView;
if(self.inputAccessoryView)
{
    tableView.contentInset = UIEdgeInsetsZero;
    tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

if(keyboardOverlap == 0)
    return;

// Get the size & animation details of the keyboard
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil];

NSTimeInterval animationDuration;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
UIViewAnimationCurve animationCurve;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];

CGRect tableFrame = tableView.frame; 
tableFrame.size.height += keyboardOverlap;

if(keyboardRect.size.height)
    animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height;

[UIView animateWithDuration:animationDuration delay:0 
                    options:UIViewAnimationOptionBeginFromCurrentState 
                 animations:^{ tableView.frame = tableFrame; } 
                 completion:nil];
}

- (void) tableAnimationEnded:(NSString*)animationID finished:(NSNumber *)finished contextInfo:(void *)context
{
// Scroll to the active cell
if(self.activeCellIndexPath)
{
    [self.tableView scrollToRowAtIndexPath:self.activeCellIndexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
    [self.tableView selectRowAtIndexPath:self.activeCellIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}

注意事项:

一个。上面的两个方法已经添加到通知中心,使用如下代码:

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

b.上面使用的 ivars 声明如下:

BOOL keyboardShown;
CGFloat keyboardOverlap;

c。 'self.activeCellIndexPath' 始终设置为拥有当前活动 UITextField/UITextView 的单元格的 indexPath。

享受吧! :)

【讨论】:

  • 我只会添加一个。如果您的 UITableViewCell 高度可能大于显示 UITableView 的剩余大小,则最好修改 tableAnimationEnded 方法,使其滚动到“TOP”而不是“NONE”。此外,如果您在选择单元格时显示键盘并且您已经处理了 UITableView 选择,则无需再次选择“activeCellIndexPath”。
【解决方案2】:

我发现最简单的解决方案就是这个(我不喜欢使用子视图来处理这类事情):

注册键盘框架更改通知(最好在 viewWillAppear 中注册并在 viewWillDisappear 中取消注册:):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];

然后在方法中:

- (void)keyboardDidChangeFrame:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView];
    kbIntersectFrame = CGRectIntersection(self.bounds, kbIntersectFrame);

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0);
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
}

或者如果您想在更改 contentInset 后摆脱“跳转”:

- (void)keyboardDidChangeFrame:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView];
    kbIntersectFrame = CGRectIntersection(self.scrollView.bounds, kbIntersectFrame);

    // get point before contentInset change
    CGPoint pointBefore = self.scrollView.contentOffset;
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0);
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
    // get point after contentInset change
    CGPoint pointAfter = self.scrollView.contentOffset;
    // avoid jump by settings contentOffset
    self.scrollView.contentOffset = pointBefore;
    // and now animate smoothly
    [self.scrollView setContentOffset:pointAfter animated:YES];
}

【讨论】:

  • 这非常有效,而且比上面的 Tarek 更简单。我不使用 UIWindow 进行挖掘,但应该可以轻松切换到视图,不是吗?
  • 理论上是的。请记住,键盘坐标位于“窗口尺寸”中,因此如果视图的窗口大小/位置不同,则无法正常工作。
【解决方案3】:

简单的解决方案是将我的扩展 UIViewController+Keyboard.swift 添加到您的项目中,使用一行 setupKeyboardNotifcationListenerForScrollView(tableView) 它会自动调整大小。不需要子类化任何东西,只是一个扩展!它的开源地址为SingleLineKeyboardResize

【讨论】:

    【解决方案4】:

    简单的解决方案 - 在 init 或 viewDidLoad 上注册以接收键盘通知:

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

    然后当收到通知时,您可以使用给定的键盘矩形来调整 tableView 的框架:

    - (void)keyboardWillShow:(NSNotification *)notification
    {
       // Get the size of the keyboard.
       CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
       CGRect newTableFrame = _myTableView.frame;
       //Here make adjustments to the tableview frame based on the value in keyboard size
       ...
    
       _myTableView.frame = newTableFrame;
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification
    {
       //Here change the table frame back to what it originally was.
    }
    

    【讨论】:

    • 我建议在 viewWillAppear: 中注册并在 viewWillDisappear 中取消注册:(由于通知中心观察员保留并且因为 viewDidUnload:(用于取消注册)自 iOS 6 起已弃用)
    【解决方案5】:

    Check out this project,它是拖放操作,所以只需将 tablview 声明为类型TPKeyboardAvoidingTableView

    【讨论】:

      【解决方案6】:

      这里是键盘方法:

      func keyboardControl(notification: NSNotification, isShowing: Bool) {
          var userInfo = notification.userInfo!
          let keyboardRect = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
          let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey]!.unsignedIntValue
      
          let convertedFrame = self.view.convertRect(keyboardRect, fromView: nil)
          let heightOffset = self.view.bounds.size.height - convertedFrame.origin.y
          let options = UIViewAnimationOptions(rawValue: UInt(curve) << 16)
          let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue
      
          //your UITableView bottom constrant
          self.tableViewMarginBottomConstraint.constant = heightOffset
      
          var contentInsets = UIEdgeInsetsZero
          if isShowing {
              contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardRect.size.height), 0.0)
          }
      
          UIView.animateWithDuration(
              duration,
              delay: 0,
              options: options.union(.LayoutSubviews).union(.BeginFromCurrentState),
              animations: {
                  self.listTableView.contentInset = contentInsets
                  self.listTableView.scrollIndicatorInsets = contentInsets
                  self.listTableView.scrollBottomToLastRow()
                  self.view.layoutIfNeeded()
              },
              completion: { bool in
          })
      }
      

      这里是 UITableView 扩展:

      extension UITableView {
          func totalRows() -> Int {
              var i = 0
              var rowCount = 0
              while i < self.numberOfSections {
                  rowCount += self.numberOfRowsInSection(i)
                  i++
              }
              return rowCount
          }
      
          var lastIndexPath: NSIndexPath {
              get { return NSIndexPath(forRow: self.totalRows()-1, inSection: 0) }
          }
      
          func scrollBottomToLastRow() {
              self.scrollToRowAtIndexPath(self.lastIndexPath, atScrollPosition: .Bottom, animated: false)
          }
      }
      

      【讨论】:

        【解决方案7】:

        你可以使用IQKeyboardManager来实现你想要的,它是一个无代码库,你只需将它添加到你的Podfile中: pod 'IQKeyboardManager' 就是这样,它会在键盘时处理滚动效果即使 UITextField/UITextView 不是 scrollView/tableView 的一部分,也会显示。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-11-12
          • 2017-08-22
          • 2012-03-26
          • 1970-01-01
          • 2012-07-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多