Autolayout Breaks Unambiguous Constraints After dismissViewControllerAnimated:
| Originator: | xitsej | ||
| Number: | rdar://14250877 | Date Originated: | 6/24/13 |
| Status: | Open | Resolved: | No |
| Product: | iOS SDK | Product Version: | 6.1 |
| Classification: | UI | Reproducible: |
Summary:
After presenting and dismissing a view controller over another view controller containing a scroll view, the contents of that scroll view are shifted upwards, breaking the constraints set on those contents.
Steps to Reproduce:
1) Set up the following in a project:
- A view controller (viewController1)
- A scroll view in viewController1 that has constraints:
scrollView.top = viewController1.top,
scrollView.bottom = viewController1.bottom
scrollView.left = viewController1.left
scrollView.right = viewController1.right
- Put a column using a UIView in the scroll view with some contents (or a background color) that has constraints:
@"V:|-(>=0)-[_columnView]|"
column.width = 200
column.centerX = scrollView.centerX
columnView.Height = 3000
- Add a tap target (can be the column view) to a view that calls presentViewController:animated:YES completion: (to show a full screen, non-modal, viewController2) and after a few seconds delay, calls dismissViewControllerAnimated:YES completion:
2) Run the project and do the following:
- Scroll down any amount.
- Tap the tap target to present viewController2
- Once viewController2 has been dismissed, notice that the columnView is higher up in the column view than it should be. Its origin is above the top of the scroll view, breaking constraint: @"V:|-(>=0)-[_columnView]|".
- Note that the amount it moves up is dependent upon the amount you scrolled down initially. If you didn't scroll down at all, the column view isn't moved at all.
Expected Results:
The column view (subview of the scroll view) stays in the same place because the constraints have not changed.
Actual Results:
The columnView moves up a seemingly arbitrary amount within the scroll view depending on how far down you scrolled, breaking the constraints without an exception being thrown.
Regression:
This has been a bug for as long as auto layout has been available.
----------- SAMPLE CODE ---------- (put this in a UIViewController subclass)
#import "ViewController.h"
@interface ViewController () <UIScrollViewDelegate>
@property(nonatomic) UIScrollView *scrollView;
@property(nonatomic) UIButton *columnView;
@property(nonatomic) UIViewController *pvc;
@end
/*
Demonstrates an autolayout bug.
To replicate:
- Start app
- Scroll down any amount other than 0
- Press the red column (also a button)
- When the yellow view has been (automatically) dismissed, notice that the red view has moved up
in the scroll view, breaking the vertical constraint: @"V:|-(>=0)-[_columnView]|"
- To further verify the constraint is broken, put a breakpoint in the scrollViewDidScroll method
and check the frame of self.columnView. Notice that the origin's Y coordinate is negative.
- Note: The further you have scrolled down before pressing the red button, the further up the red
view will move after the yellow view is dismissed.
*/
@implementation ViewController
- (id)initialize {
return [super init];
}
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view.backgroundColor = [UIColor blueColor];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.backgroundColor = [UIColor greenColor];
self.scrollView.bounces = YES;
self.scrollView.alwaysBounceVertical = YES;
self.scrollView.delegate = self;
[self.view addSubview:self.scrollView];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraints:@[
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1
constant:0],
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0],
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1
constant:0],
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1
constant:0],
]];
CGFloat columnWidth = 200;
self.columnView = [[UIButton alloc] init];
[self.scrollView addSubview:self.columnView];
self.columnView.translatesAutoresizingMaskIntoConstraints = NO;
self.columnView.backgroundColor = [UIColor redColor];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_columnView);
[self.scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-(>=0)-[_columnView]|"
options:0
metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:@[
[NSLayoutConstraint constraintWithItem:self.columnView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:columnWidth],
[NSLayoutConstraint constraintWithItem:self.columnView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0],
[NSLayoutConstraint constraintWithItem:self.columnView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:3000]
]];
[self.columnView addTarget:self
action:@selector(presentOtherViewController)
forControlEvents:UIControlEventTouchUpInside];
//self.scrollView.contentInset = UIEdgeInsetsMake(5, 0, 100, 0);
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Put a breakpoint here to see the frame of the column view after pressing the button.
}
- (void)presentOtherViewController {
self.pvc = [[UIViewController alloc] init];
self.pvc.view = [[UIView alloc] initWithFrame:self.view.bounds];
self.pvc.view.backgroundColor = [UIColor yellowColor];
[self presentViewController:self.pvc animated:YES completion:nil];
[self performSelector:@selector(dismissOtherViewController) withObject:nil afterDelay:3];
}
- (void)dismissOtherViewController {
[self.pvc dismissViewControllerAnimated:YES completion:nil];
}
@end
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!