自定义Modal转场动画
想要制作如下效果:
点击小图,从小图的位置生成一张一样大小和位置的图片,然后放大到全屏,在点击全屏图片,图片缩小回到小图的位置
首先定义两个Delegate,分别管理新控制器弹出(Present)和收起(Dissmis)的一些参数
PicBrowserPresentedAnimationDelegate.h
#import <Foundation/Foundation.h>
@protocol PicBrowserPresentedAnimationDelegate <NSObject>
- (CGRect) startRect;
- (CGRect) endRect;
- (UIImageView*) imageView;
@end
PicBrowserDismissedAnimationDelegate.h
#import <Foundation/Foundation.h>
@protocol PicBrowserDismissedAnimationDelegate <NSObject>
- (UIImageView*)imageView;
@end
然后创建一个转场动画的动画管理类,并且实现协议: UIViewControllerTransitioningDelegate和UIViewControllerAnimatedTransitioning
PicBrowserAnimation.h
#import <Foundation/Foundation.h>
#import "PicBrowserDismissedAnimationDelegate.h"
#import "PicBrowserPresentedAnimationDelegate.h"
@interface PicBrowserAnimation : NSObject <UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning>
@property (assign, nonatomic)BOOL isPresented;
@property (weak, nonatomic)id<PicBrowserDismissedAnimationDelegate> dismissDelegate;
@property (weak, nonatomic)id<PicBrowserPresentedAnimationDelegate> presentDelegate;
@end
PicBrowserAnimation.m
#import "PicBrowserAnimation.h"
#import "PicBrowserDismissedAnimationDelegate.h"
#import "PicBrowserPresentedAnimationDelegate.h"
@interface PicBrowserAnimation()
@end
@implementation PicBrowserAnimation
#pragma mark UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
self.isPresented = YES;
return self;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
self.isPresented = NO;
return self;
}
#pragma mark UIViewControllerAnimatedTransitioning
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
if(self.isPresented) {
[self animateForPresentedView:transitionContext];
} else {
[self animateForDismissedView:transitionContext];
}
}
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
return 0.3;
}
- (void)animateForPresentedView:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
if(self.presentDelegate) {
//取出要弹出的View
UIView *presentedView = [transitionContext viewForKey:UITransitionContextToViewKey];
//将presentedView添加到containerView中
[transitionContext.containerView addSubview:presentedView];
CGRect startRect = [self.presentDelegate startRect];
UIImageView *imageView = [self.presentDelegate imageView];
[transitionContext.containerView addSubview:imageView];
imageView.frame = startRect;
//执行动画
[presentedView setAlpha:0.0];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
imageView.frame = [self.presentDelegate endRect];
[transitionContext.containerView setBackgroundColor:[UIColor blackColor]];
} completion:^(BOOL finished) {
//通知上下文已经完成动画
[presentedView setAlpha:1.0];
[transitionContext completeTransition:YES];
[imageView removeFromSuperview];
[transitionContext.containerView setBackgroundColor:[UIColor clearColor]];
}];
}
}
- (void)animateForDismissedView:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
if(self.dismissDelegate) {
UIView *dissmisView = [transitionContext viewForKey:UITransitionContextFromViewKey];
[dissmisView removeFromSuperview];
UIImageView *imageView = [self.dismissDelegate imageView];
[transitionContext.containerView addSubview:imageView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
imageView.frame = [self.presentDelegate startRect];
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
}
@end
在需要转场的控制器中的转场方法,找到需要转场的实际View作为Delegate实现动画方法
@property (strong, nonatomic)PicBrowserAnimation *picBrowserAnimation;
- (PicBrowserAnimation *)picBrowserAnimation {
if(!_picBrowserAnimation) {
_picBrowserAnimation = [[PicBrowserAnimation alloc]init];
}
return _picBrowserAnimation;
}
- (void)imageSelected:(NSNotification*) notification {
AVIMImageMessage *message = (AVIMImageMessage*)notification.userInfo[@"message"];
ImageBrowserController *imageBrowserController = [ImageBrowserController imageBrowserController:message];
ChatImageView *imageView = (ChatImageView*)notification.object;
imageBrowserController.modalPresentationStyle = UIModalPresentationCustom;
imageBrowserController.transitioningDelegate = self.picBrowserAnimation;
self.picBrowserAnimation.presentDelegate = imageView;
self.picBrowserAnimation.dismissDelegate = imageBrowserController;
[self presentViewController:imageBrowserController animated:YES completion:nil];
}
Present:
ChatImageView.m,动画开始的小图,以及起始Frame和结束Frame
@implementation ChatImageView
- (CGRect)endRect {
CGSize imageSize = CGSizeMake([[self.message.file.metaData objectForKey:@"width"] floatValue],[[self.message.file.metaData objectForKey:@"height"] floatValue]);
CGFloat imageW = [UIScreen mainScreen].bounds.size.width;
CGFloat imageH = imageW / imageSize.width * imageSize.height;
return CGRectMake(0, ([UIScreen mainScreen].bounds.size.height - imageH) * 0.5, imageW, imageH);
}
- (UIImageView *)imageView {
UIImage *image = [[SDWebImageManager sharedManager].imageCache imageFromMemoryCacheForKey:self.message.file.url];
UIImageView *imageView = [[UIImageView alloc]init];
imageView.image = image;
return imageView;
}
- (CGRect)startRect {
return [self convertRect:self.frame toView:[UIApplication sharedApplication].keyWindow];
}
@end
Dismiss
#pragma mark PicBrowserDismissedAnimationDelegate
- (UIImageView *)imageView {
UIImageView *imageView = [[UIImageView alloc]init];
[imageView sd_setImageWithURL:[NSURL URLWithString:self.message.file.url]];
CGSize imageSize = CGSizeMake([[self.message.file.metaData objectForKey:@"width"] floatValue],[[self.message.file.metaData objectForKey:@"height"] floatValue]);
CGFloat imageW = [UIScreen mainScreen].bounds.size.width;
CGFloat imageH = imageW / imageSize.width * imageSize.height;
imageView.frame = CGRectMake(0, ([UIScreen mainScreen].bounds.size.height - imageH) * 0.5, imageW, imageH);
return imageView;
}