【问题标题】:Stop UITextView from jumping when programmatically setting text以编程方式设置文本时停止 UITextView 跳转
【发布时间】:2010-10-26 05:32:40
【问题描述】:

我必须更新滚动 UITextView 中的少量文本。我只会在光标当前所在的位置插入一个字符,并且只需按一下导航栏上的按钮即可。

我的问题是,每当我调用文本视图的 setText 方法时,它都会跳到文本的底部。我试过使用 contentOffset 并重置 selectedRange 但它不起作用!这是我的例子:

// Remember offset and selection
CGPoint contentOffset = [entryTextView contentOffset];
NSRange selectedRange = [entryTextView selectedRange];
// Update text
entryTextView.text = entryTextView.text;
// Try and reset offset and selection
[entryTextView setContentOffset:contentOffset animated:NO];
[entryTextView setSelectedRange: selectedRange];

有没有什么方法可以在不进行任何滚动移动的情况下更新文本...就好像他们只是在键盘上输入了什么一样?

编辑:

我已经尝试使用 textViewDidChange: 委托方法,但它仍然没有向上滚动到原始位置。

- (void)textViewDidChange:(UITextView *)textView {
    if (self.programChanged) {
        [textView setSelectedRange:self.selectedRange];
        [textView setContentOffset:self.contentOffset animated:NO];
        self.programChanged = NO;
    }
}

- (void)changeButtonPressed:(id)sender {
    // Remember position
    self.programChanged = YES;
    self.contentOffset = [entryTextView contentOffset];
    self.selectedRange = [entryTextView selectedRange];
    // Update text
    entryTextView.text = entryTextView.text;
}

