blob: 5c33c43839ddb7488bc55d61d90f07948478fd28 [file] [log] [blame] [edit]
// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "MDCBottomDrawerViewController.h"
#import "MDCBottomDrawerTransitionController.h"
#import "MaterialUIMetrics.h"
#import "private/MDCBottomDrawerHeaderMask.h"
@interface MDCBottomDrawerViewController () <MDCBottomDrawerPresentationControllerDelegate>
/** The transition controller. */
@property(nonatomic) MDCBottomDrawerTransitionController *transitionController;
@property(nonatomic) MDCBottomDrawerHeaderMask *maskLayer;
@end
@implementation MDCBottomDrawerViewController {
NSMutableDictionary<NSNumber *, NSNumber *> *_topCornersRadius;
BOOL _isMaskAppliedFirstTime;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nil bundle:nil];
if (self) {
[self commonMDCBottomDrawerViewControllerInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonMDCBottomDrawerViewControllerInit];
}
return self;
}
- (void)commonMDCBottomDrawerViewControllerInit {
_transitionController = [[MDCBottomDrawerTransitionController alloc] init];
_topCornersRadius = [NSMutableDictionary dictionary];
_topCornersRadius[@(MDCBottomDrawerStateCollapsed)] = @(0);
_maskLayer = [[MDCBottomDrawerHeaderMask alloc] initWithMaximumCornerRadius:0
minimumCornerRadius:0];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
if (!_isMaskAppliedFirstTime) {
_maskLayer.minimumCornerRadius = [self minimumCornerRadius];
[_maskLayer applyMask];
_isMaskAppliedFirstTime = YES;
}
}
- (id<UIViewControllerTransitioningDelegate>)transitioningDelegate {
return self.transitionController;
}
- (UIModalPresentationStyle)modalPresentationStyle {
return UIModalPresentationCustom;
}
- (void)setTrackingScrollView:(UIScrollView *)trackingScrollView {
_trackingScrollView = trackingScrollView;
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
bottomDrawerPresentationController.trackingScrollView = trackingScrollView;
}
// Rather than have the client manually disable scrolling on the internal scroll view for
// the drawer to work properly, we can disable it if a trackingScrollView is provided.
[trackingScrollView setScrollEnabled:NO];
}
- (void)setTopCornersRadius:(CGFloat)radius forDrawerState:(MDCBottomDrawerState)drawerState {
_topCornersRadius[@(drawerState)] = @(radius);
if (drawerState == MDCBottomDrawerStateCollapsed) {
_maskLayer.maximumCornerRadius = radius;
} else {
_maskLayer.minimumCornerRadius = [self minimumCornerRadius];
}
}
- (CGFloat)minimumCornerRadius {
return [self contentReachesFullScreen]
? [self topCornersRadiusForDrawerState:MDCBottomDrawerStateFullScreen]
: [self topCornersRadiusForDrawerState:MDCBottomDrawerStateExpanded];
}
- (CGFloat)topCornersRadiusForDrawerState:(MDCBottomDrawerState)drawerState {
NSNumber *topCornersRadius = _topCornersRadius[@(drawerState)];
if (topCornersRadius != nil) {
return (CGFloat)[topCornersRadius doubleValue];
}
return 0;
}
- (void)setHeaderViewController:(UIViewController<MDCBottomDrawerHeader> *)headerViewController {
_headerViewController = headerViewController;
_maskLayer.view = headerViewController.view;
}
- (void)setContentViewController:(UIViewController *)contentViewController {
_contentViewController = contentViewController;
if (!_headerViewController) {
_maskLayer.view = contentViewController.view;
}
}
- (void)setScrimColor:(UIColor *)scrimColor {
_scrimColor = scrimColor;
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
bottomDrawerPresentationController.scrimColor = scrimColor;
}
}
- (BOOL)isAccessibilityMode {
return UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning();
}
- (BOOL)isMobileLandscape {
return self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;
}
- (BOOL)shouldPresentFullScreen {
return [self isAccessibilityMode] || [self isMobileLandscape];
}
- (BOOL)contentReachesFullScreen {
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
return bottomDrawerPresentationController.contentReachesFullscreen;
}
return [self shouldPresentFullScreen];
}
- (void)setTopHandleHidden:(BOOL)topHandleHidden {
_topHandleHidden = topHandleHidden;
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
bottomDrawerPresentationController.topHandleHidden = topHandleHidden;
}
}
- (void)setTopHandleColor:(UIColor *)topHandleColor {
_topHandleColor = topHandleColor;
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
bottomDrawerPresentationController.topHandleColor = topHandleColor;
}
}
#pragma mark UIAccessibilityAction
// Adds the Z gesture for dismissal.
- (BOOL)accessibilityPerformEscape {
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
return YES;
}
- (void)bottomDrawerTopTransitionRatio:
(nonnull MDCBottomDrawerPresentationController *)presentationController
transitionRatio:(CGFloat)transitionRatio {
[_maskLayer animateWithPercentage:1 - transitionRatio];
if (self.delegate) {
[self contentDrawerTopInset:transitionRatio];
}
}
- (void)bottomDrawerWillChangeState:
(nonnull MDCBottomDrawerPresentationController *)presentationController
drawerState:(MDCBottomDrawerState)drawerState {
_drawerState = drawerState;
CGFloat minimumCornerRadius = [self minimumCornerRadius];
if (_maskLayer.minimumCornerRadius != minimumCornerRadius) {
_maskLayer.minimumCornerRadius = minimumCornerRadius;
[_maskLayer applyMask];
}
}
- (void)contentDrawerTopInset:(CGFloat)transitionToTop {
CGFloat topInset = MDCDeviceTopSafeAreaInset();
if ([self contentReachesFullScreen]) {
topInset -= ((CGFloat)1.0 - transitionToTop) * topInset;
} else {
topInset = (CGFloat)0.0;
}
if (!self.topHandleHidden) {
topInset = MAX(topInset, (CGFloat)7.0);
}
[self.delegate bottomDrawerControllerDidChangeTopInset:self topInset:topInset];
}
- (void)setContentOffsetY:(CGFloat)contentOffsetY animated:(BOOL)animated {
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
[bottomDrawerPresentationController setContentOffsetY:contentOffsetY animated:animated];
}
}
- (void)expandToFullscreenWithDuration:(CGFloat)duration
completion:(void (^__nullable)(BOOL finished))completion {
if ([self.presentationController isKindOfClass:[MDCBottomDrawerPresentationController class]]) {
MDCBottomDrawerPresentationController *bottomDrawerPresentationController =
(MDCBottomDrawerPresentationController *)self.presentationController;
[bottomDrawerPresentationController expandToFullscreenWithDuration:duration
completion:completion];
}
}
@end