Skip to content
Permalink
Browse files

[NavigationDrawer] Adding a top inset delegate for the drawer (#5674)

**Context:**
Clients need a way to know the content inset of the drawer to be able to lay out their content appropriately.

**The Problem:**
Because things like the status bar, safe area, and the top handle take up more space at the top of the drawer, the content that is laid out initially will be clipped if not able to move the content appropriately as the drawer is dragged.

**The Fix:**
Add an MDCBottomDrawerViewControllerDelegate that listens to topInset changes as the drawer is dragged and provides the top inset in which the content should be laid out below.

**Testing:**
Unit Test + Tested on an iPhone X and iPhone 7 on all examples, portrait and landscape.
  • Loading branch information
yarneo committed Nov 8, 2018
1 parent 3693683 commit c9f2279af98362ad38c7334aa721e700493bde82
@@ -18,7 +18,8 @@ import MaterialComponents.MaterialBottomAppBar_ColorThemer
import MaterialComponents.MaterialColorScheme
import MaterialComponents.MaterialNavigationDrawer

class BottomDrawerWithHeaderExample: UIViewController {
class BottomDrawerWithHeaderExample: UIViewController, MDCBottomDrawerViewControllerDelegate {

var colorScheme = MDCSemanticColorScheme()
let bottomAppBar = MDCBottomAppBarView()

@@ -68,10 +69,18 @@ class BottomDrawerWithHeaderExample: UIViewController {
bottomDrawerViewController.topHandleColor = UIColor.lightGray
bottomDrawerViewController.contentViewController = contentViewController
bottomDrawerViewController.headerViewController = headerViewController
bottomDrawerViewController.delegate = self
MDCBottomDrawerColorThemer.applySemanticColorScheme(colorScheme,
toBottomDrawer: bottomDrawerViewController)
present(bottomDrawerViewController, animated: true, completion: nil)
}

func bottomDrawerControllerDidChangeTopInset(_ controller: MDCBottomDrawerViewController,
topInset: CGFloat) {
headerViewController.titleLabel.center =
CGPoint(x: headerViewController.view.frame.size.width / 2,
y: (headerViewController.view.frame.size.height + topInset) / 2)
}
}

extension BottomDrawerWithHeaderExample {
@@ -67,9 +67,7 @@ class DrawerHeaderViewController: UIViewController,MDCBottomDrawerHeader {
view.addSubview(titleLabel)
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

override func viewWillAppear(_ animated: Bool) {
titleLabel.sizeToFit()
titleLabel.center =
CGPoint(x: self.view.frame.size.width / 2, y: self.view.frame.size.height / 2)
@@ -83,7 +83,6 @@ - (void)presentationTransitionWillBegin {
bottomDrawerViewController.contentViewController;
bottomDrawerContainerViewController.headerViewController =
bottomDrawerViewController.headerViewController;
self.delegate = bottomDrawerViewController;
} else {
bottomDrawerContainerViewController.contentViewController = self.presentedViewController;
}
@@ -17,6 +17,7 @@
#import "MDCBottomDrawerState.h"

@protocol MDCBottomDrawerHeader;
@protocol MDCBottomDrawerViewControllerDelegate;

/**
View controller for containing a Google Material bottom drawer.
@@ -67,6 +68,11 @@
*/
@property(nonatomic, strong, nullable) UIColor *topHandleColor;

/**
The bottom drawer delegate.
*/
@property(nonatomic, weak, nullable) id<MDCBottomDrawerViewControllerDelegate> delegate;

/**
Sets the top corners radius for an MDCBottomDrawerState drawerState
@@ -86,3 +92,22 @@
- (CGFloat)topCornersRadiusForDrawerState:(MDCBottomDrawerState)drawerState;

@end

/**
Delegate for MDCBottomDrawerViewController.
*/
@protocol MDCBottomDrawerViewControllerDelegate <NSObject>

/**
Called when the top inset of the drawer changes due to size changes when moving into full screen
to cover the status bar and safe area inset. Also if there is a top handle, the top inset will
take into regards the handle height. The top inset indicates where the content can be safely
laid out without it being clipped.
@param controller The MDCBottomDrawerViewController.
@param topInset The top inset in which the content should take into regards when being laid out.
*/
- (void)bottomDrawerControllerDidChangeTopInset:(nonnull MDCBottomDrawerViewController *)controller
topInset:(CGFloat)topInset;

@end
@@ -15,6 +15,7 @@
#import "MDCBottomDrawerViewController.h"

#import "MDCBottomDrawerTransitionController.h"
#import "MaterialUIMetrics.h"
#import "private/MDCBottomDrawerHeaderMask.h"

@interface MDCBottomDrawerViewController () <MDCBottomDrawerPresentationControllerDelegate>
@@ -175,6 +176,9 @@ - (void)bottomDrawerTopTransitionRatio:
(nonnull MDCBottomDrawerPresentationController *)presentationController
transitionRatio:(CGFloat)transitionRatio {
[_maskLayer animateWithPercentage:1.f - transitionRatio];
if (self.delegate) {
[self contentDrawerTopInset:transitionRatio];
}
}

- (void)bottomDrawerWillChangeState:
@@ -188,4 +192,17 @@ - (void)bottomDrawerWillChangeState:
}
}

- (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];
}

@end
@@ -15,6 +15,10 @@
#import "MDCNavigationDrawerFakes.h"

@implementation MDCNavigationDrawerFakeHeaderViewController
- (void)bottomDrawerControllerDidChangeTopInset:(nonnull MDCBottomDrawerViewController *)controller
topInset:(CGFloat)topInset {
self.topInset = topInset;
}
@end

static NSString *const reuseIdentifier = @"FakeCell";
@@ -16,7 +16,9 @@

#import "MaterialNavigationDrawer.h"

@interface MDCNavigationDrawerFakeHeaderViewController : UIViewController <MDCBottomDrawerHeader>
@interface MDCNavigationDrawerFakeHeaderViewController
: UIViewController <MDCBottomDrawerHeader, MDCBottomDrawerViewControllerDelegate>
@property(nonatomic) CGFloat topInset;
@end

@interface MDCNavigationDrawerFakeTableViewController : UITableViewController
@@ -526,4 +526,22 @@ - (void)testBottomDrawerScrollingEnabled {
XCTAssertEqual(self.fakeScrollView.scrollEnabled, NO);
}

- (void)testBottomDrawerTopInset {
// Given
MDCNavigationDrawerFakeHeaderViewController *fakeHeader =
[[MDCNavigationDrawerFakeHeaderViewController alloc] init];
self.fakeBottomDrawer.headerViewController = fakeHeader;
self.drawerViewController.delegate = fakeHeader;
self.fakeBottomDrawer.delegate = self.presentationController;
[self.presentationController presentationTransitionWillBegin];

// When
[self.fakeBottomDrawer viewWillAppear:YES];
[self.fakeBottomDrawer cacheLayoutCalculations];
[self.fakeBottomDrawer.scrollView setContentOffset:CGPointMake(0, 1000)];

// Then
XCTAssertEqualWithAccuracy(fakeHeader.topInset, (CGFloat)7.0, (CGFloat)0.001);
}

@end

0 comments on commit c9f2279

Please sign in to comment.
You can’t perform that action at this time.