【问题标题】:Scroll to the bottom of textView programmatically以编程方式滚动到 textView 的底部
【发布时间】:2016-01-30 08:27:35
【问题描述】:

我制作了一个向 textView 添加一些文本的按钮,我希望它在按下时自动滚动到底部,以便用户能够看到添加的新文本。
我不能在 Swift 中使用 this 解决方案,因为我不了解 Objective-C。 有谁知道如何在 Swift 中滚动到 textView 的底部?谢谢。

【问题讨论】:

  • 如果textView 是可滚动的,您可以滚动吗?你到底是什么意思?你在发帖前试过吗?请显示一些代码:)
  • 您使用的是什么版本的操作系统?
  • self.textView.scrollRangeToVisible(NSMakeRange(0, 0)) try this where for range put the end of the textView
  • 试试这个link
  • @Korpel 我编辑了我的问题。我想以编程方式滚动。想象一个充满字符的 textView。我想按一个按钮并以编程方式滚动到这个 textView 的底部。您可以在上面的链接中看到 Objective-C 解决方案。

标签: ios swift


【解决方案1】:

尝试了内容偏移和滚动查看解决方案,得到混合结果和不连贯的滚动;环顾下方似乎可以工作,并在需要时产生一致的滚动到底部。

在viewdidload中:

self.storyTextView.layoutManager.allowsNonContiguousLayout = false

然后在需要时:

let stringLength:Int = self.storyTextView.text.characters.count
self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))

【讨论】:

  • 哇,我有时会遇到不稳定的滚动和奇怪的字形截止,结果.layoutManager.allowsNonContiguousLayout = false 是灵丹妙药,谢谢!
【解决方案2】:

斯威夫特 4

let bottom = NSMakeRange(textLog.text.count - 1, 1)
textLog.scrollRangeToVisible(bottom)

斯威夫特 3

let bottom = NSMakeRange(textLog.text.characters.count - 1, 1)
textLog.scrollRangeToVisible(bottom)

更新:感谢@AntoineRucquoy 提醒 Swift 4!

【讨论】:

  • 从 Swift 4.x 开始,为了避免被弃用的警告,使用 textLog.text.count 而不是 textLog.text.characters.count :-)
【解决方案3】:

简单地说,myTextView 是有问题的UITextView

let bottom = myTextView.contentSize.height

myTextView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true) // Scrolls to end

