简体   繁体   English

如何在文本上方移动UITextView修正建议

[英]How to move UITextView correction suggestion above text

I'm using a UITextView to roughly replicate the SMS text box above the keyboard. 我正在使用UITextView粗略复制键盘上方的SMS文本框。 I'm using a UITextView instead of a field so that it can expand with multiple lines. 我正在使用UITextView而不是字段,以便它可以使用多行进行扩展。

The problem is that, in my UITextView, the correction suggestions pop up below the text, causing them to be partially obscured by the keyboard. 问题是,在我的UITextView中,修正建议会弹出文本下方 ,导致它们被键盘部分遮挡。

In the SMS app, the suggestions pop up above the text. 在SMS应用程序中,建议会在文本上方弹出。 The placement does not appear to be a property of UITextView, or UITextInputTraits. 展示位置似乎不是UITextView或UITextInputTraits的属性。

Any idea how to replicate this behavior? 知道如何复制这种行为吗? Thanks! 谢谢!

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. 问题是键盘是作为单独的UIWindow实现的,而不是作为主UIWindow中的视图实现的,因此使用它进行布局是棘手的。 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. 通过应用程序的-windows属性查找私有的UITextEffectsWindow窗口并找出其框架。 This is the keyboard 这是键盘
  • Hunt through the TextView's subviews to find the private UIAutocorrectInlinePrompt view. 通过TextView的子视图查找私有UIAutocorrectInlinePrompt视图。 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. 将该子视图移动到单独的包装器视图中(添加到TextView中),然后移动该包装器视图,使其位于上述键盘窗口的上方。

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. 我不知道为什么Apple甚至在他们的应用程序不得不解决它时仍然允许问题持续存在。

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. 通过在重写或调配的layoutSubViews中搜索UIAutocorrectInlinePrompt,可以更改校正的布局,使其显示在上方。 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. 您可以在不调用任何私有API的情况下执行此操作,方法是查找以您期望的方式放置的特定类的子视图。 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. 这个例子确定哪个视图是哪个视图,检查校正是否已经在文本上方并移动上面的校正,并在窗口上绘制它,使其不受UITextView本身的限制。 Obviously if apple change the underlying implementation then this will fail to move correction. 显然,如果苹果改变了底层实施,那么这将无法进行修正。 Add this to your overriden or swizzled layoutSubViews implementation. 将其添加到您的覆盖或混合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;
     }

    }
   }

  }

 }
}

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 将泡泡设置在文本的顶部......需要注意的是,您丢失了滚动条件,在我的情况下,由于仅仅出于字符限制的输入目的而使用文本字段而不是问题

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). 实际上,键盘只是使用 - [UITextInput textInputView]的结果来确定放置校正视图的位置(并询问您的视图是否支持校正)。 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: 请注意,您可能还需要更新 - [UITextInput firstRectForRange:]以使用窗口/设备的坐标系,因此您可以执行以下操作:

- (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). (在上面的上下文中,self是一个实现UITextInput的类)。

Putting the below method, adjustAutocorrectPromptView in layoutSubviews worked for me in portrait and landscape. 使用下面的方法,layoutSubviews中的adjustAutocorrectPromptView在纵向和横向中为我工作。 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;
        }
    }
}

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. 如果UITextView的底部清除键盘,您应该只需将UITextView的大小调整到足以看到更正。 The corrections themselves don't display outside of the UITextView's frame. 更正本身不会显示在UITextView框架之外。

If you want to mimic what you are getting in the SMS app (corrections above), you'll probably have to roll your own. 如果你想模仿你在短信应用程序中获得的内容(上面的更正),你可能需要自己动手。

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. 当键盘弹出时,确保视图控制器委托正在监听通知,以便您调整UITextView的大小,以便键盘不会遮盖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 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM