diff --git a/components/BottomSheet/src/MDCBottomSheetPresentationController.h b/components/BottomSheet/src/MDCBottomSheetPresentationController.h index 30fc2d239fa..894164abc06 100644 --- a/components/BottomSheet/src/MDCBottomSheetPresentationController.h +++ b/components/BottomSheet/src/MDCBottomSheetPresentationController.h @@ -64,6 +64,18 @@ */ @property(nonatomic, assign) BOOL dismissOnBackgroundTap; +/** + This is used to set a custom height on the sheet view. + + @note If a positive value is passed then the sheet view will be that height even if + perferredContentSize has been set. Otherwise the sheet will open up to half the screen height or + the size of the presentedViewController's preferredContentSize whatever value is smaller. + @note The preferredSheetHeight can never be taller than the height of the content, if the content + is smaller than the value passed to preferredSheetHeight then the sheet view will be the size of + the content height. + */ +@property(nonatomic, assign) CGFloat preferredSheetHeight; + /** If @c YES, then the dimmed scrim view will act as an accessibility element for dismissing the bottom sheet. diff --git a/components/BottomSheet/src/MDCBottomSheetPresentationController.m b/components/BottomSheet/src/MDCBottomSheetPresentationController.m index b18fd3e9aed..5c1e58aad44 100644 --- a/components/BottomSheet/src/MDCBottomSheetPresentationController.m +++ b/components/BottomSheet/src/MDCBottomSheetPresentationController.m @@ -108,7 +108,7 @@ - (void)presentationTransitionWillBegin { [containerView addSubview:_dimmingView]; [containerView addSubview:self.sheetView]; - [self setPreferredSheetHeight:self.presentedViewController.preferredContentSize.height]; + [self updatePreferredSheetHeight]; // Add tap handler to dismiss the sheet. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self @@ -155,7 +155,7 @@ - (void)preferredContentSizeDidChangeForChildContentContainer:(id _Nonnull context) { self.sheetView.frame = [self frameOfPresentedViewInContainerView]; [self.sheetView layoutIfNeeded]; - [self setPreferredSheetHeight:self.presentedViewController.preferredContentSize.height]; + [self updatePreferredSheetHeight]; } completion:nil]; } @@ -176,11 +176,16 @@ - (void)viewWillTransitionToSize:(CGSize)size Sets the new value of @c sheetView.preferredSheetHeight. If @c preferredContentHeight is non-positive, it will set it to half of sheetView's frame's height. - - @param preferredSheetHeight If positive, the new value for @sheetView.preferredSheetHeight. */ -- (void)setPreferredSheetHeight:(CGFloat)preferredSheetHeight { +- (void)updatePreferredSheetHeight { // If |preferredSheetHeight| has not been specified, use half of the current height. + CGFloat preferredSheetHeight; + if (self.preferredSheetHeight > 0.f) { + preferredSheetHeight = self.preferredSheetHeight; + } else { + preferredSheetHeight = self.presentedViewController.preferredContentSize.height; + } + if (MDCCGFloatEqual(preferredSheetHeight, 0)) { preferredSheetHeight = MDCRound(CGRectGetHeight(self.sheetView.frame) / 2); } @@ -244,6 +249,11 @@ - (UIAccessibilityTraits)scrimAccessibilityTraits { return _scrimAccessibilityTraits; } +- (void)setPreferredSheetHeight:(CGFloat)preferredSheetHeight { + _preferredSheetHeight = preferredSheetHeight; + [self updatePreferredSheetHeight]; +} + #pragma mark - MDCSheetContainerViewDelegate - (void)sheetContainerViewDidHide:(nonnull __unused MDCSheetContainerView *)containerView { diff --git a/components/BottomSheet/src/MDCBottomSheetTransitionController.h b/components/BottomSheet/src/MDCBottomSheetTransitionController.h index 08ea0fb8c59..e9c8d929286 100644 --- a/components/BottomSheet/src/MDCBottomSheetTransitionController.h +++ b/components/BottomSheet/src/MDCBottomSheetTransitionController.h @@ -45,6 +45,19 @@ */ @property(nonatomic, assign) BOOL dismissOnBackgroundTap; +/** + This is used to set a custom height on the sheet view. This is can be used to set the initial + height when the ViewController is presented. + + @note If a positive value is passed then the sheet view will be that height even if + perferredContentSize has been set. Otherwise the sheet will open up to half the screen height or + the size of the presentedViewController's preferredContentSize whatever value is smaller. + @note The preferredSheetHeight can never be taller than the height of the content, if the content + is smaller than the value passed to preferredSheetHeight then the sheet view will be the size of + the content height. + */ +@property(nonatomic, assign) CGFloat preferredSheetHeight; + @end @interface MDCBottomSheetTransitionController (ScrimAccessibility) diff --git a/components/BottomSheet/src/MDCBottomSheetTransitionController.m b/components/BottomSheet/src/MDCBottomSheetTransitionController.m index b96b096237d..9ca5ab9c0c6 100644 --- a/components/BottomSheet/src/MDCBottomSheetTransitionController.m +++ b/components/BottomSheet/src/MDCBottomSheetTransitionController.m @@ -48,6 +48,7 @@ - (instancetype)init { presentationController.isScrimAccessibilityElement = _isScrimAccessibilityElement; presentationController.scrimAccessibilityHint = _scrimAccessibilityHint; presentationController.scrimAccessibilityLabel = _scrimAccessibilityLabel; + presentationController.preferredSheetHeight = _preferredSheetHeight; return presentationController; } diff --git a/components/BottomSheet/tests/unit/MDCBottomSheetPresentationControllerPreferredSheetHeightTests.m b/components/BottomSheet/tests/unit/MDCBottomSheetPresentationControllerPreferredSheetHeightTests.m index f5e20166ffa..1f4511f0e9b 100644 --- a/components/BottomSheet/tests/unit/MDCBottomSheetPresentationControllerPreferredSheetHeightTests.m +++ b/components/BottomSheet/tests/unit/MDCBottomSheetPresentationControllerPreferredSheetHeightTests.m @@ -22,7 +22,7 @@ // Exposing internal methods for unit testing @interface MDCBottomSheetPresentationController (Testing) @property(nonatomic, strong) MDCSheetContainerView *sheetView; -- (void)setPreferredSheetHeight:(CGFloat)preferredSheetHeight; +- (void)updatePreferredSheetHeight; @end /** @@ -79,53 +79,150 @@ - (void)setUp { self.presentationController.sheetView = self.sheetView; } -- (void)testSetPreferredSheetHeightZeroWhenSheetViewHasStandardizedFrame { +- (void)testUpdatePreferredSheetHeightZeroWhenSheetViewHasStandardizedFrame { // Given CGFloat sheetFrameHeight = 80; self.sheetView.frame = CGRectMake(0, 0, 75, sheetFrameHeight); // When - [self.presentationController setPreferredSheetHeight:0]; + [self.presentationController updatePreferredSheetHeight]; // Then XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, sheetFrameHeight / 2, 0.001); } -- (void)testSetPreferredSheetHeightZeroWhenSheetViewHasUnstandardizedFrame { +- (void)testUpdatePreferredSheetHeightZeroWhenSheetViewHasUnstandardizedFrame { // Given CGFloat sheetFrameHeight = -80; self.sheetView.frame = CGRectMake(75, 80, -75, sheetFrameHeight); // When - [self.presentationController setPreferredSheetHeight:0]; + [self.presentationController updatePreferredSheetHeight]; // Then XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, (CGFloat)fabs(sheetFrameHeight / 2), 0.001); } -- (void)testSetPreferredSheetHeightPositiveValue { +- (void)testUpdatePreferredSheetHeightPositiveValue { // Given CGFloat preferredSheetHeight = 120; + self.presentationController.presentedViewController.preferredContentSize = + CGSizeMake(100, preferredSheetHeight); self.sheetView.frame = CGRectMake(0, 0, 75, 80); // When - [self.presentationController setPreferredSheetHeight:preferredSheetHeight]; + [self.presentationController updatePreferredSheetHeight]; // Then XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001); } -- (void)testSetPreferredSheetHeightNegativeValue { +- (void)testUpdatePreferredSheetHeightNegativeValue { // Given CGFloat preferredSheetHeight = -120; + self.presentationController.presentedViewController.preferredContentSize = + CGSizeMake(0, preferredSheetHeight); self.sheetView.frame = CGRectMake(0, 0, 75, -80); // When - [self.presentationController setPreferredSheetHeight:preferredSheetHeight]; + [self.presentationController updatePreferredSheetHeight]; // Then XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001); } +- (void)testUpdatePreferredSheetHeight { + // Given + CGFloat preferredSheetHeight = 100; + self.presentationController.preferredSheetHeight = preferredSheetHeight; + + // When + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001); +} + +- (void)testUpdateNegativePreferredSheetHeight { + // Given + CGFloat preferrredSheetHeight = -100; + self.presentationController.preferredSheetHeight = preferrredSheetHeight; + CGFloat sheetHeight = 250; + self.sheetView.frame = CGRectMake(0, 0, 75, sheetHeight); + + // When + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, (CGFloat)(sheetHeight / 2), + 0.001); +} + +- (void)testUpdatePreferredSheetHeightAndPreferredContentSize { + // Given + CGSize preferredContentSize = CGSizeMake(100, 100); + CGFloat preferredSheetHeight = 200; + self.presentationController.presentedViewController.preferredContentSize = preferredContentSize; + self.presentationController.preferredSheetHeight = preferredSheetHeight; + + // When + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredSheetHeight, 0.001); +} + +- (void)testPreferredSheetHeightNegativeAndPreferredContentSizePositive { + // Given + CGSize preferredContentSize = CGSizeMake(100, 100); + CGFloat preferredSheetHeight = -200; + self.presentationController.presentedViewController.preferredContentSize = preferredContentSize; + self.presentationController.preferredSheetHeight = preferredSheetHeight; + + // When + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, preferredContentSize.height, + 0.001); +} + +- (void)testPreferredSheetHeightNonZeroThenZero { + // Given + CGFloat preferredSheetHeight = 200; + self.presentationController.preferredSheetHeight = preferredSheetHeight; + CGFloat sheetHeight = 300; + self.sheetView.frame = CGRectMake(0, 0, 200, sheetHeight); + + // When + [self.presentationController updatePreferredSheetHeight]; + self.presentationController.preferredSheetHeight = 0; + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(self.sheetView.preferredSheetHeight, (CGFloat)(sheetHeight / 2), + 0.001); +} + +- (void)testVeryLargePreferredSheetHeightAndSmallContent { + // Given + CGFloat scrollViewHeight = 100; + CGRect smallFrame = CGRectMake(0, 0, 200, scrollViewHeight); + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:smallFrame]; + FakeSheetView *sheetView = + [[FakeSheetView alloc] initWithFrame:smallFrame + contentView:[[UIView alloc] initWithFrame:smallFrame] + scrollView:scrollView]; + + self.presentationController.sheetView = sheetView; + self.presentationController.preferredSheetHeight = 5000; + + // When + [self.presentationController updatePreferredSheetHeight]; + + // Then + XCTAssertEqualWithAccuracy(CGRectGetHeight(sheetView.frame), CGRectGetHeight(smallFrame), 0.001); +} + @end