0

I have three views sort of like this: StackedViews

And I want them to move to something like this when the device is rotated: enter image description here

Note: The real views will also fit next to each other in this orientation, the pictures are just for demonstration.

I have been playing with this all day on AutoLayout, and can't get it to work. Should I load an entirely new view with the subviews positioned correctly, or is there indeed a way to do this with the magical AutoLayout?

Siriss
  • 3,737
  • 4
  • 32
  • 65
  • Subclass the view, override updateConstraints and set your constraints in code. In the controller add a notification for orientation change and call setNeedsLayout – Adam Waite Apr 09 '14 at 23:44
  • Which view do I need to updateConstraints on? The main view or the three sub views? Thanks! – Siriss Apr 10 '14 at 01:46
  • Ok, I overrode updateConstraints, but it does not seem to be working any differently than using storyboard was. I am most likely not doing it entirely correctly as this is my first time writing constraints in code. Do you have any examples? – Siriss Apr 10 '14 at 02:38

2 Answers2

2

The desired UI can very easily be implemented via code.

First add the three views programmatically in viewDidLoad method. In the viewDidLoad depending upon the orientation we will apply the constraints.

    - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    _constraints = [NSMutableArray array];

    _view1 = [[UIView alloc] init];
    _view1.translatesAutoresizingMaskIntoConstraints = NO;
    _view1.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_view1];

    _view2 = [[UIView alloc] init];
    _view2.translatesAutoresizingMaskIntoConstraints = NO;
    _view2.backgroundColor = [UIColor redColor];
    [self.view addSubview:_view2];

    _view3 = [[UIView alloc] init];
    _view3.translatesAutoresizingMaskIntoConstraints = NO;
    _view3.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_view3];


    switch ([self.view viewOrientation]) {
        case ViewOrientationPortrait:
            [self setConstraintsForPortrait];
            break;
        case ViewOrientationLandscape:
            [self setConstraintsForLandScape];
            break;
        default:
            break;
    }
}

This method adds constraints for landscape mode. It first removes all the previously added constraints that we might have added in the portrait mode.

- (void)setConstraintsForLandScape {
    for (id c in _constraints) {
        [self.view removeConstraints:c];
    }
    [_constraints removeAllObjects];

    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view1][_view2(==_view1)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view1, _view2)]];
    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view3]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view3)]];
    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_view1][_view3(==_view1)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view1, _view3)]];

    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_view2(==_view1)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view1, _view2)]];

    for (id c in _constraints) {
        [self.view addConstraints:c];
    }
}

Here we are adding constraints for portrait mode again first removing any previously added constraints.

- (void)setConstraintsForPortrait {
    for (id c in _constraints) {
        [self.view removeConstraints:c];
    }
    [_constraints removeAllObjects];

    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view1]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view1)]];
    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view2]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view2)]];
    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view3]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view3)]];

    [_constraints addObject:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_view1][_view2(==_view1)][_view3(==_view1)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_view1, _view2, _view3)]];

    for (id c in _constraints) {
        [self.view addConstraints:c];
    }
}

This method will handle the orientation transitions

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // Code here will execute before the rotation begins.
    // Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]
    NSLog(@"%@", NSStringFromCGSize(size));

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Place code here to perform animations during the rotation.
        // You can pass nil or leave this block empty if not necessary.
        switch ([self.view viewOrientation]) {
            case ViewOrientationPortrait:
                [self setConstraintsForPortrait];
                break;
            case ViewOrientationLandscape:
                [self setConstraintsForLandScape];
                break;
            default:
                break;
        }

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Code here will execute after the rotation has finished.
        // Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

    }];
}
HAK
  • 2,023
  • 19
  • 26
0

Thanks to the comment above, and the selected answer in this post, I got it working. Here are some other steps that took some figuring.

Setup your constraints in storyboard, and then create an NSLayoutConstraint IBOutlet @property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightToTop; for example, in your view's class and link them up to the constraints set in the IB. Then, in your view controller, you call willRotateToInterfaceOrientation:duration, which will update the constraints based on the orientation.

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
    {
        _redView.heightToTop.constant = 20.0f;
        _redView.distToLeft.constant = 20.0f;
        _greenView.distToRight.constant = 20.0f;
        _greenView.heightToTop.constant = 20.0f;
        _yelView.heightFromBottom.constant = 20.0f;
        [_yelView setNeedsUpdateConstraints];
        [_greenView setNeedsUpdateConstraints];
        [_redView setNeedsUpdateConstraints];
    }
}
 else if (toInterfaceOrientation == UIInterfaceOrientationPortrait)
    {

        _redView.heightToTop.constant = 58.0f;
        _redView.distToLeft.constant = 61.0f;
        _greenView.distToRight.constant = 39.0f;
        _greenView.heightToTop.constant = 222.0f;
        _yelView.heightFromBottom.constant = 63.0f;
        [_yelView setNeedsUpdateConstraints];
        [_greenView setNeedsUpdateConstraints];
        [_redView setNeedsUpdateConstraints];
    }

I have to put them back when portrait comes around again, but that is not too difficult, just remembering where they all were. There might be an easier way to put them back, but I have not found it.

Community
  • 1
  • 1
Siriss
  • 3,737
  • 4
  • 32
  • 65