| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_coordinator.h" |
| #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_coordinator+modal_configuration.h" |
| |
| #import "base/apple/foundation_util.h" |
| #import "base/notreached.h" |
| #import "ios/chrome/browser/ui/infobars/presentation/infobar_modal_positioner.h" |
| #import "ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.h" |
| #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_mediator.h" |
| #import "ios/chrome/browser/ui/overlays/overlay_presentation_util.h" |
| #import "ios/chrome/browser/ui/overlays/overlay_request_coordinator+subclassing.h" |
| #import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h" |
| |
| @interface InfobarModalOverlayCoordinator () <InfobarModalPositioner> |
| // The navigation controller used to display the modal view. |
| @property(nonatomic) UINavigationController* modalNavController; |
| // The transition delegate used by the coordinator to present the modal UI. |
| @property(nonatomic, strong) |
| InfobarModalTransitionDriver* modalTransitionDriver; |
| @end |
| |
| @implementation InfobarModalOverlayCoordinator |
| |
| #pragma mark - OverlayRequestCoordinator |
| |
| - (void)startAnimated:(BOOL)animated { |
| if (self.started || !self.request) |
| return; |
| [self configureModal]; |
| [self configureViewController]; |
| __weak InfobarModalOverlayCoordinator* weakSelf = self; |
| [self.baseViewController presentViewController:self.viewController |
| animated:animated |
| completion:^{ |
| [weakSelf finishPresentation]; |
| }]; |
| self.started = YES; |
| } |
| |
| - (void)stopAnimated:(BOOL)animated { |
| if (!self.started) |
| return; |
| // Mark started as NO before calling dismissal callback to prevent dup |
| // stopAnimated: executions. |
| self.started = NO; |
| __weak InfobarModalOverlayCoordinator* weakSelf = self; |
| [self.baseViewController dismissViewControllerAnimated:animated |
| completion:^{ |
| [weakSelf finishDismissal]; |
| }]; |
| } |
| |
| - (UIViewController*)viewController { |
| return self.modalNavController; |
| } |
| |
| #pragma mark - InfobarModalPositioner |
| |
| - (CGFloat)modalHeightForWidth:(CGFloat)width { |
| UIView* modalView = self.modalViewController.view; |
| CGSize modalContentSize = CGSizeZero; |
| if (UIScrollView* scrollView = |
| base::apple::ObjCCast<UIScrollView>(modalView)) { |
| CGRect layoutFrame = self.baseViewController.view.bounds; |
| layoutFrame.size.width = width; |
| scrollView.frame = layoutFrame; |
| [scrollView setNeedsLayout]; |
| [scrollView layoutIfNeeded]; |
| modalContentSize = scrollView.contentSize; |
| } else { |
| modalContentSize = [modalView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)]; |
| } |
| return modalContentSize.height + |
| CGRectGetHeight(self.modalNavController.navigationBar.bounds); |
| } |
| |
| #pragma mark - InfobarModalPresentationHandler |
| |
| - (void)resizeInfobarModal { |
| UIView* containerView = self.modalNavController.view; |
| containerView.frame = ContainedModalFrameThatFit(self, containerView); |
| } |
| |
| #pragma mark - Private |
| |
| // Called when the presentation of the modal UI is completed. |
| - (void)finishPresentation { |
| // Notify the presentation context that the presentation has finished. This |
| // is necessary to synchronize OverlayPresenter scheduling logic with the UI |
| // layer. |
| if (self.delegate) { |
| self.delegate->OverlayUIDidFinishPresentation(self.request); |
| } |
| } |
| |
| // Called when the dismissal of the modal UI is finished. |
| - (void)finishDismissal { |
| [self resetModal]; |
| self.modalNavController = nil; |
| // Notify the presentation context that the dismissal has finished. This |
| // is necessary to synchronize OverlayPresenter scheduling logic with the UI |
| // layer. |
| if (self.delegate) { |
| self.delegate->OverlayUIDidFinishDismissal(self.request); |
| } |
| } |
| |
| @end |
| |
| @implementation InfobarModalOverlayCoordinator (ModalConfiguration) |
| |
| - (OverlayRequestMediator*)modalMediator { |
| NOTREACHED() << "Subclasses implement."; |
| return nullptr; |
| } |
| |
| - (UIViewController*)modalViewController { |
| NOTREACHED() << "Subclasses implement."; |
| return nil; |
| } |
| |
| - (void)configureModal { |
| NOTREACHED() << "Subclasses implement."; |
| } |
| |
| - (void)configureViewController { |
| self.mediator = self.modalMediator; |
| self.modalTransitionDriver = [[InfobarModalTransitionDriver alloc] |
| initWithTransitionMode:InfobarModalTransitionBase]; |
| self.modalTransitionDriver.modalPositioner = self; |
| self.modalNavController = [[UINavigationController alloc] |
| initWithRootViewController:self.modalViewController]; |
| self.modalNavController.modalPresentationStyle = UIModalPresentationCustom; |
| self.modalNavController.transitioningDelegate = self.modalTransitionDriver; |
| UINavigationBarAppearance* opaqueAppearance = |
| [[UINavigationBarAppearance alloc] init]; |
| [opaqueAppearance configureWithOpaqueBackground]; |
| self.modalNavController.navigationBar.standardAppearance = opaqueAppearance; |
| self.modalNavController.navigationBar.compactAppearance = opaqueAppearance; |
| self.modalNavController.navigationBar.scrollEdgeAppearance = opaqueAppearance; |
| } |
| |
| - (void)resetModal { |
| NOTREACHED() << "Subclasses implement."; |
| } |
| |
| @end |