如何将 UITextView 修正建议移到文本上方

发布于 2024-08-16 11:21:47 字数 224 浏览 3 评论 0原文

我正在使用 UITextView 大致复制键盘上方的 SMS 文本框。我使用 UITextView 而不是字段,以便它可以扩展多行。

问题是,在我的 UITextView 中,更正建议会在文本下方弹出,导致它们被键盘部分遮挡。

在短信应用程序中,建议会弹出在文本上方。该位置似乎不是 UITextView 或 UITextInputTraits 的属性。

知道如何复制这种行为吗?谢谢!

I'm using a UITextView to roughly replicate the SMS text box above the keyboard. I'm using a UITextView instead of a field so that it can expand with multiple lines.

The problem is that, in my UITextView, the correction suggestions pop up below the text, causing them to be partially obscured by the keyboard.

In the SMS app, the suggestions pop up above the text. The placement does not appear to be a property of UITextView, or UITextInputTraits.

Any idea how to replicate this behavior? Thanks!

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

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

发布评论

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

评论(7

笑梦风尘 2024-08-23 11:21:47

问题在于键盘是作为单独的 UIWindow 实现的,而不是作为主 UIWindow 内的视图实现的,因此布局很棘手。以下是一些正确方向的指示:

  • 通过应用程序的 -windows 属性查找私有 UITextEffectsWindow 窗口并找出其框架。这是键盘
  • 通过 TextView 的子视图查找私有 UIAuto CorrectInlinePrompt 视图。这是自动更正气泡。
  • 将该子视图移动到单独的包装视图(添加到 TextView 中),然后移动该包装视图,使其位于上述键盘窗口上方。

您会注意到上面两次提到“私人”。这包含所有相关的警告。我不知道为什么苹果公司允许这个问题持续存在,甚至他们的应用程序也必须解决这个问题。

The problem is that the Keyboard is implemented as a separate UIWindow, rather than as a view within the main UIWindow, so layout with it is tricky. Here are some pointers in the right direction:

  • Hunt through the application's -windows property to find the private UITextEffectsWindow window and figure out its frame. This is the keyboard
  • Hunt through the TextView's subviews to find the private UIAutocorrectInlinePrompt view. This is the autocorrect bubble.
  • Move that subview into a separate wrapper view (added to the TextView) and then move that wrapper view so it's above the above-mentioned keyboard window.

You'll notice two mentions of "private" above. That carries all the relevant caveats. I have no idea why Apple has allowed the problem to persist when even their apps have had to work around it.

丶视觉 2024-08-23 11:21:47

通过在覆盖或混合的layoutSubViews中搜索UIAuto CorrectInlinePrompt,可以更改更正的布局,使其显示在上方。您可以通过查找以您期望的方式定位的特定类的子视图来执行此操作,而无需调用任何私有 API。此示例计算出哪个视图是哪个视图,检查更正是否已经位于文本上方,并将更正移动到上方,并将其绘制在窗口上,以便它不受 UITextView 本身的限制。显然,如果苹果改变底层实现,那么这将无法进行修正。将其添加到您的重写或混合的layoutSubViews 实现中。

- (void) moveSpellingCorrection {
 for (UIView *view in self.subviews)
 {
  if ([[[view class] description] isEqualToString:@"UIAutocorrectInlinePrompt"])
  {
   UIView *correctionShadowView = nil; // [view correctionShadowView];

   for (UIView *subview in view.subviews)
   {
    if ([[[subview class] description] isEqualToString:@"UIAutocorrectShadowView"])
    {
     correctionShadowView = subview;
     break;
    }
   }

   if (correctionShadowView)
   {
    UIView *typedTextView = nil; //[view typedTextView];
    UIView *correctionView = nil; //[view correctionView];

    for (UIView *subview in view.subviews)
    {
     if ([[[subview class] description] isEqualToString:@"UIAutocorrectTextView"])
     {
      if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
      {
       correctionView = subview;
      }
      else
      { 
       typedTextView = subview;
      }
     }

    }
    if (correctionView && typedTextView)
    {

     CGRect textRect = [typedTextView frame];
     CGRect correctionRect = [correctionView frame];
     if (textRect.origin.y < correctionRect.origin.y)
     {
      CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0);
      [correctionView setTransform: moveUp];
      [correctionShadowView setTransform: moveUp];

      CGRect windowPos = [self convertRect: view.frame toView: nil ];
      [view removeFromSuperview];
      [self.window addSubview: view];
      view.frame = windowPos;
     }

    }
   }

  }

 }
}

