element-ios/Riot/Modules/MatrixKit/Animators/MXKAttachmentInteractionCon...

201 lines
7.7 KiB
Objective-C

/*
Copyright 2024 New Vector Ltd.
Copyright 2017 Aram Sargsyan
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
#import "MXKAttachmentInteractionController.h"
#import "MXLog.h"
@interface MXKAttachmentInteractionController ()
@property (nonatomic, weak) UIViewController <MXKDestinationAttachmentAnimatorDelegate> *destinationViewController;
@property (nonatomic, weak) UIViewController <MXKSourceAttachmentAnimatorDelegate> *sourceViewController;
@property (nonatomic) UIImageView *transitioningImageView;
@property (nonatomic, weak) id <UIViewControllerContextTransitioning> transitionContext;
@property (nonatomic) CGPoint translation;
@property (nonatomic) CGPoint delta;
@end
@implementation MXKAttachmentInteractionController
#pragma mark - Lifecycle
- (instancetype)initWithDestinationViewController:(UIViewController <MXKDestinationAttachmentAnimatorDelegate> *)viewController sourceViewController:(UIViewController <MXKSourceAttachmentAnimatorDelegate> *)sourceViewController
{
self = [super init];
if (self) {
self.destinationViewController = viewController;
self.sourceViewController = sourceViewController;
self.interactionInProgress = NO;
[self preparePanGestureRecognizerInView:viewController.view];
}
return self;
}
#pragma mark - Gesture recognizer
- (void)preparePanGestureRecognizerInView:(UIView *)view
{
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
recognizer.minimumNumberOfTouches = 1;
recognizer.maximumNumberOfTouches = 3;
[view addGestureRecognizer:recognizer];
}
- (void)handleGesture:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.destinationViewController.view];
self.delta = CGPointMake(translation.x - self.translation.x, translation.y - self.translation.y);
self.translation = translation;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
self.interactionInProgress = YES;
if (self.destinationViewController.navigationController) {
[self.destinationViewController.navigationController popViewControllerAnimated:YES];
} else {
[self.destinationViewController dismissViewControllerAnimated:YES completion:nil];
}
break;
case UIGestureRecognizerStateChanged:
[self updateInteractiveTransition:(ABS(translation.y) / (CGRectGetHeight(self.destinationViewController.view.frame) / 2))];
break;
case UIGestureRecognizerStateCancelled:
self.interactionInProgress = NO;
[self cancelInteractiveTransition];
break;
case UIGestureRecognizerStateEnded:
self.interactionInProgress = NO;
if (ABS(self.translation.y) < CGRectGetHeight(self.destinationViewController.view.frame)/6) {
[self cancelInteractiveTransition];
} else {
[self finishInteractiveTransition];
}
break;
default:
MXLogDebug(@"UIGestureRecognizerState not handled");
break;
}
}
#pragma mark - UIPercentDrivenInteractiveTransition
- (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
self.transitionContext = transitionContext;
[self.destinationViewController prepareSubviewsForTransition:YES];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIImageView *destinationImageView = [self.destinationViewController finalImageView];
destinationImageView.hidden = YES;
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
[[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
UIImageView *originalImageView = [self.sourceViewController originalImageView];
originalImageView.hidden = YES;
self.transitioningImageView = [[UIImageView alloc] initWithImage:destinationImageView.image];
self.transitioningImageView.frame = [MXKAttachmentAnimator aspectFitImage:destinationImageView.image inFrame:destinationImageView.frame];
[[transitionContext containerView] addSubview:self.transitioningImageView];
}
- (void)updateInteractiveTransition:(CGFloat)percentComplete {
self.destinationViewController.view.alpha = MAX(0, (1 - percentComplete));
CGRect newFrame = CGRectMake(self.transitioningImageView.frame.origin.x, self.transitioningImageView.frame.origin.y + self.delta.y, CGRectGetWidth(self.transitioningImageView.frame), CGRectGetHeight(self.transitioningImageView.frame));
self.transitioningImageView.frame = newFrame;
}
- (void)cancelInteractiveTransition {
UIViewController *fromViewController = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIImageView *destinationImageView = [self.destinationViewController finalImageView];
UIImageView *originalImageView = [self.sourceViewController originalImageView];
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:([self transitionDuration:self.transitionContext]/2) animations:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
fromViewController.view.alpha = 1;
self.transitioningImageView.frame = [MXKAttachmentAnimator aspectFitImage:destinationImageView.image inFrame:destinationImageView.frame];
}
} completion:^(BOOL finished) {
if (weakSelf)
{
typeof(self) self = weakSelf;
destinationImageView.hidden = NO;
originalImageView.hidden = NO;
[self.transitioningImageView removeFromSuperview];
[self.transitionContext cancelInteractiveTransition];
[self.transitionContext completeTransition:NO];
[self.destinationViewController prepareSubviewsForTransition:NO];
}
}];
}
- (void)finishInteractiveTransition
{
UIViewController *fromViewController = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIImageView *destinationImageView = [self.destinationViewController finalImageView];
UIImageView *originalImageView = [self.sourceViewController originalImageView];
CGRect originalImageViewFrame = [self.sourceViewController convertedFrameForOriginalImageView];
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:[self transitionDuration:self.transitionContext] animations:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
fromViewController.view.alpha = 0.0;
self.transitioningImageView.frame = originalImageViewFrame;
}
} completion:^(BOOL finished) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self.transitioningImageView removeFromSuperview];
destinationImageView.hidden = NO;
originalImageView.hidden = NO;
[self.transitionContext finishInteractiveTransition];
[self.transitionContext completeTransition:YES];
}
}];
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.3;
}
@end