In the context of the iPhone:

I have a UIScrollView containing a UIImage. When the user taps the screen inside the UIImage, a UITextField is added where the user touched. The user can edit this UITextField, and the text field will automatically resize itself based on whether text was added or deleted.

When the a UITextField being edited increases its width, the scrollview automatically scrolls to show the increased width.

The problem comes in because the automatic scrolling of the textfield doesn't respect the y-value of the screen

For example, say the user added a text field to the bottom of the image. When they go to edit that text field, the keyboard will show, hiding the text field. I have code in place to scroll the screen to show the text field. The problem comes in when the user enters so much text that that text field extends past the edge of the screen. When this happens, the screen scrolls horizontally to fit the wider text, but also vertically - the vertical scrolling ends up hiding the textfield, basically nullifying anything I did to show the text field.

Code to show the text field if it's hidden by the keyboard:

- (void)keyboardWasShown:(NSNotification*)notification
    NSDictionary* info = [notification userInfo];
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    self.offset = self.contentOffset;

    CGRect frame = self.frame;
    // self.activeField is the name of the field that is the current first responder - this just adds a little bit of padding
    frame.size.height -= keyboardSize.height + (self.activeField.frame.size.height * 2);

    if (!CGRectContainsPoint(frame, self.activeField.frame.origin)) {
        CGPoint scrollPoint = CGPointMake(self.offset.x, self.activeField.frame.origin.y - keyboardSize.height + (activeField.frame.size.height * 2));
    [self setContentOffset:scrollPoint animated:YES];

Here is the code to increase the size of the text field:

    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    CGSize stringSize = [string sizeWithFont:textField.font];
    CGSize newSize = [newString sizeWithFont:textField.font];

    // Make textField wider if we're close to running up against it
    if (newSize.width > (textField.frame.size.width - self.widthOffset)) {
        CGRect textFieldFrame = textField.frame;
        if (stringSize.width > self.widthOffset) {
            textFieldFrame.size.width += stringSize.width;
        textFieldFrame.size.width += self.widthOffset;
        textField.frame = textFieldFrame;

    // Shrink the textField if there is too much wasted space
    if ((textField.frame.size.width - newSize.width) > self.widthOffset) {
        CGRect textFieldFrame = textField.frame;
        textFieldFrame.size.width = newSize.width + self.widthOffset;
        textField.frame = textFieldFrame;
    return YES;

The question is: How do I get the UIScrollView to respect the y-value of itself when automatically scrolling?

  • 2
    Awesome question ! – Legolas Jun 5 '11 at 4:33
  • It would help if you can provide a link with a sample project. – Deepak Danduprolu Jun 12 '11 at 9:54

Basically setFrame of UIScrollView will readjust the scrollview offset, done by _adjustContentOffsetIfNecessary. As that method is private and not documented, there is very little we can guess on how the adjustment will happen. There are two ways to stop the unnecessary scrolling or wrong offset being set:

1) reset the UIScrollView offset after applying setFrame. This you can do, if you are modifying the frame of UIScrollView intentionally based on some calculations.

CGPoint oldOffset = [scrollView contentOffset];         
scrollView.frame = CGRectMake(newFrame);
[scrollView setContentOffset:oldOffset];        

2) apply offset changes without animation. In your keyboardWasShown , change [self setContentOffset:scrollPoint animated:YES]; to
[self setContentOffset:scrollPoint animated:NO];

Reason: when more than one offset is applied with animation on, the result offset is ambiguous. Here the internal method(_adjustContentOffsetIfNecessary) applies an offset change and the other one is done by your code. You can notice this if you try to log all the offsets being applied in the UIScrollView delegate method:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
    NSLog(@" offset: %@", NSStringFromCGPoint(scrollView.conentOffset))

Let me know if this helps.

  • @Hooray Im Helping did the suggestion helped ? If not please update – Tatvamasi Jun 13 '11 at 21:08
  • Sorry for the long response time. This didn't resolve it. I overrode the setContentOffset methods and had a logging statement inside them - they weren't being called, which seems to indicate that it's a parent class doing the scrolling. – Hooray Im Helping Jun 19 '11 at 19:39

One possible workaround could be to respond to the scrollViewDidScroll delegate method check to see if the UITextField is hidden again, then re-scroll if necessary. Seems like a bit of a hack, but it sounds like the UIScrollView auto scrolling behavior is what's getting in your way, and if there's no way to directly affect it, the only option is to work around it. There is also the disadvantage, however, that if you do this then it appears to scroll twice.

If the auto-scrolling behavior happens only when the UITextField expands beyond the edge of the screen, you could also move the field to stay completely visible if it looks like it's going to expand beyond the edge of the screen.

  • If it comes down to it, I'll resort to a hack like this, but I'd rather not. +1 for a good answer anyway. – Hooray Im Helping Jun 19 '11 at 19:40

Instead of changing the content offset, change the scroll view's display frame.

- (void)keyboardWill:(NSNotification*)notification
    // Update the scroll view's frame to the region not covered by the keyboard.
    self.frame = CGRectMake(self.fullSizedFrame.origin.x, 
                            self.fullSizedFrame.size.height - keyboardSize.height);

- (void)keyboardWillHide:(NSNotification*)notification
    // Set the frame back to the original frame before the keyboard was shown.
    self.frame = self.fullSizedFrame;

If you don't allow the user to change screen orientation, then fullSizedFrame could be set to the original frame of the view when it is first displayed. If changes in orientation are allowed, you'll need to calculate the appropriate value for fullSizedFrame based on the orientation.

  • This didn't work. I'm already offsetting the height of the UIScrollView's frame by the height of the keyboard. – Hooray Im Helping Jun 10 '11 at 2:22

Your Answer

