【问题标题】:UIScrollView scroll to bottom programmaticallyUIScrollView 以编程方式滚动到底部
【发布时间】:2010-10-31 10:41:49
【问题描述】:

如何让UIScrollView 在我的代码中滚动到底部?或者以更通用的方式,到子视图的任何一点?

【问题讨论】:

    标签: ios objective-c swift uiscrollview


    【解决方案1】:

    要滚动到底端,我们必须使用目标视图的最大高度。

    import UIKit
    
    extension UIScrollView {
    
        func scrollToBottomOf(targetView: UIView, animated: Bool) {
            setContentOffset(CGPoint(x:targetView.frame.minX, y:targetView.frame.maxY), animated: animated)
        }
    }
    
    //func invocation example 
    optionScrollView.scrollToBottomOf(targetView: self.optionsStackView, animated: false)
    

    【讨论】:

      【解决方案2】:

      您可以使用 UIScrollView 的 setContentOffset:animated: 函数滚动到内容视图的任何部分。下面是一些滚动到底部的代码,假设你的 scrollView 是self.scrollView:

      目标-C:

      CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
      [self.scrollView setContentOffset:bottomOffset animated:YES];
      

      斯威夫特:

      let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom)
      scrollView.setContentOffset(bottomOffset, animated: true)
      

      希望有帮助!

      【讨论】:

      • 您可能希望将以下内容滚动到底部,而不是滚出滚动视图:CGPoint bottomOffset = CGPointMake(0, [self.scrollView contentSize].height - self.scrollView.frame.size .height);
      • 您没有考虑插图。我认为您应该更喜欢这个底部偏移:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
      • 这在横向模式下不起作用!没有完全滚动到底部
      • 如果contentSize 低于bounds 将无法正常工作。所以应该是这样的:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
      • 这处理底部插入,并超过 0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
      【解决方案3】:

      接受答案的 Swift 版本,便于复制粘贴:

      let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
      scrollView.setContentOffset(bottomOffset, animated: true)
      

      【讨论】:

      • bottomOffset 在你的例子中应该是 let 而不是 var。
      • 对,改了。 swift2的东西:)
      • 你需要检查 scrollView.contentSize.height - scrollView.bounds.size.height > 0 否则你会得到奇怪的结果
      • 当你有新的导航栏时,这个解决方案并不完美。
      • @Josh,还在等待 SO 发现这一点的那一天
      【解决方案4】:

      扩展 UIScrollView 以添加一个 scrollToBottom 方法:

      extension UIScrollView {
          func scrollToBottom(animated:Bool) {
              let offset = self.contentSize.height - self.visibleSize.height
              if offset > self.contentOffset.y {
                  self.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
              }
          }
      }
      

      【讨论】:

      • 这对现有答案有什么好处?
      【解决方案5】:

      斯威夫特:

      你可以使用这样的扩展:

      extension UIScrollView {
          func scrollsToBottom(animated: Bool) {
              let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
              setContentOffset(bottomOffset, animated: animated)
          }
      }
      

      用途:

      scrollView.scrollsToBottom(animated: true)
      

      【讨论】:

        【解决方案6】:

        使用(可选)footerView 和 contentInset,解决方案是:

        CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height - tableView.frame.size.height + _tableView.contentInset.bottom);
        if (bottomOffset.y > 0) [_tableView setContentOffset: bottomOffset animated: YES];
        

        【讨论】:

          【解决方案7】:

          如果您以某种方式更改滚动视图 contentSize(例如,向滚动视图内的 stackView 添加一些内容),您必须在滚动之前调用 scrollView.layoutIfNeeded(),否则它什么也不做。

          例子:

          scrollView.layoutIfNeeded()
          let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
          if(bottomOffset.y > 0) {
              scrollView.setContentOffset(bottomOffset, animated: true)
          }
          

          【讨论】:

            【解决方案8】:

            看起来这里的所有答案都没有考虑到安全区域。 从 iOS 11 开始,iPhone X 引入了安全区域。这可能会影响滚动视图的contentInset。

            对于 iOS 11 及更高版本,正确滚动到底部并包含内容插图。您应该使用adjustedContentInset 而不是contentInset。检查此代码:

            • 斯威夫特:
            let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
            scrollView.setContentOffset(bottomOffset, animated: true)
            
            • Objective-C
            CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
            [self.scrollView setContentOffset:bottomOffset animated:YES];
            
            • Swift 扩展(保留原来的contentOffset.x):
            extension UIScrollView {
                func scrollsToBottom(animated: Bool) {
                    let bottomOffset = CGPoint(x: contentOffset.x,
                                               y: contentSize.height - bounds.height + adjustedContentInset.bottom)
                    setContentOffset(bottomOffset, animated: animated)
                }
            }
            

            参考资料:

            【讨论】:

              【解决方案9】:

              对于水平滚动视图

              如果你喜欢我有一个 Horizo​​ntal ScrollView 并且想要滚动到它的末尾(在我的例子中是它的最右边),你需要更改已接受答案的某些部分:

              Objective-C

              CGPoint rightOffset = CGPointMake(self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, 0 );
              [self.scrollView setContentOffset:rightOffset animated:YES];
              

              斯威夫特

              let rightOffset: CGPoint = CGPoint(x: self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, y: 0)
              self.scrollView.setContentOffset(rightOffset, animated: true)
              

              【讨论】:

                【解决方案10】:

                快速实现:

                extension UIScrollView {
                   func scrollToBottom(animated: Bool) {
                     if self.contentSize.height < self.bounds.size.height { return }
                     let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.size.height)
                     self.setContentOffset(bottomOffset, animated: animated)
                  }
                }
                

                使用它:

                yourScrollview.scrollToBottom(animated: true)
                

                【讨论】:

                  【解决方案11】:

                  Xamarin.iOS 版本为UICollectionView 接受的答案,便于复制和粘贴

                  var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
                  CollectionView.SetContentOffset (bottomOffset, false);
                  

                  【讨论】:

                    【解决方案12】:

                    Xamarin.iOS 接受答案的版本

                    var bottomOffset = new CGPoint (0,
                         scrollView.ContentSize.Height - scrollView.Frame.Size.Height
                         + scrollView.ContentInset.Bottom);
                    
                    scrollView.SetContentOffset (bottomOffset, false);
                    

                    【讨论】:

                      【解决方案13】:

                      滚动到表格视图的最后一项的解决方案:

                      斯威夫特 3:

                      if self.items.count > 0 {
                              self.tableView.scrollToRow(at:  IndexPath.init(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
                      }
                      

                      【讨论】:

                        【解决方案14】:

                        考虑到contentInset 的 Swift 2.2 解决方案

                        let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
                        scrollView.setContentOffset(bottomOffset, animated: true)
                        

                        这应该在扩展中

                        extension UIScrollView {
                        
                          func scrollToBottom() {
                            let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
                            setContentOffset(bottomOffset, animated: true)
                          }
                        }
                        

                        请注意,您可能需要在滚动前检查bottomOffset.y &gt; 0

                        【讨论】:

                          【解决方案15】:

                          如果contentSize 低于bounds 怎么办?

                          对于 Swift 它是:

                          scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
                          

                          【讨论】:

                            【解决方案16】:

                            在 Swift 中使用 UIScrollView 的 setContentOffset:animated: 函数滚动到底部。

                            let bottomOffset : CGPoint = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
                            scrollView.setContentOffset(bottomOffset, animated: true)
                            

                            【讨论】:

                              【解决方案17】:

                              迅速:

                                 if self.mainScroll.contentSize.height > self.mainScroll.bounds.size.height {
                                      let bottomOffset = CGPointMake(0, self.mainScroll.contentSize.height - self.mainScroll.bounds.size.height);
                                      self.mainScroll.setContentOffset(bottomOffset, animated: true)
                                  }
                              

                              【讨论】:

                                【解决方案18】:

                                我发现contentSize并不能真正反映文字的实际大小,所以在尝试滚动到底部时,它会有点偏离。确定实际内容大小的最佳方法实际上是使用NSLayoutManager 的usedRectForTextContainer: 方法:

                                UITextView *textView;
                                CGSize textSize = [textView.layoutManager usedRectForTextContainer:textView.textContainer].size;
                                

                                要确定UITextView 中实际显示了多少文本,您可以通过从框架高度中减去文本容器插入来计算它。

                                UITextView *textView;
                                UIEdgeInsets textInsets = textView.textContainerInset;
                                CGFloat textViewHeight = textView.frame.size.height - textInsets.top - textInsets.bottom;
                                

                                然后滚动就变得容易了:

                                // if you want scroll animation, use contentOffset
                                UITextView *textView;
                                textView.contentOffset = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);
                                
                                // if you don't want scroll animation
                                CGRect scrollBounds = textView.bounds;
                                scrollBounds.origin = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);
                                textView.bounds = scrollBounds;
                                

                                一些数字供参考,以了解不同大小代表空UITextView。

                                textView.frame.size = (width=246, height=50)
                                textSize = (width=10, height=16.701999999999998)
                                textView.contentSize = (width=246, height=33)
                                textView.textContainerInset = (top=8, left=0, bottom=8, right=0)
                                

                                【讨论】:

                                  【解决方案19】:

                                  虽然Matt 解决方案对我来说似乎是正确的,但如果已经设置了集合视图插图,您还需要考虑集合视图插图。

                                  修改后的代码将是:

                                  CGSize csz = sv.contentSize;
                                  CGSize bsz = sv.bounds.size;
                                  NSInteger bottomInset = sv.contentInset.bottom;
                                  if (sv.contentOffset.y + bsz.height + bottomInset > csz.height) {
                                      [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                                                       csz.height - bsz.height + bottomInset) 
                                                  animated:YES];
                                  }
                                  

                                  【讨论】:

                                    【解决方案20】:

                                    最简单的解决方案:

                                    [scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];
                                    

                                    【讨论】:

                                    • 谢谢。我忘了我将滚动视图更改为 UITableView 并且这段代码也适用于 UITableView,仅供参考。
                                    • 为什么不只是:[scrollview scrollRectToVisible:CGRectMake(0, scrollview.contentSize.height, 1, 1) animated:YES];
                                    【解决方案21】:
                                    CGFloat yOffset = scrollView.contentOffset.y;
                                    
                                    CGFloat height = scrollView.frame.size.height;
                                    
                                    CGFloat contentHeight = scrollView.contentSize.height;
                                    
                                    CGFloat distance = (contentHeight  - height) - yOffset;
                                    
                                    if(distance < 0)
                                    {
                                        return ;
                                    }
                                    
                                    CGPoint offset = scrollView.contentOffset;
                                    
                                    offset.y += distance;
                                    
                                    [scrollView setContentOffset:offset animated:YES];
                                    

                                    【讨论】:

                                      【解决方案22】:

                                      如果您不需要动画,这可行:

                                      [self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];
                                      

                                      【讨论】:

                                        【解决方案23】:

                                        只是对现有答案的增强。

                                        CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
                                        [self.scrollView setContentOffset:bottomOffset animated:YES];
                                        

                                        它还负责底部插图(以防您在键盘可见时使用它来调整滚动视图)

                                        【讨论】:

                                        • 这应该是正确的答案...而不是其他如果弹出键盘就会损坏的答案。
                                        【解决方案24】:

                                        确保内容底部可见的一个好方法是使用以下公式:

                                        contentOffsetY = MIN(0, contentHeight - boundsHeight)
                                        

                                        这可确保您的内容的底部边缘始终位于或高于视图的底部边缘。 MIN(0, ...) 是必需的,因为UITableView(可能还有UIScrollView)在用户尝试通过明显捕捉contentOffsetY = 0 进行滚动时确保contentOffsetY &gt;= 0。这对用户来说看起来很奇怪。

                                        实现这个的代码是:

                                        UIScrollView scrollView = ...;
                                        CGSize contentSize = scrollView.contentSize;
                                        CGSize boundsSize = scrollView.bounds.size;
                                        if (contentSize.height > boundsSize.height)
                                        {
                                            CGPoint contentOffset = scrollView.contentOffset;
                                            contentOffset.y = contentSize.height - boundsSize.height;
                                            [scrollView setContentOffset:contentOffset animated:YES];
                                        }
                                        

                                        【讨论】:

                                          【解决方案25】:

                                          救援类别!

                                          将此添加到某处的共享实用程序标头中:

                                          @interface UIScrollView (ScrollToBottom)
                                          - (void)scrollToBottomAnimated:(BOOL)animated;
                                          @end
                                          

                                          然后到那个实用程序实现:

                                          @implementation UIScrollView(ScrollToBottom)
                                          - (void)scrollToBottomAnimated:(BOOL)animated
                                          {
                                               CGPoint bottomOffset = CGPointMake(0, self.contentSize.height - self.bounds.size.height);
                                               [self setContentOffset:bottomOffset animated:animated];
                                          }
                                          @end
                                          

                                          然后在任何你喜欢的地方实现它,例如:

                                          [[myWebView scrollView] scrollToBottomAnimated:YES];
                                          

                                          【讨论】:

                                          • 总是,总是,总是,总是使用类别!完美:)
                                          【解决方案26】:

                                          滚动到顶部

                                          - CGPoint topOffset = CGPointMake(0, 0);
                                          - [scrollView setContentOffset:topOffset animated:YES];
                                          

                                          滚动到底部

                                          - CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - self.scrollView.bounds.size.height);
                                           - [scrollView setContentOffset:bottomOffset animated:YES];
                                          

                                          【讨论】:

                                            【解决方案27】:

                                            在添加footerView 后,当我尝试在self.tableView (iOS 4.1) 上的UITableViewController 中使用它时,对我不起作用。它滚动出边界,显示黑屏。

                                            替代解决方案:

                                             CGFloat height = self.tableView.contentSize.height; 
                                            
                                             [self.tableView setTableFooterView: myFooterView];
                                             [self.tableView reloadData];
                                            
                                             CGFloat delta = self.tableView.contentSize.height - height;
                                             CGPoint offset = [self.tableView contentOffset];
                                             offset.y += delta;
                                            
                                             [self.tableView setContentOffset: offset animated: YES];
                                            

                                            【讨论】:

                                              【解决方案28】:

                                              将内容偏移设置为内容大小的高度是错误的:它会将内容的底部滚动到滚动视图的顶部,因此看不见。

                                              正确的解决方法是将内容的底部滚动到滚动视图的底部,像这样(sv是UIScrollView):

                                              CGSize csz = sv.contentSize;
                                              CGSize bsz = sv.bounds.size;
                                              if (sv.contentOffset.y + bsz.height > csz.height) {
                                                  [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                                                                   csz.height - bsz.height) 
                                                              animated:YES];
                                              }
                                              

                                              【讨论】:

                                              • 不应该是if (sv.contentOffset.y + csz.height &gt; bsz.height) {吗?
                                              • @jeffamaphone - 感谢您的提问。实际上,在这一点上,我什至不确定该条件应该用于什么目的! setContentOffset: 命令很重要。
                                              • 如果 setContentOffset 不执行任何操作,我认为它会避免调用?
                                              • @jeffamaphone - 过去,在设备旋转后,滚动视图的内容底部可能高于框架底部(因为如果我们旋转滚动视图,它的内容偏移量保持不变,但是由于自动调整大小,滚动视图高度可能已经增加)。条件是:如果发生这种情况,请将内容底部放回框架的底部。但是当我粘贴到代码中时包含条件是愚蠢的。但是,条件 is 正确地测试了它正在测试的内容。
                                              【解决方案29】:

                                              valdyr,希望这对你有帮助:

                                              CGPoint bottomOffset = CGPointMake(0, [textView contentSize].height - textView.frame.size.height);
                                              
                                              if (bottomOffset.y > 0)
                                               [textView setContentOffset: bottomOffset animated: YES];
                                              

                                              【讨论】:

                                                【解决方案30】:

                                                如果您使用 UITableview(它是 UIScrollView 的子类),我还发现了另一种有用的方法:

                                                [(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
                                                

                                                【讨论】:

                                                • 仅供参考,这只是 UITableView 的功能
                                                猜你喜欢
                                                • 2016-01-30
                                                • 2011-01-15
                                                • 2018-12-13
                                                • 2018-11-22
                                                • 2011-12-22
                                                • 2015-07-21
                                                • 1970-01-01
                                                相关资源
                                                最近更新 更多