Search code examples
iosiphoneuiscrollviewuitextviewimessage-extension

iMessage Extension: UITextView in Child View Controller, scrolling behaviour has multiple issues


I have a very basic view controller in an iMessage Extension. (Expanded view of course). I am aware that you can not have keyboard input int the Compact view...

The UITextView is constrained to the Element above it, and below it. It has a set height and width. The constraints are all in order, and are not broken.

All the options on the textview in interface builder have been left as DEFAULTS.

The following problems occur with the UITextView:

  1. When I begin editing, the cursor is barely visible. (on the bottom of the field, half way cut off vertically).

  2. Typing the first letter brings the cursor to the vertical centre of the Text View.

  3. Putting a new line and then a character, puts the cursor back down again.

  4. Then typing subsequent characters after that will bring the cursor to the Vertical Centre again. And every other character scrolls the whole text view to the bottom again. So you get this weird bounce up and down behaviour with every key stroke.

  5. The Scroll Bar visual hint on the right side does not reflect the proper height of the text view, when I scroll. (The scroll bar only goes down half way - of the text view's height.)

I expect the UITextView to work like this by default:

  1. Cursor should go to the TOP of the text view, when activated and editing.

  2. Text View should not jump when typing keys after a new line.

  3. Vertical Scroll Bar indicator should show the full range of the text field, and not half of it.

So, obviously the internal Scroll View inside the Text View has a problem figuring out the sizes of things.

Does anyone know, if this is an iMessage specific issue, or whether anyone has had this problem outside of iMessage as well, and how to fix?

My IB constraints (The second view down is the text view): enter image description here

Video Demo of the problem: https://youtu.be/1bkvHnkXLWM

UPDATE: I am using Child View Controllers to embed my View Controller inside the Root Controller. I have tried a blank Hello World application, and the UITextView works normally by default. So the issue, therefore is related to the way i'm embedding a Child View Controller.

This is the code I'm using to embed my Child View Controller:

- (void)showViewController:(UIViewController*)vcToShow isPop:(BOOL)isPop
{
    NSLog(@"Presenting Controller: %@", vcToShow);
    [self.activeVC willMoveToParentViewController:nil];
    [self addChildViewController:vcToShow];

    vcToShow.view.translatesAutoresizingMaskIntoConstraints = NO;
    vcToShow.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self.rootView addControls:@[vcToShow.view]
    align:VerticalAlignStretchToFullHeight];

    UIViewController* currentVC = self.activeVC;
    self.activeVC = vcToShow;
    vcToShow.view.alpha = 0.0f;

    [self transitionFromViewController:currentVC
        toViewController:vcToShow
        duration:0.3
        options:UIViewAnimationOptionTransitionNone
        animations:^{

            vcToShow.view.alpha = 1.0f;
        }
        completion:^(BOOL finished) {
            [currentVC removeFromParentViewController];
            [vcToShow didMoveToParentViewController:self];
            [self updateScreenState];

            if (isPop) {
                [self removeReferenceToController:currentVC];
            }
        }];
}

Solution

  • I have a solution.

    Instead of using Interface Builder to place my UITextView, I used my own programmatic constraint generation method to create and place the UITextView, only after ViewDidAppear.

    My suspicion is that at the time Interface builder rendered the Child VC's view, it did not have everything populated that the UITextView's internal scroll view needed. So the scroll view got initialized with wrong dimension values, and therefore would glitch out on anything scrolling related. After doing everything in viewDidAppear programmatically (in the Child VC), the text view now scrolls properly, and the cursor is always at the beginning, working as expected.

    The update code:

    1. Took UITextView out of interface builder completely.

    2. Added it programmatically (called from viewDidAppear):

      self.textView = [[UITextView alloc] init];
      self.textView.translatesAutoresizingMaskIntoConstraints = NO;
      
      CGFloat topPadding = self.instructionLabel.frame.origin.y + self.instructionLabel.frame.size.height + TEXT_VIEW_TOP_PADDING;
      
      // This custom method generates Visual Constraint format for me
      // so i don't have to write manual individual constraint syntax. 
      [self.rootView addControls:@[self.textView]
          align:VerticalAlignTop
          withHeight:TEXT_VIEW_HEIGHT
          verticalPadding: 0
          horizontalPadding: 20
          topPadding: topPadding];