【问题讨论】:

    标签: iphone cocoa-touch uitextview


    【解决方案1】:

    看看 UITextViewDelegate,我相信 textViewDidChangeSelection 方法可以让你做你需要的。

    【讨论】:

    • 感谢您的意见。这个委托方法应该发生什么?这是 contentOffset 和 selectedRange 最终重置的时间吗?
    【解决方案2】:

    不是那么优雅的解决方案 - 但它确实有效,所以谁在乎:

    - (IBAction)changeTextProgrammaticaly{
         myTextView.text = @"Some text";
         [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(rewindOffset) userInfo:nil repeats:NO];
    }
    
    - (void)rewindOffset{
        [myTextView setContentOffset:CGPointMake(0,0) animated: NO];
    }
    

    【讨论】:

    • (此解决方案是在此处建议的所有其他解决方案对我不起作用之后出现的)
    • 这不值得反对。有时像这样的滚动问题必须在运行循环中解决。我知道可能有更优雅的解决方案,但这有效且无害。
    【解决方案3】:

    如果您使用 iPhone 3.0 或更高版本,则可以解决此问题:

    textView.scrollEnabled = NO;
    
    //You should know where the cursor will be(if you update your text by appending/inserting/deleting you can know the selected range) so keep it in a NSRange variable.
    
    Then update text:
    textView.text = yourText;
    
    textView.scrollEnabled = YES;
    textView.selectedRange = range;//you keep before
    

    它现在应该可以工作了(不再跳跃)

    问候 梅尔阿萨亚格

    【讨论】:

      【解决方案4】:

      没有任何建议的解决方案对我有用。 -setContentOffset:animated: 由 -setText: 触发 3 次,动画 YES 和结尾的 contentOffset(减去 UITextView 的默认 8pt 边距)。我将 -setText: 包裹在一个守卫中:

      textView.contentOffsetAnimatedCallsDisabled = YES;
      textView.text = text;
      textView.contentOffsetAnimatedCallsDisabled = NO;
      

      在 -setContentOffset:animated: put 中的 UITextView 子类中

      if (contentOffsetAnimatedCallsDisabled) return; // early return
      

      在你的其他逻辑中。不要忘记超级电话。这行得通。

      拉斐尔

      【讨论】:

      • 谢谢建议,我试试看!
      • 它不是私有API,你重写 UITextView 并根据我的例子自己实现行为。 Meir Assayag 的回答不适用于我的情况和 iOS 3.2。
      • 在 iOS 7 上这不起作用,因为 contentOffset 方法将在显示时再次调用。我正在寻找可行的解决方案。
      【解决方案5】:

      根据 Meir 的建议,这里的代码以编程方式删除选择(是的,我知道有一个选择菜单按钮也可以这样做,但我正在做一些时髦的事情)而不滚动文本视图。

      NSRange selectedRange = textView.selectedRange;
      textView.scrollEnabled = NO;
      // I'm deleting text. Replace this line with whatever insertions/changes you want
      textView.text = [textView.text
                      stringByReplacingCharactersInRange:selectedRange withString:@""];
      selectedRange.length = 0;
      // If you're inserting text, you might want to increment selectedRange.location to be
      // after the text you inserted
      textView.selectedRange = selectedRange;
      textView.scrollEnabled = YES;
      

      【讨论】:

      • 这个解决方案(在设置选定范围后设置 scrollEnabled,而不是之前)修复了我在设置 UITextView 的属性文本属性时遇到的“跳跃文本”问题!谢谢
      【解决方案6】:

      我找到了一个在 iOS 6 和 7(可能还有更早的版本)中可靠运行的解决方案。在 UITextView 的子类中,执行以下操作:

      @interface MyTextView ()
      @property (nonatomic) BOOL needToResetScrollPosition;
      @end
      
      @implementation MyTextView
      
      - (void)setText:(NSString *)text
      {
          [super setText:text];
          self.needToResetScrollPosition = YES;
      }
      
      - (void)layoutSubviews
      {
          [super layoutSubviews];
      
          if (self.needToResetScrollPosition) {
              self.contentOffset = CGPointMake(0, 0);
              self.needToResetScrollPosition = NO;
          }
      }
      

      其他答案在 iOS 7 中均无效,因为它会在显示时调整滚动偏移量。

      【讨论】:

      • self.contentOffset 对我来说始终是 0,0(这个子类在 iOS 7 下对防止滚动没有影响)。
      【解决方案7】:

      老问题,但我在 iOS 7 应用程序中遇到了同样的问题。需要在运行循环之后稍微更改 contentOffset。这是一个快速的想法。

      self.clueString = [self.level clueText];
      CGPoint point = self.clueText.contentOffset;
      self.clueText.attributedText = self.clueString;
      double delayInSeconds = 0.001; // after the run loop update
      dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
      dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
          [self.clueText setContentOffset:point animated:NO]; 
      });
      

      【讨论】:

        【解决方案8】:

        在 iOS 7 中。sizeThatFits 存在错误,并且 UITextView 中有换行符,我发现可行的解决方案是通过禁用滚动来包装它。像这样:

        textView.scrollEnabled = NO;
        CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
        textView.scrollEnabled = YES;
        

        奇怪的跳跃已经修复。

        【讨论】:

        • 在 iOS 8 上设置 textView.scrollEnabled = NO 中的 textDidChange: 似乎永远禁用滚动。然而在这个操作系统上,在viewDidLoad 期间切换一次scrollEnabled 似乎就足够了,即将其设置为NO 并直接返回YES
        【解决方案9】:

        以下两种解决方案不适用于 iOS 8.0。

        textView.scrollEnabled = NO;
        [textView.setText: text];
        textView.scrollEnabled = YES;
        

        CGPoint offset = textView.contentOffset;
        [textView.setText: text];
        [textView setContentOffset:offset];
        

        我为 textview 设置了一个委托来监视滚动事件,并注意到在我恢复偏移量的操作后,偏移量再次重置为 0。所以我改为使用主操作队列来确保我的还原操作发生在“重置为 0”选项之后。

        这是适用于 iOS 8.0 的解决方案。

        CGPoint offset = self.textView.contentOffset;
        self.textView.attributedText = replace;
        [[NSOperationQueue mainQueue] addOperationWithBlock: ^{
            [self.textView setContentOffset: offset];
        }];
        

        【讨论】:

        • 所以,这解决了我在模拟器中测试时的问题,但在我的设备上运行时并没有解决问题。这让我相信它仍然是一个竞争条件。我正在考虑子类化 uitextview 并覆盖 layoutsubviews 方法以在启用锁定属性时不运行。
        【解决方案10】:

        此决定适用于 iOS 8:

        let offset = textView.contentOffset
        textView.text = newText
        textView.layoutIfNeeded()
        textView.setContentOffset(offset, animated: false)
        

        必须准确调用setContentOffset:animated:,因为只有这样才能取消动画。 textView.contentOffset = offset 不会取消动画,也无济于事。

        【讨论】:

          【解决方案11】:

          为了编辑 UITextView 的文本,你需要更新它的 textStorage 字段:

          [_textView.textStorage beginEditing];
          
          NSRange replace = NSMakeRange(10, 2); //enter your editing range
          [_textView.textStorage replaceCharactersInRange:replace withString:@"ha ha$ "];
          
          //if you want to edit the attributes
          NSRange attributeRange = NSMakeRange(10, 5); //enter your editing attribute range
          [_textView.textStorage addAttribute:NSBackgroundColorAttributeName value:[UIColor greenColor] range:attributeRange];
          
          [_textView.textStorage endEditing];
          

          祝你好运

          【讨论】:

            【解决方案12】:

            我在 IOS9 中遇到了一个类似的问题,如果不一样的话。将某些文本的特征更改为 BOLD 会导致视图将选择滚动到视线之外。我通过在 setSelectedRange 之后添加对 scrollRangeToVisible 的调用来对此进行排序:

                [self setSelectedRange:range];
                [self scrollRangeToVisible:range];
            

            【讨论】:

            • 完美运行,没有明显的跳跃,非常简单!我不知道我是怎么错过的!
            【解决方案13】:

            最后试试这个,在 iOS 10 上检查过

            let offset = textView.contentOffset
            textView.attributedText = newValue
            OperationQueue.main.addOperation {
                self.textView.setContentOffset(offset, animated: false)
            }
            

            【讨论】:

              猜你喜欢
              • 2010-12-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-24
              • 2016-03-10
              • 1970-01-01
              相关资源
              最近更新 更多