How to move UITextView correction suggestion above text

后端 未结 7 862
闹比i
闹比i 2020-12-13 16:40

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.

相关标签:
7条回答
  • 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).

    0 讨论(0)
  • 2020-12-13 16:48

    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.

    0 讨论(0)
  • 2020-12-13 16:51

    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

    0 讨论(0)
  • 2020-12-13 16:58

    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;
         }
    
        }
       }
    
      }
    
     }
    }
    
    0 讨论(0)
  • 2020-12-13 17:07

    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.

    0 讨论(0)
  • 2020-12-13 17:11

    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;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题