Reputation: 2640
I am creating an iPhone application, and in one view I am planning to have a split screen. It will be a map view in the main area of the page, but I want to then display a table underneath the map that shows details of the nearest hits to the user.
So the layout in portrait view will be like this:
------------
| |
| |
| |
| |
| |
| |
| |
------------
| |
| |
| |
------------
With a 60/40 split between the top mapview area and the bottom table area.
When I rotate the phone, I need this layout to re-adjust and rotate sensibly, so that it adjusts to:
---------------------------
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
---------------------------
With the mapview in the left area and the table at the right.
And, of course, re-layout between the two views smoothly as the user rotates their device.
What is the best way to handle this in code? I am not sure what is the best way to re-layout the screen on rotation?
Is it good practice to just resize and move the two views using, eg for the top view:
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight*0.6)];
And then use the following to move and redraw this when I detect an orientation change:
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, screenWidth*0.6, screenHeight)];
Upvotes: 2
Views: 1262
Reputation: 543
You can achieve the exact same behaviour by using Autolayout with multiple Arrays of Constraints. Just save yourself a reference of the applied NSLayoutConstraint's. Lets Assume you have declared your Views as a property and synthesized them like listed below.
@interface YourFancySplitScreenViewController ()
@property (nonatomic, strong) AwesomeMapView* myAwesomeMapView;
@property (nonatomic, strong) CoolTableView* myCoolTableView;
@property (nonatomic, strong) NSMutableArray* portraitLayoutConstraints;
@property (nonatomic, strong) NSMutableArray* landscapeLayoutConstraint;
@end
@implementation YourFancySplitScreenViewController
@synthesize myAwesomeMapView, myCoolTableView;
@synthesize portraitLayoutConstraints, landscapeLayoutConstraint;
// [...]
@end
Just set up your Views and add them as Subviews to UIViewController.view. After this you fill up your Arrays portraitLayoutConstraints and landscapeLayoutConstraint with the appropriate Constraints.
- (void) getYourConstraintsRight {
// The Views you want to manipulate with your Constraints
NSDictionary* viewsDictionary = NSDictionaryOfVariableBindings(myAwesomeMapView, myCoolTableView);
self.portraitLayoutConstraints = [[NSMutableArray alloc] init];
self.landscapeLayoutConstraint = [[NSMutableArray alloc] init];
// Now just add The Constraints to your Arrays, eg. with constraintsWithVisualFormat
[self.portraitLayoutConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"Your terrific portrait Contstraints" options:0 metrics:0 views:viewsDictionary]];
[self.landscapeLayoutConstraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"Your even better landscape Contstraints" options:0 metrics:0 views:viewsDictionary]];
}
In my current Project I add the portraitLayoutConstraints to the view. After this I deactivate the Constraints from the same Array. Afterwards I can add the landscapeLayoutConstraint and deactivate them as well. This is for sure not the most elegant way of implement the intended behaviour, but it does the trick for me. Having my Constraints set, I can deactivate and activate them, as I need them.
- (void) addYourConstraintsToTheView {
// place some ugly code here
[self.view addConstraints:self.landscapeLayoutConstraint];
[NSLayoutConstraint deactivateConstraints:self.landscapeLayoutConstraint];
[self.view addConstraints:self.portraitLayoutConstraints];
[NSLayoutConstraint deactivateConstraints:self.portraitLayoutConstraints];
}
- (void) setYourConstraintsAccordingToTheUIInterfaceOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
[NSLayoutConstraint activateConstraints:self.landscapeLayoutConstraint];
}
if (orientation == UIInterfaceOrientationPortrait) {
[NSLayoutConstraint activateConstraints:self.portraitLayoutConstraints];
}
}
In my special Case I wanted to react to the change of the DeviceOrientation, so I added an Observer for UIApplicationWillChangeStatusBarOrientationNotification in my viewDidLoad and deactivate/activate the Constraints when the DeviceOrientation changes.
- (void)viewDidLoad {
[super viewDidLoad];
// Maybe you want to set your views here
// then add the Constraints
[self getYourConstraintsRight];
[self addYourConstraintsToTheView];
[self setYourConstraintsAccordingToTheUIInterfaceOrientation];
// Observe your Notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification_OrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)notification_OrientationWillChange:(NSNotification*)notification {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];;
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
[self animateToPortrait];
}
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
[self animateToLandscape];
}
}
- (void)animateToPortrait {
[UIView animateWithDuration:1.0 animations:^{
[NSLayoutConstraint deactivateConstraints: self.landscapeLayoutConstraint];
[NSLayoutConstraint activateConstraints: self.portraitLayoutConstraints];
}];
[self.view layoutIfNeeded];
}
- (void)animateToLandscape {
[UIView animateWithDuration:1.0 animations:^{
[NSLayoutConstraint deactivateConstraints: self.portraitLayoutConstraints];
[NSLayoutConstraint activateConstraints: self.landscapeLayoutConstraint];
}];
[self.view layoutIfNeeded];
}
// You want to remove the Observer
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisapper:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self.view];
}
Maybe this will help to find a solution for your Problem. Another difficulty I see in the available indicating range in Portrait Orientation. If you consider to hug the content inside a scrollview check out this answer of mine: Adding programatically created views into scrollview vertically. Also I would recommend the WWDC2015 Session on Mysteries of Auto Layout and the corresponding AstroLayout Sample code.
Upvotes: 1
Reputation:
You want your UI to adapt. Apple wants your UI to adapt.
You could write the code to handle orientation changes and lay the views out yourself, but you might miss an edge case, or it might not work on a future device or with upcoming functionality (such as split-screen multitasking).
Instead of trying to write code to handle this yourself, you may want to take advantage of UIStackView
.
Stack views let you leverage the power of Auto Layout, creating user interfaces that can dynamically adapt to the device’s orientation, screen size, and any changes in the available space.
Upvotes: 0