5

I have a UIScrollView which contains a button. When the button is pressed, I would like to scroll to the bottom of the view using scrollRectToVisible.

eg:

CGRect r = CGRectMake(0, myUIScrollView.contentSize.height - 1, 1, 1);
[myUIScrollView scrollRectToVisible:r animated:YES];

If I set animated to NO, everything works as expected, but if I set it to YES, I see really weird behaviour:

  • basically, nothing happens.
  • if I tap the button repeatedly, it may scroll a couple pixels, or may scroll all the way.
  • but if I scroll the view manually with a finger before pressing the button, it has a chance of scrolling to the bottom as expected, but it's not a sure thing.

I've printed _geScroll_Settings.contentSize, and it's as-expected.

I've also tried to delay the call to scrollRectToVisible by starting a timer, but the results are pretty much the same.

The scrollView is fairly vanilla. I'm creating it in interface builder. I am dynamically adding the scrollView's content at startup, and adjusting it's contentSize appropriately, but all that seems to be working fine.

Any thoughts?

vcsjones
  • 138,677
  • 31
  • 291
  • 286
orion elenzil
  • 4,484
  • 3
  • 37
  • 49
  • haven't figured this out, but i have to get it done, so i just started a timer and scroll it myself until it's at the right spot, using setContentOffset w/ animated:NO. works but :( – orion elenzil Aug 24 '11 at 00:15

2 Answers2

6

My bet is that scrollRectToVisible is crapping out because the visible area is not valid (1x1), or the y offset is just outside the bounds, have you tried setting it with the size of the visible area of the scrollView instead?

CGRect rectBottom = CGRectZero;
rectBottom.size = myUIScrollView.frame.size;
rectBottom.origin.y = myUIScrollView.contentSize.height - rectBottom.size.height;
rectBottom.origin.x = 0;

[myUIScrollView scrollRectToVisible:rectBottom animated:YES];

Sorry I can't help you out more, but I'm not on my Mac right now, so I can't run a test. The code above would create a CGRect of the exact size of what fits inside the scrollView visible portion, and the offset would be the last visible portion in it.

Dylan Bettermann
  • 753
  • 8
  • 14
Can
  • 8,502
  • 48
  • 57
  • interesting. i'll give that a try but i'm dubious. i did use rectangles of more normal size - eg the width of the scrollView and some height greater than one but less than the height of the scrollview, but it was no different. also tried rectangles in the middle of the contentSize rather than at the end. i'll let you know. on a side note, i didn't know about CGRectZero, thanks ! – orion elenzil Aug 24 '11 at 15:26
  • just getting back to you on this - i tried that and no luck. i finally ended up animating it myself by using 'animated:NO' and a timer. thanks anyhow. – orion elenzil Sep 20 '11 at 00:30
  • I've just run into this. Spent about 2 hours looking at everything *but* the scrollRectToVisible call. – Hot Licks Jul 03 '13 at 22:06
0

I encountered a similar problem, including the "If I set animated to NO, everything works as expected" part.

It turned out that on iOS 6 the UITextView auto scrolls its nearest parent UIScrollView to make the cursor visible when it becomes first responder. On iOS 7 there is no such behavior. The UIScrollView seems to get confused by two calls to to scrollRectToVisible at about the same time.

On iOS 6 my explicit call to scrollRectToVisible is ignored most of the time. It will only scroll to make the first line of the UITextView visible (the auto scroll) and not the whole thing as it does on iOS 7.

To test it, make a new single view app in Xcode 5, set its deployment target to 6.0 and use the code below for the ViewController.m. Run it in the iOS 6.1 simulator, scroll to make the UITextView hidden and tap anywhere on the screen. You might have to retry it a few times, but in most cases it will only make the first line visible. If you re-enable the WORKAROUD define the UITextView gets embedded in its own UIScrollView and the call to scrollRectToVisible works as expected.

#import "ViewController.h"

//#define WORKAROUND

@interface ViewController ()
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UITextView *textView;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTap)]];

    self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 240)];
    self.scrollView.contentSize = CGSizeMake(320, 400);
    self.scrollView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.scrollView];

#ifdef WORKAROUND
    UIScrollView* dummyScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(20, 280, 280, 100)];
    self.textView = [[UITextView alloc] initWithFrame:dummyScrollView.bounds];
    [dummyScrollView addSubview:self.textView];
    [self.scrollView addSubview:dummyScrollView];
#else
    self.textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 280, 280, 100)];
    [self.scrollView addSubview:self.textView];
#endif

    self.textView.backgroundColor = [UIColor grayColor];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewTap
{
    if (self.textView.isFirstResponder) {
        [self.textView resignFirstResponder];
    }
    else {
        [self.textView becomeFirstResponder];
    }
}

- (void)keyboardWasShown:(NSNotification*)aNotification
{
#ifdef WORKAROUND
    [self.scrollView scrollRectToVisible:CGRectInset(self.textView.superview.frame, 0, -10) animated:YES];
#else
    [self.scrollView scrollRectToVisible:CGRectInset(self.textView.frame, 0, -10) animated:YES];
#endif
}

@end
Sebastian Kirsche
  • 861
  • 2
  • 19
  • 36