UITableViewCell 中不可滚动的 UITextView - 如何确定光标 y 坐标

发布于 2024-10-20 20:49:41 字数 372 浏览 8 评论 0原文

我在 UITableView 单元格内使用 UITextView。我已禁用 UITextView 的滚动功能。当用户键入时,我有逻辑自动增长 UITextView 和 UITableView 单元格。我还有自动滚动 UITableView 并保持新输入的文本可见的逻辑。我通过调用 [tableView rollRectToVisible:animated:] 来做到这一点,只要光标位于文本的最末尾,这一切都可以正常工作,因为我将简单地滚动到末尾(tableView.contentSize.height)。

当用户决定将光标放置在文本中的其他位置(例如块的中间)并再次开始输入时,问题就出现了。此时,我无法再将新输入的文本保留在视图中,因为我无法确定光标所在的 y 坐标。

任何帮助将不胜感激!

I'm using a UITextView inside a UITableView cell. I have scrolling disabled for the UITextView. When the user types, I have logic to auto grow both the UITextView and UITableView cell. I also have logic to auto scroll the UITableView and keep the newly typed text visible. I do this by calling [tableView scrollRectToVisible:animated:] This all works fine, as long as the cursor is at the very end of the text, since I will simply scroll to the end (tableView.contentSize.height).

The problem comes, when the user decides to position the cursor elsewhere in the text (say in the middle of their block), and begin typing again. At this point, I can no longer keep the newly typed text in view, because I can't determine what y coordinate the cursor is at.

Any help would be greatly appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

£冰雨忧蓝° 2024-10-27 20:49:41

好的,我终于实现了它,并且更容易,您不需要像我之前所说的那样使用 contentOffset,它可以工作,但有一些意想不到的问题。诀窍是使用辅助 UITextView 来计算光标上方文本的高度。

这是想法(我测试了它并且有效):

  • 创建辅助 UITextView (_auxTextView)

  • 为主文本视图实现 textViewDidChange 委托。

表视图控制器应通过使用带有给定偏移量的 scrollRectToVisible 滚动表视图来实现 cellDidChange。确切的计算取决于具体情况,但这里重要的是调用 beginUpdateendUpdate 以便强制更新应基于属性 rowHeight 的行高。

这是代码的一部分(抱歉,我无法发布实际代码,因为它位于一个大上下文中)。

    -(void)textViewDidChange:(UITextView *)textView {
      if(_prevHeight != self.textHeight) {
        _prevHeight = self.textHeight;
        NSRange range;
        range.location = 0;
        range.length = textView.selectedRange.location;
        NSString *str = [_textView.text substringWithRange:range];
        _auxTextView.text = str;
        [delegate cellDidChange:self offset:_auxTextView.contentSize.height];
      }
    }

   -(CGFloat)rowHeight {
      return self.textHeight + somePadding;
    }

   -(CGFloat)textHeight {
     return fmaxf(_textView.contentSize.height, someMinimumHeight);
   }

OK, I finally implemented it and there is much easier, you don't need to use contentOffset as I said before, It works but has some unexpected problems. The trick is using the auxiliar UITextView for calculating the height of the text above the cursor.

Here is the idea (I tested it and works):

  • Create an auxiliar UITextView (_auxTextView)

  • Implement the textViewDidChange delegate for the main text view.

The table view controller should implement cellDidChange by scrolling the table view using scrollRectToVisible with the given offset. The exact calculations deppend on the case but the important thing here is calling beginUpdate and endUpdate in order to force updating the row height that should be based in the property rowHeight.

