Support for Adaptive User Interfaces
Before iOS 8, most of your application interface/structure was determined by looking at:
Device type
Interface Orientation
Size
From iOS 8, the first two have been reshaped into traits and trait collection. The new way to structure the app is via:
Traits and trait collection
Size
What’s a trait collection?
A collection of traits, more specifically:
horizontalSizeClass
verticalSizeClass
userInterfaceIdiom
displayScale
What’s a (horizontal/vertical) size class? A trait that coarsely defines the space available for that axis (possible values: .compact or .regular)
Most UIKit objects conform to a new UITraitEnvironment that both provide the current trait collection and also lets us get notified when the trait environment changes:
@protocol UITraitEnvironment <NSObject>
@property UITraitCollection *traitCollection;
- (void)traitCollectionDidChange:
@endA parent view controller can override the trait collection for its child. For example, a UISplitViewController might give different traits to its primary and secondary view controllers.
How to:
@interface UIViewController <UITraitEnvironment>
- (void)setOverrideTraitCollection: forChildViewController:
- (UITraitCollection *)overrideTraitCollectionForChildViewController:
@endPresentation Controllers
For finer control on custom transitions, from iOS 8 we have UIPresentationController, which takes care of calling and retaining references to various parts of each transition (e.g., the containerView, the presentedView). Note that the presented view might not be the presented view controller view, as UIKit might add a dimming view/drop shadow around that view because that’s what the custom presentation demanded.
To overcome this UIViewControllerContextTransitioning now offers a new view(forKey:) besides the old viewController(forKey:), make sure to use this new function to figure out which views are actually participating in the animation.
@protocol UIViewControllerContextTransitioning
- (UIView *)viewForKey:(NSString *)key AVAILABLE_IOS(8_0);
@end
@interface UIPresentationController : NSObject
<UIAppearanceContainer, UITraitEnvironment, UIContentContainer>
@property(nonatomic, readonly) UIView *containerView;
- (UIView *)presentedView;
// we can decide whether we want the removal of the presenter views
- (BOOL)shouldRemovePresentersView;
// we can also decide whether the presented view will take over the screen.
// If you set this to NO, your presentation will no longer adapt.
- (BOOL)shouldPresentInFullScreen;
@endPrevious iPad-only styles are available on the iPhone (by default they adapt to full screen presentations when the horizontal trait is .compact).
New Presentation Styles:
UIModalPresentationOverFullscreenUIModalPresentationOverCurrentContextUIModalPresentationPopover
All presentation styles have an associated presentation controller that we can access to (to set their delegate):
-[UIViewController presentationController]
-[UIViewController popoverPresentationController]Thanks to the UIAdaptivePresentationControllerDelegate delegate we can
@protocol UIAdaptivePresentationControllerDelegate <NSObject>
@optional
// here for example we can tell the presentation to not adapt to full screen when the horizontal trait is compact
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:
// allows you to return a whole new view controller that should be presented in that style.
- (UIViewController *)presentationController:viewControllerForAdaptivePresentationStyle:
@endTransition Coordinators
Objects conforming to
UIViewControllerTransitionCoordinatorEvery transition coordinator has an associated transition
This coordinator can add animation blocks to the original animation
from iOS 8, transition coordinators also conform to
UIContentContainer(UIViewControllers also conform to this)
@protocol UIContentContainer <NSObject>
@property (nonatomic, assign) CGSize preferredContentSize;
- (void)preferredContentSizeDidChangeForChildContentContainer:
- (void)systemLayoutFittingSizeDidChangeForChildContentContainer:
- (void)sizeForChildContentContainer:withParentContainerSize:
- (void)willTransitionToTraitCollection:withTransitionCoordinator:
- (void)viewWillTransitionToSize:withTransitionCoordinator:
@endUse - (void)willTransitionToTraitCollection:withTransitionCoordinator: to react to resizing (replaces of old deprecated rotation callbacks).
When a trait changes, you can check UIViewControllerTransitionCoordinatorContext’s targetTransform to participate in the rotation animation (if there’s a rotation).
@protocol UIViewControllerTransitionCoordinatorContext
- (CGAffineTransform)targetTransform;
- (UIView *)viewForKey:(NSString *)key;
@endFor example:
- (void) viewWillTransitionToSize:(CGSize)s
withTransitionCoordinator:(UIVCTC)t {
orientation = [self orientationFromTransform: [t targetTransform]];
oldOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[self myWillRotateToInterfaceOrientation:orientation duration: duration];
[t animateAlongsideTransition:^(id <UIVCTCContext>) {
[self myWillAnimateRotationToInterfaceOrientation:orientation
duration:duration];
}
completion: ^(id <UIVCTCContext>) {
[self myDidAnimateFromInterfaceOrientation:oldOrientation];
}];
}Note:
The legacy rotation methods are still available (just don’t implement the new methods)
Most view controller transitions are immediate when called from within the dynamic scope of this method (e.g., if you do a push here, the push will be done immediately without animation)
Call
superin order to forward to descendant view controllersOnly necessary when you need to do a special size transition
