Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIScrollView wrong offset with Auto Layout

I have a fairly simple view configuration:

A UIViewController, with a child UIScrollView and a UIImageView in this UIScrollView. I set the UIImageView with a height sufficient to break out of the visible area (ie. higher to 1024pt), and set the Bottom space to superview constraint of my UIImageView to a fixed positive value (20 for example).

project layout

The whole setup works as expected, the image scrolls nicely in its parent. Except when the view is scrolled (the effect is more visible if you scrolled to the bottom of the view), then disappear, and appear again (you switched to another view and came back) the scrolling value is restored, but the content of the scroll view is moved to the outside top part of its parent view.

This is not simple to explain, I'll try to draw it: visual representation of previous paragraph

If you want to test/view the source (or the storyboard, I did not edit a single line of code). I put a little demo on my github: https://github.com/guillaume-algis/iOSAutoLayoutScrollView

I did read the iOS 6 changelog and the explanation on this particular topic, and think this is the correct implementation of the second option (pure auto layout), but in this case why is the UIScrollView behaving so erratically ? Am I missing something ?

EDIT: This is the exact same issue as #12580434 uiscrollview-autolayout-issue. The answers are just workarounds, as anyone found a proper way to fix this or is this a iOS bug ?

EDIT 2: I found another workaround, which keep the scroll position in the same state the user left it (this is an improvement over 12580434's accepted answer):

@interface GAViewController ()  @property CGPoint tempContentOffset;  @end   @implementation GAViewController  -(void)viewWillAppear:(BOOL)animated {     [super viewWillAppear:animated];      self.tempContentOffset = self.mainScrollView.contentOffset;     self.scrollView.contentOffset = CGPointZero; }  -(void)viewDidAppear:(BOOL)animated {     [super viewDidAppear:animated];      self.scrollView.contentOffset = self.tempContentOffset; } 

This basically save the offset in viewWillAppear, reset it to the origin, and then restore the value in viewDidAppear. The problem seems to occur between these two calls, but I can't find its origin.

like image 265
Guillaume Algis Avatar asked Mar 11 '13 18:03

Guillaume Algis


2 Answers

Yeah, something strange happened with UIScrollView in pure autolayout environment. Re-reading the iOS SDK 6.0 release notes for the twentieth time I found that:

Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints between the view and a view outside the scroll view’s subtree, such as the scroll view’s superview.

Solution

Connect your subview to the outer view. In another words, to the view in which scrollview is embedded.

As IB does not allow us set up constraints between the imageView and a view outside the scroll view’s subtree, such as the scroll view’s superview then I've done it in code.

- (void)viewDidLoad {     [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.     [self.view removeConstraints:[self.view constraints]];     [self.scrollView removeConstraints:[self.scrollView constraints]];     [self.imageView removeConstraints:[self.imageView constraints]];     [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];     [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];     [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];     [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]]; } 

And vau! It works!

like image 141
Mark Kryzhanouski Avatar answered Oct 04 '22 11:10

Mark Kryzhanouski


The edit didn't work for me. But this worked:

-(void)viewWillDisappear:(BOOL)animated {      [super viewWillDisappear:animated];       self.tempContentOffset = self.scrollView.contentOffset;      self.scrollView.contentOffset = CGPointZero; }  - (void)viewDidLayoutSubviews {      [super viewDidLayoutSubviews];      self.scrollView.contentOffset = self.tempContentOffset; } 
like image 42
Pion Avatar answered Oct 04 '22 09:10

Pion