Here is part of the code (sorry, I can't post the actual code because is inside a big context).

    -(void)textViewDidChange:(UITextView *)textView {
      if(_prevHeight != self.textHeight) {
        _prevHeight = self.textHeight;
        NSRange range;
        range.location = 0;
        range.length = textView.selectedRange.location;
        NSString *str = [_textView.text substringWithRange:range];
        _auxTextView.text = str;
        [delegate cellDidChange:self offset:_auxTextView.contentSize.height];
      }
    }

   -(CGFloat)rowHeight {
      return self.textHeight + somePadding;
    }

   -(CGFloat)textHeight {
     return fmaxf(_textView.contentSize.height, someMinimumHeight);
   }
殊姿 2024-10-27 20:49:41

我已经设法解决了这类问题(至少在大多数情况下),但这可能很棘手。获取 y 坐标并不难。可以抑制系统在您的桌子上触发的杂散滚动消息。

我采取的方法是这样的:

  • 在 UITextView 的委托中实现 textViewDidChangeSelection:
  • 从 textView 和 selectedRange 获取文本,
  • 您可能需要检查所选范围是否不 > >文本长度(有时可以;如果你认为不可能,你可能会遇到奇怪的崩溃......我就是这么做的)
  • 截断字符串,这样你就可以得到从开始到选择点的字符串
  • 计算高度UITextView 的长度为该长度的字符串。使用类似的东西:

    textHeight = [workingString sizeWithFont: [self screenFont] constrainedToSize: CGSizeMake([self editWidth]-16.0, CGFLOAT_MAX)].height;

  • -16.0 对于补偿默认边框很重要。我在 16.0 上取得了很好的成功,但没有记录。可能有更好的方法来计算补偿,但我还没有解决。

  • 此时我手动滚动 tableView。它是 UIScrollView 的子类,因此响应 setContentOffset: 但是,为了让一切顺利工作,我对 UITableView 进行了子类化,并让它有时忽略 contentOffset: 和 contentOffset:animated: 消息。系统会生成这些文件,有时它会在您不需要它们时生成。 (这就是我上面所说的“棘手”的意思)。

可能有一种更简单的方法来实现这一目标。我希望如此。如果是这样,那就有人发布了。

这是我的应用程序中的一些(有点难看)代码(仍在进行中)。它使用我的应用程序中的一些其他类/类别,但希望您可以轻松地了解正在发生的事情。这可能不太符合您的问题,但希望这有一定用处。

- (void) textViewDidChangeSelection: (UITextView *)textView;
{
    if (!checkSelectionVisible) return; // bail most of the time
    checkSelectionVisible = NO; // this is a one off

    EditController* theEditController = [self editController];
    ParagraftTableView* theTableView = (ParagraftTableView*)[theEditController tableView];

    ParagraphCell* currentCell = (ParagraphCell*) [theTableView cellForRowAtIndexPath: [NSIndexPath indexPathForRow:[self paragraphNumber] inSection:0]];

    CGPoint cellOrigin = currentCell.origin;
    CGPoint contentOffset = [theTableView contentOffset];
    NSRange selectedRange = [textView selectedRange];
    NSString* text = [textView text];
    if (selectedRange.location > [text length])
    {
        selectedRange.location = 0; // of out of bounds set to start
        NSLog(@"In textViewDidChangeSelection - selection out of bounds. Setting to start of paragraph.");
    }

    CGFloat textHeight = [[Formats shared] heightForText: [text substringToIndex: selectedRange.location]]; 

    if ((cellOrigin.y + textHeight) > ([theTableView height]+ [theTableView contentOffset].y))
    {
        NSLog(@"Selection point is off screen. Scroll into view.");

        CGFloat txHeight = [[[Formats shared] screenFont] pointSize];
        CGFloat newOffset = cellOrigin.y + textHeight - ([theTableView height] - 3*txHeight);
        contentOffset.y = newOffset;

        [theTableView setContentOffset:contentOffset];
    }
}

I have managed to solved this sort of problem (at least for most cases), but it can be tricky. Getting the y co-ordinate is not so hard. Suppressing stray scroll messages that the system fires at your table can be.

The approach I have taken goes like this:

  • in the delegate for your UITextView implement textViewDidChangeSelection:
  • get the text from the textView and the selectedRange
  • you may need to check that the selected range is not > text length (sometimes it can be; if you believe it couldn't possible, you may get weird crashes... I did)
  • truncate the string, so you get the string from the start to the selection point
  • calculate the height of a UITextView for a string of that length. Use something like:

    textHeight = [workingString sizeWithFont: [self screenFont] constrainedToSize: CGSizeMake([self editWidth]-16.0, CGFLOAT_MAX)].height;

  • the -16.0 is important to compensate for the default border. I've had good success with 16.0, but it's not documented. There may be a better way to figure out the compensation, but I've not worked it out.

  • at this point I manually scroll the tableView. It is a subclass of UIScrollView, so responds to setContentOffset: However, to get everything to work smoothly, I sub classed UITableView and got it to sometimes ignore contentOffset: and contentOffset:animated: messages. The system generates these and sometimes it does when you don't want them. (That's what i mean above by 'tricky').

There may be a much simpler way to achieve this. I hope so. And if so, that someone posts it.

Here's a bit of (slightly ugly) code from my app (still in work in progress). It uses some other classes / categories from my app, but hopefully you can follow what's going on readily enough. This might not quite match your issue, but hopefully this is of some use.

- (void) textViewDidChangeSelection: (UITextView *)textView;
{
    if (!checkSelectionVisible) return; // bail most of the time
    checkSelectionVisible = NO; // this is a one off

    EditController* theEditController = [self editController];
    ParagraftTableView* theTableView = (ParagraftTableView*)[theEditController tableView];

    ParagraphCell* currentCell = (ParagraphCell*) [theTableView cellForRowAtIndexPath: [NSIndexPath indexPathForRow:[self paragraphNumber] inSection:0]];

    CGPoint cellOrigin = currentCell.origin;
    CGPoint contentOffset = [theTableView contentOffset];
    NSRange selectedRange = [textView selectedRange];
    NSString* text = [textView text];
    if (selectedRange.location > [text length])
    {
        selectedRange.location = 0; // of out of bounds set to start
        NSLog(@"In textViewDidChangeSelection - selection out of bounds. Setting to start of paragraph.");
    }

    CGFloat textHeight = [[Formats shared] heightForText: [text substringToIndex: selectedRange.location]]; 

    if ((cellOrigin.y + textHeight) > ([theTableView height]+ [theTableView contentOffset].y))
    {
        NSLog(@"Selection point is off screen. Scroll into view.");

        CGFloat txHeight = [[[Formats shared] screenFont] pointSize];
        CGFloat newOffset = cellOrigin.y + textHeight - ([theTableView height] - 3*txHeight);
        contentOffset.y = newOffset;

        [theTableView setContentOffset:contentOffset];
    }
}
水晶透心 2024-10-27 20:49:41

实际上有一种更简单的方法:)

只需添加一个隐藏的 UITextView 并将对主文本视图所做的所有更改镜像到隐藏的 UITextView 中即可。隐藏的文本视图应该具有固定的高度,因此它会自动滚动并更改 contentOffset 属性。 contentOffset 正是您计算光标垂直位置所需的。

祝你好运!

There is actually a simpler way to do it :)

Just add a hidden UITextView and mirror all the changes made to the main text view in the hidden one. The hidden text view should have a fixed height, so it will scroll automatically changing the contentOffset property. And contentOffset is exactly what you need in order to calculate the vertical position of the cursor.

Goog luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文