【讨论】:

    【解决方案4】:

    因此,如果您单击发布的链接,则接受的答案会显示此 Objective-C 代码:

    -(void)scrollTextViewToBottom:(UITextView *)textView 
    {
      if(textView.text.length > 0 ) 
      {
        NSRange bottom = NSMakeRange(textView.text.length -1, 1);
        [textView scrollRangeToVisible:bottom];
      }
    }
    

    因此,您面临的挑战是将代码转换为 Swift。

    把它分解成碎片,一次一个地解决它们。首先是方法定义本身。

    该方法称为scrollTextViewToBottom。它以 UITextView 作为参数,并且不返回结果。你会如何在 Swift 中编写该方法定义?

    接下来看看方法体。 if 语句在 Swift 中应该完全相同。 NSRange 的创建几乎完全相同。你只需要稍微改变一下:

    let bottom = NSMakeRange(textView.text.length -1, 1)
    

    对于不了解 Objective-C 的人来说,最难的部分可能是方法调用。它将消息 scrollRangeToVisible 发送到对象textView。传递的参数是bottom。看看你能不能用 Swift 重写那行代码。然后把整个东西放在一起。

    【讨论】:

    • 那么谁拒绝了我的回答,为什么?我正在尝试教 OP 如何为自己解决这些问题,而不是给他转换后的代码。从长远来看,学习如何自己解决这个问题比让别人用勺子喂你完成的代码更有帮助。
    • 先生,我完全同意你的看法。你的回答是提供合法的步骤来帮助 OP.Upvoted
    • 我不同意,这是一种非常不标准的滚动文本视图的方式。大多数 iOS 最熟悉使用 contentOffset。更不用说,NSRange 很可能在未来被弃用,取而代之的是 Swift 的 Range
    • 我根据 OP 找到的代码进行了回答,以便提供有关如何将 Objective-C 转换为 Swift 的课程。我敢打赌,NSRange 在未来 2 或 3 年内不会被弃用,因为它在 Objective-C 代码中被广泛使用,而且 Objective-C 将会存在很长时间。
    • 您使用 textView 的 contentOffset 属性和setContentOffset:animated: 的解决方案很好,但我觉得教 OP 如何理解 Objective-C 很重要。
    【解决方案5】:

    我在添加文本时自动滚动到底部的应用中使用以下内容:

    首先在初始化你的 textView 时,请执行以下操作:

    textView.layoutManager.allowsNonContiguousLayout = false
    textView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
    

    然后添加以下观察者方法:

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        var bottom = textView.contentSize.height - textView.frame.size.height
        if bottom < 0 {
            bottom = 0
        }
        if textView.contentOffset.y != bottom {
            textView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true)
        }
    }
    

    allowsNonContiguousLayout 设置为 false 为我修复了 contentSize 问题。

    添加 contentSize 观察者将观察 textView 的 contentSize 的任何新变化并调用 -observeValue(forKeyPath...) 进行更改时的功能。

    -observeValue(...)函数中,我们首先得到bottom(完全滚动到底部时的y contentOffset)。然后我们检查该值是否为负,这意味着 contentSize 高度小于 textView 框架高度,并且您不能真正进行任何滚动。如果您尝试使用该负值以编程方式滚动,则会导致许多人都知道和喜爱的臭名昭著的抖动。因此,为了避免这种抖动,我们只需将值设置为它应该是的值,0 或者您也可以只是 return

    然后我们只是测试看看 contentOffset 是否已经不等于 bottom 值,我们给它新的值。这样可以避免在不需要设置时设置 contentOffset

    【讨论】:

      【解决方案6】:

      语言:斯威夫特

      请按照以下步骤操作:
      //声明

      @IBOutlet weak var trTextDataRead: UITextView!  
      

      //自定义方法

      func insertTextView(text: String){
       //Insert text
       trTextDataRead.text.append(text)  
      
       //Scroll to the end  
       let btm = NSMakeRange(trTextDataRead.text.lengthOfBytes(using: String.Encoding.utf8), 0)  
       trTextDataRead.scrollRangeToVisible(btm)
      }
      

      【讨论】:

        【解决方案7】:

        如果您正在处理 UITextView 的属性文本属性:

        在 viewDidLoad() 中

        self.storyTextView.layoutManager.allowsNonContiguousLayout = false
        

        在你的滚动方法中

        let stringLength:Int = self.storyTextView.attributedText.string.characters.count
        self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))
        

        【讨论】:

          【解决方案8】:

          斯威夫特 4

          private func textViewScrollToBottom() {
              let bottomRange = NSMakeRange(self.myTextView.text.count - 1, 1)
          
              self.myTextView.scrollRangeToVisible(bottomRange)
          }
          

          【讨论】:

            【解决方案9】:

            UITextView 有一个属性contentOffsent。您可以设置textView.contentOffsettextView.setContentOffset(offset, animated: true)

            例如,如果您的文本视图的contentSize(100, 500),但文本视图的高度只有100,那么要滚动到底部,请将contentOffset 属性设置为(0, 400)(这是为了垂直文本视图)。更一般地,滚动到底部的公式是textView.contentSize.height-textView.height。每次按下按钮时,设置偏移量。

            我还真的建议您阅读文档并尝试弄清楚。 Swift 和 iOS 都有很好的文档记录,这样的问题很容易通过 Google 搜索到。

            编辑:这是因为 UITextView 继承自 UIScrollView

            旁注:我写了一个UITextView 子类,您可以在其中设置垂直文本对齐方式,因此如果将文本对齐方式设置为.Bottom,文本将与视图底部对齐。

            class TextView: UITextView {
            
                enum VerticalAlignment: Int {
                    case Top = 0, Middle, Bottom
                }
            
                var verticalAlignment: VerticalAlignment = .Middle
            
                //override contentSize property and observe using didSet
                override var contentSize: CGSize {
                    didSet {
                        let textView = self
                        let height = textView.bounds.size.height
                        let contentHeight:CGFloat = contentSize.height
                        var topCorrect: CGFloat = 0.0
                        switch(self.verticalAlignment){
                        case .Top:
                            textView.contentOffset = CGPointZero //set content offset to top
                       case .Middle:
                            topCorrect = (height - contentHeight * textView.zoomScale)/2.0
                            topCorrect = topCorrect < 0 ? 0 : topCorrect
                            textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
                       case .Bottom:
                            topCorrect = textView.bounds.size.height - contentHeight
                            topCorrect = topCorrect < 0 ? 0 : topCorrect
                            textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
                        }
                        if contentHeight >= height { //if the contentSize is greater than the height
                            topCorrect = contentHeight - height //set the contentOffset to be the
                            topCorrect = topCorrect < 0 ? 0 : topCorrect //contentHeight - height of textView
                            textView.contentOffset = CGPoint(x: 0, y: topCorrect)
                        }
                    }
                }
            
                // MARK: - UIView
            
                override func layoutSubviews() {
                    super.layoutSubviews()
                    let size = self.contentSize //forces didSet to be called
                    self.contentSize = size
                }
            }
            

            在上面的示例中(直接从我的子类中提取),您会注意到我大量使用了contentOffset 属性。我做了一些计算来确定偏移量应该基于垂直对齐属性的位置,然后根据(这是您以编程方式使用滚动视图滚动的方式)设置内容偏移属性

            【讨论】:

              【解决方案10】:

              很多人都在解释如何滚动到底部,但需要注意的一点是,如果你将它放在viewDidLoad 中,这将不起作用。例如:当页面加载时,我需要使用它来将日志滚动到底部。为此,我必须实现以下代码

              - (void) viewDidLoad {
                  [super viewDidLoad];
                  [_logTextView setText:[Logger loadLogText]];
              }
              
              - (void) viewDidLayoutSubviews {
                  [_logTextView
                   setContentOffset:CGPointMake(
                                        0.0,
                                        _logTextView.contentSize.height
                                      - _logTextView.frame.size.height
                                    )
                           animated:NO
                  ];
              }
              

              viewDidLoad中无法进行UITextView的实际滚动。

              在我的viewDidLoad 实现中,我为文本框设置了文本。

              在我的viewDidLayoutSubviews 实现中,我通过使用文本视图内容的高度减去文本视图本身的高度生成 CGPoint 来设置 UITextView 的内容偏移量。这样,当它滚动到底部时,文本的底部不在框的顶部,而是文本的底部在框的底部。

              【讨论】:

                猜你喜欢
                • 2010-10-31
                • 2018-12-13
                • 2018-11-22
                • 1970-01-01
                • 1970-01-01
                • 2011-12-22
                • 2015-07-21
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多