123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- //
- // SignatureCustomPresentationController.m
- // ComPDFKit_Tools
- //
- // Copyright © 2014-2024 PDF Technologies, Inc. All Rights Reserved.
- //
- // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
- // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
- // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
- // This notice may not be removed from this file.
- //
- #import "SignatureCustomPresentationController.h"
- #define CORNER_RADIUS 16.f
- @interface SignatureCustomPresentationController () <UIViewControllerAnimatedTransitioning>
- @property (nonatomic, strong) UIView *dimmingView;
- @property (nonatomic, strong) UIView *presentationWrappingView;
- @end
- @implementation SignatureCustomPresentationController
- - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController
- {
- self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
-
- if (self) {
- presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
- }
-
- return self;
- }
- - (UIView*)presentedView
- {
- // Return the wrapping view created in -presentationTransitionWillBegin.
- return self.presentationWrappingView;
- }
- - (void)presentationTransitionWillBegin
- {
- // The default implementation of -presentedView returns
- // self.presentedViewController.view.
- UIView *presentedViewControllerView = [super presentedView];
-
- // Wrap the presented view controller's view in an intermediate hierarchy
- // that applies a shadow and rounded corners to the top-left and top-right
- // edges. The final effect is built using three intermediate views.
- //
- // presentationWrapperView <- shadow
- // |- presentationRoundedCornerView <- rounded corners (masksToBounds)
- // |- presentedViewControllerWrapperView
- // |- presentedViewControllerView (presentedViewController.view)
- //
- // SEE ALSO: The note in AAPLCustomPresentationSecondViewController.m.
- {
- UIView *presentationWrapperView = [[UIView alloc] initWithFrame:self.frameOfPresentedViewInContainerView];
- presentationWrapperView.layer.shadowOpacity = 0.44f;
- presentationWrapperView.layer.shadowRadius = 13.f;
- presentationWrapperView.layer.shadowOffset = CGSizeMake(0, -6.f);
- self.presentationWrappingView = presentationWrapperView;
-
- // presentationRoundedCornerView is CORNER_RADIUS points taller than the
- // height of the presented view controller's view. This is because
- // the cornerRadius is applied to all corners of the view. Since the
- // effect calls for only the top two corners to be rounded we size
- // the view such that the bottom CORNER_RADIUS points lie below
- // the bottom edge of the screen.
- UIView *presentationRoundedCornerView = [[UIView alloc] initWithFrame:UIEdgeInsetsInsetRect(presentationWrapperView.bounds, UIEdgeInsetsMake(0, 0, -CORNER_RADIUS, 0))];
- presentationRoundedCornerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- presentationRoundedCornerView.layer.cornerRadius = CORNER_RADIUS;
- presentationRoundedCornerView.layer.masksToBounds = YES;
-
- // To undo the extra height added to presentationRoundedCornerView,
- // presentedViewControllerWrapperView is inset by CORNER_RADIUS points.
- // This also matches the size of presentedViewControllerWrapperView's
- // bounds to the size of -frameOfPresentedViewInContainerView.
- UIView *presentedViewControllerWrapperView = [[UIView alloc] initWithFrame:UIEdgeInsetsInsetRect(presentationRoundedCornerView.bounds, UIEdgeInsetsMake(0, 0, CORNER_RADIUS, 0))];
- presentedViewControllerWrapperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-
- // Add presentedViewControllerView -> presentedViewControllerWrapperView.
- presentedViewControllerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- presentedViewControllerView.frame = presentedViewControllerWrapperView.bounds;
- [presentedViewControllerWrapperView addSubview:presentedViewControllerView];
-
- // Add presentedViewControllerWrapperView -> presentationRoundedCornerView.
- [presentationRoundedCornerView addSubview:presentedViewControllerWrapperView];
-
- // Add presentationRoundedCornerView -> presentationWrapperView.
- [presentationWrapperView addSubview:presentationRoundedCornerView];
- }
-
- // Add a dimming view behind presentationWrapperView. self.presentedView
- // is added later (by the animator) so any views added here will be
- // appear behind the -presentedView.
- {
- UIView *dimmingView = [[UIView alloc] initWithFrame:self.containerView.bounds];
- dimmingView.backgroundColor = [UIColor blackColor];
- dimmingView.opaque = NO;
- dimmingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [dimmingView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimmingViewTapped:)]];
- self.dimmingView = dimmingView;
- [self.containerView addSubview:dimmingView];
-
- // Get the transition coordinator for the presentation so we can
- // fade in the dimmingView alongside the presentation animation.
- id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
-
- self.dimmingView.alpha = 0.f;
- [transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
- self.dimmingView.alpha = 0.5f;
- } completion:NULL];
- }
- }
- - (void)presentationTransitionDidEnd:(BOOL)completed
- {
- // The value of the 'completed' argument is the same value passed to the
- // -completeTransition: method by the animator. It may
- // be NO in the case of a cancelled interactive transition.
- if (completed == NO)
- {
- // The system removes the presented view controller's view from its
- // superview and disposes of the containerView. This implicitly
- // removes the views created in -presentationTransitionWillBegin: from
- // the view hierarchy. However, we still need to relinquish our strong
- // references to those view.
- self.presentationWrappingView = nil;
- self.dimmingView = nil;
- }
- }
- - (void)dismissalTransitionWillBegin
- {
- // Get the transition coordinator for the dismissal so we can
- // fade out the dimmingView alongside the dismissal animation.
- id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
-
- [transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
- self.dimmingView.alpha = 0.f;
- } completion:NULL];
- }
- - (void)dismissalTransitionDidEnd:(BOOL)completed
- {
- // The value of the 'completed' argument is the same value passed to the
- // -completeTransition: method by the animator. It may
- // be NO in the case of a cancelled interactive transition.
- if (completed == YES)
- {
- // The system removes the presented view controller's view from its
- // superview and disposes of the containerView. This implicitly
- // removes the views created in -presentationTransitionWillBegin: from
- // the view hierarchy. However, we still need to relinquish our strong
- // references to those view.
- self.presentationWrappingView = nil;
- self.dimmingView = nil;
- }
- }
- #pragma mark -
- #pragma mark Layout
- // This method is invoked whenever the presentedViewController's
- // preferredContentSize property changes. It is also invoked just before the
- // presentation transition begins (prior to -presentationTransitionWillBegin).
- //
- - (void)preferredContentSizeDidChangeForChildContentContainer:(id<UIContentContainer>)container
- {
- [super preferredContentSizeDidChangeForChildContentContainer:container];
-
- if (container == self.presentedViewController)
- [self.containerView setNeedsLayout];
- }
- // When the presentation controller receives a
- // -viewWillTransitionToSize:withTransitionCoordinator: message it calls this
- // method to retrieve the new size for the presentedViewController's view.
- // The presentation controller then sends a
- // -viewWillTransitionToSize:withTransitionCoordinator: message to the
- // presentedViewController with this size as the first argument.
- //
- // Note that it is up to the presentation controller to adjust the frame
- // of the presented view controller's view to match this promised size.
- // We do this in -containerViewWillLayoutSubviews.
- //
- - (CGSize)sizeForChildContentContainer:(id<UIContentContainer>)container withParentContainerSize:(CGSize)parentSize
- {
- if (container == self.presentedViewController)
- return ((UIViewController*)container).preferredContentSize;
- else
- return [super sizeForChildContentContainer:container withParentContainerSize:parentSize];
- }
- - (CGRect)frameOfPresentedViewInContainerView
- {
- CGRect containerViewBounds = self.containerView.bounds;
- CGSize presentedViewContentSize = [self sizeForChildContentContainer:self.presentedViewController withParentContainerSize:containerViewBounds.size];
-
- // The presented view extends presentedViewContentSize.height points from
- // the bottom edge of the screen.
- CGRect presentedViewControllerFrame = containerViewBounds;
- presentedViewControllerFrame.size.height = presentedViewContentSize.height;
- presentedViewControllerFrame.origin.y = CGRectGetMaxY(containerViewBounds) - presentedViewContentSize.height;
- return presentedViewControllerFrame;
- }
- // This method is similar to the -viewWillLayoutSubviews method in
- // UIViewController. It allows the presentation controller to alter the
- // layout of any custom views it manages.
- //
- - (void)containerViewWillLayoutSubviews
- {
- [super containerViewWillLayoutSubviews];
-
- self.dimmingView.frame = self.containerView.bounds;
- // self.frameOfPresentedViewInContainerView
- CGFloat width = self.containerView.frame.size.width;
- self.presentationWrappingView.frame = CGRectMake((self.frameOfPresentedViewInContainerView.size.width - width)/2, self.frameOfPresentedViewInContainerView.origin.y,width, self.frameOfPresentedViewInContainerView.size.height);
- }
- #pragma mark -
- #pragma mark Tap Gesture Recognizer
- // IBAction for the tap gesture recognizer added to the dimmingView.
- // Dismisses the presented view controller.
- //
- - (IBAction)dimmingViewTapped:(UITapGestureRecognizer*)sender
- {
- [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
- }
- #pragma mark -
- #pragma mark UIViewControllerAnimatedTransitioning
- - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
- {
- return [transitionContext isAnimated] ? 0.35 : 0;
- }
- // The presentation animation is tightly integrated with the overall
- // presentation so it makes the most sense to implement
- // <UIViewControllerAnimatedTransitioning> in the presentation controller
- // rather than in a separate object.
- //
- - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
- {
- UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
- UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
-
- UIView *containerView = transitionContext.containerView;
-
- // For a Presentation:
- // fromView = The presenting view.
- // toView = The presented view.
- // For a Dismissal:
- // fromView = The presented view.
- // toView = The presenting view.
- UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
- // If NO is returned from -shouldRemovePresentersView, the view associated
- // with UITransitionContextFromViewKey is nil during presentation. This
- // intended to be a hint that your animator should NOT be manipulating the
- // presenting view controller's view. For a dismissal, the -presentedView
- // is returned.
- //
- // Why not allow the animator manipulate the presenting view controller's
- // view at all times? First of all, if the presenting view controller's
- // view is going to stay visible after the animation finishes during the
- // whole presentation life cycle there is no need to animate it at all — it
- // just stays where it is. Second, if the ownership for that view
- // controller is transferred to the presentation controller, the
- // presentation controller will most likely not know how to layout that
- // view controller's view when needed, for example when the orientation
- // changes, but the original owner of the presenting view controller does.
- UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
-
- BOOL isPresenting = (fromViewController == self.presentingViewController);
-
- // This will be the current frame of fromViewController.view.
- CGRect __unused fromViewInitialFrame = [transitionContext initialFrameForViewController:fromViewController];
- // For a presentation which removes the presenter's view, this will be
- // CGRectZero. Otherwise, the current frame of fromViewController.view.
- CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromViewController];
- // This will be CGRectZero.
- CGRect toViewInitialFrame = [transitionContext initialFrameForViewController:toViewController];
- // For a presentation, this will be the value returned from the
- // presentation controller's -frameOfPresentedViewInContainerView method.
- CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toViewController];
-
- // We are responsible for adding the incoming view to the containerView
- // for the presentation (will have no effect on dismissal because the
- // presenting view controller's view was not removed).
- [containerView addSubview:toView];
-
- if (isPresenting) {
- toViewInitialFrame.origin = CGPointMake(CGRectGetMinX(containerView.bounds), CGRectGetMaxY(containerView.bounds));
- toViewInitialFrame.size = toViewFinalFrame.size;
- toView.frame = toViewInitialFrame;
- } else {
- // Because our presentation wraps the presented view controller's view
- // in an intermediate view hierarchy, it is more accurate to rely
- // on the current frame of fromView than fromViewInitialFrame as the
- // initial frame (though in this example they will be the same).
- fromViewFinalFrame = CGRectOffset(fromView.frame, 0, CGRectGetHeight(fromView.frame));
- }
-
- NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
-
- [UIView animateWithDuration:transitionDuration animations:^{
- if (isPresenting)
- toView.frame = toViewFinalFrame;
- else
- fromView.frame = fromViewFinalFrame;
-
- } completion:^(BOOL finished) {
- // When we complete, tell the transition context
- // passing along the BOOL that indicates whether the transition
- // finished or not.
- BOOL wasCancelled = [transitionContext transitionWasCancelled];
- [transitionContext completeTransition:!wasCancelled];
- }];
- }
- #pragma mark -
- #pragma mark UIViewControllerTransitioningDelegate
- // If the modalPresentationStyle of the presented view controller is
- // UIModalPresentationCustom, the system calls this method on the presented
- // view controller's transitioningDelegate to retrieve the presentation
- // controller that will manage the presentation. If your implementation
- // returns nil, an instance of UIPresentationController is used.
- //
- - (UIPresentationController*)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
- {
- NSAssert(self.presentedViewController == presented, @"You didn't initialize %@ with the correct presentedViewController. Expected %@, got %@.",
- self, presented, self.presentedViewController);
-
- return self;
- }
- // The system calls this method on the presented view controller's
- // transitioningDelegate to retrieve the animator object used for animating
- // the presentation of the incoming view controller. Your implementation is
- // expected to return an object that conforms to the
- // UIViewControllerAnimatedTransitioning protocol, or nil if the default
- // presentation animation should be used.
- //
- - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
- {
- return self;
- }
- // The system calls this method on the presented view controller's
- // transitioningDelegate to retrieve the animator object used for animating
- // the dismissal of the presented view controller. Your implementation is
- // expected to return an object that conforms to the
- // UIViewControllerAnimatedTransitioning protocol, or nil if the default
- // dismissal animation should be used.
- //
- - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
- {
- return self;
- }
- @end
|