By doing the search for the UIAutocorrectInlinePrompt in an overridden or swizzled layoutSubViews it is possible to alter the layout of the correction so that it appears above. You can do this without calling any private APIs by looking for the subs views of particular classes positioned in a way you'd expect them. This example works out which view is which, checks to see that the correction is not already above the text and moves the correction above, and draws it on the window so that it is not bounded by the UITextView itself. Obviously if apple change the underlying implementation then this will fail to move correction. Add this to your overriden or swizzled layoutSubViews implementation.

- (void) moveSpellingCorrection {
 for (UIView *view in self.subviews)
 {
  if ([[[view class] description] isEqualToString:@"UIAutocorrectInlinePrompt"])
  {
   UIView *correctionShadowView = nil; // [view correctionShadowView];

   for (UIView *subview in view.subviews)
   {
    if ([[[subview class] description] isEqualToString:@"UIAutocorrectShadowView"])
    {
     correctionShadowView = subview;
     break;
    }
   }

   if (correctionShadowView)
   {
    UIView *typedTextView = nil; //[view typedTextView];
    UIView *correctionView = nil; //[view correctionView];

    for (UIView *subview in view.subviews)
    {
     if ([[[subview class] description] isEqualToString:@"UIAutocorrectTextView"])
     {
      if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
      {
       correctionView = subview;
      }
      else
      { 
       typedTextView = subview;
      }
     }

    }
    if (correctionView && typedTextView)
    {

     CGRect textRect = [typedTextView frame];
     CGRect correctionRect = [correctionView frame];
     if (textRect.origin.y < correctionRect.origin.y)
     {
      CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0);
      [correctionView setTransform: moveUp];
      [correctionShadowView setTransform: moveUp];

      CGRect windowPos = [self convertRect: view.frame toView: nil ];
      [view removeFromSuperview];
      [self.window addSubview: view];
      view.frame = windowPos;
     }

    }
   }

  }

 }
}
眼睛会笑 2024-08-23 11:21:47

实际上,这样做

textview.scrollEnabled = NO;

会将气泡设置在文本顶部...需要注意的是,您会失去滚动功能,在我的情况下,这不是问题,因为文本字段仅用于字符限制的输入目的

Actually doing

textview.scrollEnabled = NO;

will set the bubble on top of the text... the caveat is that you lose scrolling, in my case it wasn't a problem due to havinng a textfield only for input purposes with character limit

月隐月明月朦胧 2024-08-23 11:21:47

实际上,键盘只是使用 -[UITextInput textInputView] 的结果来确定将校正视图放置在哪里(并询问您的视图是否支持校正)。因此,您需要做的就是:

- (UIView *)textInputView {
  for (UIWindow *window in [UIApplication sharedApplication].windows) {
    if ([window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")] && 
        window != self.window) {
      return window;
    }
  }

  // Fallback just in case the UITextEffectsWindow has not yet been created.
  return self;
}

请注意,您可能还需要更新 -[UITextInput firstRectForRange:] 以使用窗口/设备的坐标系,因此您可以执行以下操作:(

- (CGRect)firstRectForRange:(CoreTextTokenTextRange *)range {
  CGRect firstRect = [self firstRectForRangeInternal:range];

  return [self convertRect:firstRect toView:[self textInputView]];
}

在上面的上下文中, self 是实现 UITextInput 的类)。

Actually, the keyboard simply uses the result of -[UITextInput textInputView] to determine where to put the correction view (and to ask if your view supports correction). So all you need to do is this:

- (UIView *)textInputView {
  for (UIWindow *window in [UIApplication sharedApplication].windows) {
    if ([window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")] && 
        window != self.window) {
      return window;
    }
  }

  // Fallback just in case the UITextEffectsWindow has not yet been created.
  return self;
}

Note that you'll likely also need to update -[UITextInput firstRectForRange:] to use the coordinate system of the window / device, so you can do this:

- (CGRect)firstRectForRange:(CoreTextTokenTextRange *)range {
  CGRect firstRect = [self firstRectForRangeInternal:range];

  return [self convertRect:firstRect toView:[self textInputView]];
}

(In the above context, self is a class that implements UITextInput).

ˉ厌 2024-08-23 11:21:47

如果 UITextView 的底部清除了键盘,您应该能够将 UITextView 的大小调整到足够高以查看更正。更正本身不会显示在 UITextView 框架之外。

如果您想模仿短信应用程序中的内容(上面的更正),您可能需要自己动手。

If the bottom of your UITextView clears the keyboard, you should be able to just resize your UITextView to be tall enough to see the corrections. The corrections themselves don't display outside of the UITextView's frame.

If you want to mimic what you are getting in the SMS app (corrections above), you'll probably have to roll your own.

挽心 2024-08-23 11:21:47

使用下面的方法,在layoutSubviews中调整Auto CorrectPromptView在纵向和横向上对我有用。我有一个类别提供了视图中的底部和顶部方法,但您明白了。

NSArray * subviewsWithDescription(UIView *view, NSString *description)
{
    return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"class.description == '%@'", description]]];
}

- (void) adjustAutocorrectPromptView;
{
    UIView *autocorrectPromptView = [subviewsWithDescription(self, @"UIAutocorrectInlinePrompt") lastObject];

    if (! autocorrectPromptView)
    {
        return;
    }

    UIView *correctionShadowView = [subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectShadowView") lastObject];

    if (! correctionShadowView)
    {
        return;
    }

    UIView *typedTextView = nil; //[view typedTextView];
    UIView *correctionView = nil; //[view correctionView];

    for (UIView *subview in subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectTextView"))
    {
        if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
        {
            correctionView = subview;
        }
        else
        {
            typedTextView = subview;
        }
    }

    if (correctionView && typedTextView)
    {
        if (typedTextView.top < correctionView.top)
        {
            correctionView.bottom = typedTextView.top;
            correctionShadowView.center = correctionView.center;
        }
    }
}

Putting the below method, adjustAutocorrectPromptView in layoutSubviews worked for me in portrait and landscape. I have a category that provides the bottom and top methods on view but you get the idea.

NSArray * subviewsWithDescription(UIView *view, NSString *description)
{
    return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"class.description == '%@'", description]]];
}

- (void) adjustAutocorrectPromptView;
{
    UIView *autocorrectPromptView = [subviewsWithDescription(self, @"UIAutocorrectInlinePrompt") lastObject];

    if (! autocorrectPromptView)
    {
        return;
    }

    UIView *correctionShadowView = [subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectShadowView") lastObject];

    if (! correctionShadowView)
    {
        return;
    }

    UIView *typedTextView = nil; //[view typedTextView];
    UIView *correctionView = nil; //[view correctionView];

    for (UIView *subview in subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectTextView"))
    {
        if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
        {
            correctionView = subview;
        }
        else
        {
            typedTextView = subview;
        }
    }

    if (correctionView && typedTextView)
    {
        if (typedTextView.top < correctionView.top)
        {
            correctionView.bottom = typedTextView.top;
            correctionShadowView.center = correctionView.center;
        }
    }
}
数理化全能战士 2024-08-23 11:21:47

确保您的视图控制器委托在键盘弹出时正在侦听通知,以便您调整 UITextView 的大小,以便键盘不会遮挡 UITextView。这样你的修正就不会被键盘遮盖了。请参阅:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html" iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html

以下是该页面的代码副本,以防原始链接损坏:

// the amount of vertical shift upwards keep the Notes text view visible as the keyboard appears
#define kOFFSET_FOR_KEYBOARD                    140.0

// the duration of the animation for the view shift
#define kVerticalOffsetAnimationDuration        0.50

- (IBAction)textFieldDoneEditing:(id)sender
{
    [sender resignFirstResponder];
}

- (IBAction)backgroundClick:(id)sender
{
    [latitudeField resignFirstResponder];
    [longitudeField resignFirstResponder];
    [notesField resignFirstResponder];

    if (viewShifted)
    {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:kVerticalOffsetAnimationDuration];

        CGRect rect = self.view.frame;
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
        self.view.frame = rect;

        [UIView commitAnimations];

        viewShifted = FALSE;
    }       
}

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
    if (!viewShifted) {     // don't shift if it's already shifted

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:kVerticalOffsetAnimationDuration];

        CGRect rect = self.view.frame;      
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
        self.view.frame = rect;

        [UIView commitAnimations];

        viewShifted = TRUE;
    }
    return YES;
}

Make sure your view controller delegate is listening to the notification when the keyboard pops up so that you resize your UITextView so that the keyboard doesn't obscure the UITextView. Then your correction won't be obscured by the keyboard. See:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html

Here is a copy of the code from that page in case the original link is broken:

// the amount of vertical shift upwards keep the Notes text view visible as the keyboard appears
#define kOFFSET_FOR_KEYBOARD                    140.0

// the duration of the animation for the view shift
#define kVerticalOffsetAnimationDuration        0.50

- (IBAction)textFieldDoneEditing:(id)sender
{
    [sender resignFirstResponder];
}

- (IBAction)backgroundClick:(id)sender
{
    [latitudeField resignFirstResponder];
    [longitudeField resignFirstResponder];
    [notesField resignFirstResponder];

    if (viewShifted)
    {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:kVerticalOffsetAnimationDuration];

        CGRect rect = self.view.frame;
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
        self.view.frame = rect;

        [UIView commitAnimations];

        viewShifted = FALSE;
    }       
}

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
    if (!viewShifted) {     // don't shift if it's already shifted

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:kVerticalOffsetAnimationDuration];

        CGRect rect = self.view.frame;      
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
        self.view.frame = rect;

        [UIView commitAnimations];

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