您的位置:首页 > 移动开发 > IOS开发

iOS开源项目推荐|侧滑与右滑返回手势;几句代码快速集成自定义转场效果+ 全手势驱动

2016-07-29 18:00 1266 查看


1、CircleSpreadTransition 小圆点扩散



2、MagicMoveTransition 神奇移动





3、XWDrawerAnimator 抽屉效果,仿照QQ和淘宝





4、XWCoolAnimator 自定义一些效果















5、XWFilterAnimator 通过CIFilter滤镜自定义一些效果,请在真机上运行

















如何使用

1、git地址:几句代码快速集成自定义转场效果+ 全手势驱动,clone后将整个XWTranstion文件夹导入工程

2、导入UINavigationController+XWTransition.h或者UIViewController+XWTransition.h两个分类

3、选择你需要的效果器进行根据初始化方法进行初始化,比如下面的小圆点扩散,初始化指定开始圆心和半径

XWCircleSpreadAnimator *animator = [XWCircleSpreadAnimator xw_animatorWithStartCenter:self.button.center radius:20];

4、通过初始化的效果器转场,根据分类提供的方法进行push或者present,就完成了!

[self.navigationController xw_pushViewController:toVC withAnimator:animator];

或者

[self xw_presentViewController:toVC withAnimator:animator];

手势驱动

1、在UIViewController+XWTransition.h分类中提供了两个方法,用来注册手势驱动,在viewDidLoad的时候调用注册手势就可以了,详见demo,注意避免循环引用,手势支持边缘属性

/**

*  注册to手势(push或者Present手势)

*

*  @param direction       手势方向

*  @param tansitionConfig 手势触发的block,block中需要包含你的push或者Present的逻辑代码,注意避免循环引用问题

*  @param edgeSpacing     手势触发的边缘距离,该值为0,表示在整个控制器视图上都有效,否者这在边缘的edgeSpacing之类有效

*/

- (void)xw_registerToInteractiveTransitionWithDirection:(XWInteractiveTransitionGestureDirection)direction transitonBlock:(dispatch_block_t)tansitionConfig
edgeSpacing:(CGFloat)edgeSpacing;

/**

*  注册back手势(pop或者dismiss手势)

*

*  @param direction       手势方向

*  @param tansitionConfig 手势触发的block,block中需要包含你的pop或者dismiss的逻辑代码,注意避免循环引用问题

*  @param edgeSpacing     手势触发的边缘距离,该值为0,表示在整个控制器视图上都有效,否者这在边缘的edgeSpacing之类有效

*/

- (void)xw_registerBackInteractiveTransitionWithDirection:(XWInteractiveTransitionGestureDirection)direction transitonBlock:(dispatch_block_t)tansitionConfig
edgeSpacing:(CGFloat)edgeSpacing;

2、事例代码

__weak typeof(self)weakSelf = self;

//注册一个全屏的back转场

[self xw_registerBackInteractiveTransitionWithDirection:XWInteractiveTransitionGestureDirectionDown transitonBlock:^{

//pop或者dismiss操作

[weakSelf xw_transiton];

} edgeSpacing:0];

关于神奇移动效果

1、在UIViewController+XWTransition.h分类中提供了三个关于神奇移动的方法,你需要在转场前和转场后的控制器中分别注册神奇移动前后的视图(用来告知神奇移动前后的frame),然后通过神奇移动效果器就可以触发神奇移动转场了

/**

* 注册神奇移动起始视图

*

* @param group 神奇移动起始视图数组

*/

- (void)xw_addMagicMoveStartViewGroup:(NSArray<UIView *> *)group;

/**

* 注册神奇移动终止视图

*

* @param group 神奇移动终止视图数组,注意起始视图数组和终止视图数组的视图需要一一对应才能有正确的效果

*/

- (void)xw_addMagicMoveEndViewGroup:(NSArray<UIView *> *)group;

/**

* 改变神奇移动起始视图,因为在back的时候,有可能不需要再回到原来起始的位置,需要去一个新的视图位置,所以在back前需要调用该方法改变起始视图数组

*

* @param group 新的起始视图数组

*/

- (void)xw_changeMagicMoveStartViewGroup:(NSArray<UIView *> *)group;

2、事例代码

//fromVC转场前控制器中注册神奇移动前视图

[self xw_addMagicMoveStartViewGroup:@[imgView, view1, view2]];

//toVC转场后控制器中注册神奇移动前视图

[self xw_addMagicMoveEndViewGroup:@[imgView, view1, view2]];

//初始化神奇移动效果器转场

XWMagicMoveToController *toVC = [XWMagicMoveToController new];

[self xw_presentViewController:toVC withAnimator:animator];

3、转场中存在cell,由于在转场过程中cell还没有加载,所以无法注册cell为神奇移动视图,这种情况需要生产一个零时视图注册为转场视图来使用,具体请参考demo中的九宫格例子

4、关于提供的imageMode属性:在神奇移动中,有个问题,就是移动中的临时视图一般都是用截图大法截图而来的,但是如果从从小图变成大图,由于截图为小图截图,变大过程中会有模糊的现象,如果设置了该属性,我会对神奇移动视图中的包含了image的视图进行检测,如果能检测到image则直接取image,而不截图,就能解决模糊的问题,代码如下

- (UIView *)_xw_snapshotView:(UIView *)view{

CALayer *layer = view.layer;

UIView *snapView = [UIView new];

snapView.frame = view.frame;

BOOL imgMode = [objc_getAssociatedObject(view, &kXWMagicMovePropertyInViewKey) boolValue] || _imageMode;

UIImage *img = nil;

if (imgMode) {//如果开启imgMode,优先直接获取图片,避免截图时时从小到大造成的模糊

if ([view isKindOfClass:[UIImageView class]]) {//取imageView中的image

img = [(UIImageView *)view image];

}else if ([view isKindOfClass:[UIButton class]]){//取button中的image

img = [(UIButton *)view currentImage];

}

if (!img && [view isKindOfClass:[UIView class]]) {//没取到尝试取content

img = [UIImage imageWithCGImage:(__bridge CGImageRef)view.layer.contents];

}

}

//若都没有取到,则截图

if (!img) {

UIGraphicsBeginImageContextWithOptions(layer.bounds.size, layer.opaque, 0);

CGContextRef context = UIGraphicsGetCurrentContext();

[layer renderInContext:context];

img = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

}

snapView.layer.contents = (__bridge id)img.CGImage;

return snapView;

}

关于抽屉效果的全屏拖动

1、抽屉效果由于注册的手势都是在控制器的的视图上,如果做QQ设置界面的效果,不可能在toVC之外点击和拖动能够back,我的思路是会在toVC没有覆盖的区域添加一个透明视图,给透明视图加上点击和拖动手势,具体代码如下

//首先需要设置点击和拖动的back操作,block中应该包含你的dismiss或者pop逻辑

/**

*  开启边缘(就是屏幕除开toView所占用的部分)back手势和边缘点击返回效果,类似于QQ设置界面的返回效果

*

*  @param backConfig 返回操作,您的dismiss或者pop操作

*/

- (void)xw_enableEdgeGestureAndBackTapWithConfig:(dispatch_block_t)backConfig;

//添加全屏手势代码如下

/**

*  添加全局手势和点击视图

*/

- (void)_xw_addFullGestureAndTapBackViewInContainerView:(UIView *)containerView toView:(UIView *)toView distance:(CGFloat)distance{

CGFloat width = _vertical ? containerView.frame.size.width : containerView.frame.size.width - fabs(distance);

CGFloat height = _vertical ? containerView.frame.size.height - fabs(distance) : containerView.frame.size.height;

//如果toVC是全屏铺满则无需添加全局手势,直接使用toVC的view的手势就好了

if (width == 0 || height == 0)return;

if (!_backConfig) return;

//如果toView注册过手势,我们直接获取这个手势

NSArray<UIGestureRecognizer *> *gestures = toView.gestureRecognizers;

__block id target = nil;

[gestures enumerateObjectsUsingBlock:^(UIGestureRecognizer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

NSString *panType = objc_getAssociatedObject(obj, "xw_interactivePanKey");

if ([panType isEqualToString:@"xw_interactiveBackPan"] && obj.delegate) {

target = obj.delegate;

*stop = YES;

}

}];

CGFloat x = _vertical || _direction == XWDrawerAnimatorDirectionRight ? 0 : -distance;

CGFloat y = !_vertical || _direction == XWDrawerAnimatorDirectionBottom ? 0 : -distance;

UIControl *gestureView = [UIControl new];

//添加点击事件

[gestureView addTarget:self action:@selector(_xw_backConfig) forControlEvents:UIControlEventTouchUpInside];

gestureView.frame = CGRectMake(x, y, width, height);

gestureView.backgroundColor = [UIColor clearColor];

//第一种情况,toView已经添加了返回手势,我们直接拿到该手势的target和action

if (target) {

//给containerView添加全局手势

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:NSSelectorFromString(@"_xw_handleGesture:")];

[containerView addGestureRecognizer:pan];

}else{

//第二种情况,toView没有添加手势,我们需要创建一个

__weak typeof(self)weakSelf = self;

XWInteractiveTransition *backTransition = [XWInteractiveTransition xw_interactiveTransitionWithDirection:(XWInteractiveTransitionGestureDirection)_direction
config:^{

weakSelf.backConfig();

} edgeSpacing:0];

backTransition.panRatioBaseValue = _vertical ? containerView.frame.size.height : containerView.frame.size.width;

[backTransition xw_addPanGestureForView:gestureView to:NO];

//        [self xw_setBackInteractiveTransition:backTransition];

[self setValue:backTransition forKey:@"backTransition"];

}

[containerView addSubview:gestureView];

}

解决动画生硬

1、先看小圆点效果的例子,前面是解决前写的,后面是现在的

未解决



解决后



2、问题原因:在手势结束后该效果不会动画的过渡到成功或者失败,而是整个转场进度会直接update到0或者1,就木有动画了

3、解决:在手指松开的时候,我会开启一个CADisplayLink来不断的刷新整个转场进度到1或者0,来达到动画的效果,具体代码如下

case UIGestureRecognizerStateEnded:{//转场结束后

//判断是否需要timer

if (!_timerEable) {

_percent >= 0.5 ? [self _xw_finish] : [self _xw_cancle];

return;

}

//判断此时是否已经转场完成,大于1或者小于0

BOOL canEnd = [self _xw_canEndInteractiveTransitionWithPercent:_percent];

if (canEnd) return;

//开启timer

[self _xw_setEndAnimationTimerWithPercent:_percent];

//设置开启timer

- (void)_xw_setEndAnimationTimerWithPercent:(CGFloat)percent{

_percent = percent;

//根据失败还是成功设置刷新间隔

if (percent > 0.5) {

_timeDis = (1 - percent) / ((1 - percent) * 60);

}else{

_timeDis = percent / (percent * 60);

}

//开启timer

[self _xw_startTimer];

}

//开启timer

- (void)_xw_startTimer{

if (_timer) {

return;

}

_timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(_xw_timerEvent)];

[_timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

}

//timer 事件

- (void)_xw_timerEvent{

if (_percent > 0.5) {

_percent += _timeDis;

}else{

_percent -= _timeDis;

}

//通过timer不断刷新转场进度,达到动画效果

[self _xw_updatingWithPercent:_percent];

//判断进度是否达到0和1,达到则结束timer,结束转场

BOOL canEnd = [self _xw_canEndInteractiveTransitionWithPercent:_percent];

if (canEnd) {

[self _xw_stopTimer];

}

}

解决闪烁问题

1、闪烁原因:在不使用UIView的动画block时,我们直接为layer添加一个CAAnimtion,此时会先设置modelLayer为转场成功的状态,比如小圆点效果会设置path为大圆的path,但是如果转场失败,presentLayer依然会先变为modelLayer设置的成功值,然后动画才结束,走我们的转场失败逻辑,所以就会闪烁

2、解决:我把手势改变的一些关键状态通过代理传出来,在手势结束前,我们如果检查到失败,可以先将modelLayer的值标记为失败时候的值,也就是初始值,就解决了该问题

3、事例代码

//手势转场时的代理事件,animator默认为为其手势的代理,复写对应的代理事件可处理一些手势失败闪烁的情况

@protocol XWInteractiveTransitionDelegate <NSObject>

@optional

/**手势转场即将开始时调用*/

- (void)xw_interactiveTransitionWillBegin:(XWInteractiveTransition *)interactiveTransition;

/**手势转场中调用*/

- (void)xw_interactiveTransition:(XWInteractiveTransition *)interactiveTransition isUpdating:(CGFloat)percent;

/**如果开始了转场手势timer,会在松开手指,timer开始的时候调用*/

- (void)xw_interactiveTransitionWillBeginTimerAnimation:(XWInteractiveTransition *)interactiveTransition;

/**手势转场结束的时候调用*/

- (void)xw_interactiveTransition:(XWInteractiveTransition *)interactiveTransition willEndWithSuccessFlag:(BOOL)flag percent:(CGFloat)percent;

@end

//我在小圆点扩散效果中处理的如下

- (void)xw_interactiveTransition:(XWInteractiveTransition *)interactiveTransition willEndWithSuccessFlag:(BOOL)flag percent:(CGFloat)percent{

if (!flag) {

//防止失败后的闪烁,如果失败将遮罩的path设置为其实的小圆path

_maskLayer.path = _startPath.CGPath;

}

_containerView.userInteractionEnabled = YES;

}

关于coolTransiton

1、直接通过枚举初始化就有已经集成的部分效果,具体如下:

typedef NS_ENUM(NSUInteger, XWCoolTransitionAnimatorType){

//全屏翻页

XWCoolTransitionAnimatorTypePageFlip,

//中间翻页

XWCoolTransitionAnimatorTypePageMiddleFlipFromLeft,

XWCoolTransitionAnimatorTypePageMiddleFlipFromRight,

XWCoolTransitionAnimatorTypePageMiddleFlipFromTop,

XWCoolTransitionAnimatorTypePageMiddleFlipFromBottom,

//开窗

XWCoolTransitionAnimatorTypePortal,

//折叠

XWCoolTransitionAnimatorTypeFoldFromLeft,

XWCoolTransitionAnimatorTypeFoldFromRight,

//爆炸

XWCoolTransitionAnimatorTypeExplode,

//酷炫线条效果

XWCoolTransitionAnimatorTypeHorizontalLines,

XWCoolTransitionAnimatorTypeVerticalLines,

//扫描效果

XWCoolTransitionAnimatorTypeScanningFromLeft,

XWCoolTransitionAnimatorTypeScanningFromRight,

XWCoolTransitionAnimatorTypeScanningFromTop,

XWCoolTransitionAnimatorTypeScanningFromBottom,

};

2、 cool转场效果中的Portal、Fold、Explode效果的部分代码逻辑来源于ColinEberhardt/VCTransitionsLibrary,非常感谢作者,我只是将其进行了部分改动,以便对手势的支持更加完善,里面还有许多其他效果,本人经历有限就没有再集成进来了,大家可以自行查看;cool转场效果的Lines的想法来自于cinkster/HUAnimator
非常感谢作者,但是由于作者在对toVC截图采用了延迟的方式来处理,导致了不好处理的bug和一些手势上的bug,对此我采用了另一种方式来解决截图的问题,使用了layer的contentRect属性,解决了发现的问题,相关代码请自行查看

关于FilterTransition

1、XWFilterAnimator 全都是基于不同的CIFilter产生的一些滤镜效果,貌似在模拟器无法运行这些效果,请在真机上测试,直接通过枚举初始化就有已经集成的部分效果,具体如下:

typedef NS_ENUM(NSUInteger, XWFilterAnimatorType) {

XWFilterAnimatorTypeBoxBlur,//模糊转场,对应CIBoxBlur

XWFilterAnimatorTypeSwipe,//滑动过渡转场,对应CISwipeTranstion

XWFilterAnimatorTypeBarSwipe,//对应CIBarSwipeTranstion

XWFilterAnimatorTypeMask,//按指定遮罩图片转场,对应CIDisintegrateWithMaskTransition

XWFilterAnimatorTypeFlash,//闪烁转场,对应CIFlashTransition

XWFilterAnimatorTypeMod,//条纹转场 对应CIModTransition

XWFilterAnimatorTypePageCurl,//翻页转场 对应CIPageCurlWithShadowTransition

XWFilterAnimatorTypeRipple,//波纹转场,对应CIRippleTransition

XWFilterAnimatorTypeCopyMachine, //效果和XWCoolAnimator中的Scanning效果类似,对应CICopyMachineTransition

};

2、如果想要添加其他滤镜转场,可以尝试我的FilterTransition中书写分类的方式,只需要指定CIFilter和相关逻辑即可

关于自定义转场效果

1、你只需要继承于XWTransitionAnimator,就像我上面所有的效果器一样,然后复写需要的属性和两个必须的方法即可,然后你就可以使用你自定义的效果器转场,XWTransitionAnimator头文件如下:

@interface XWTransitionAnimator : NSObject<UIViewControllerTransitioningDelegate, UINavigationControllerDelegate, UITabBarControllerDelegate, XWInteractiveTransitionDelegate>

//to转场时间 默认0.5

@property (nonatomic, assign) NSTimeInterval toDuration;

//back转场时间 默认0.5

@property (nonatomic, assign) NSTimeInterval backDuration;

//是否需要开启手势timer,某些转场如果在转成过程中所开手指,不会有动画过渡,显得很生硬,开启timer后,松开手指,会用timer不断的刷新转场百分比,消除生硬的缺点

@property (nonatomic, assign) BOOL needInteractiveTimer;

/**

*  配置To过程动画(push, present),自定义转场动画应该复写该方法

*/

- (void)xw_setToAnimation:(id<UIViewControllerContextTransitioning>)transitionContext;

/**

*  配置back过程动画(pop, dismiss),自定义转场动画应该复写该方法

*/

- (void)xw_setBackAnimation:(id<UIViewControllerContextTransitioning>)transitionContext;

@end

2、这样就只需要关心动画的逻辑,其余的事情就不用管了,不过如果遇到闪烁问题,你只需要复写相关的手势代理方法,就像我在小圆点转场中一样,因为XWTransitionAnimator默认是手势管理者的代理,所以直接实现代理方法就好了

SloppySwiper - iOS系统自带的UINavigationController要7.0才支持,但不过该手势只能从屏幕左侧边缘识别,如果要扩大到整个屏幕范围怎么办?配合一个SloppySwiper无需代码就可以轻松实现。



SCNavigation - UINavigation可以右滑返回,隐藏UINavigationBar。



UINavigationController-YRBackGesture -
支持右滑返回手势,标题栏不动。



GHSidebarNav - 现在比较流行使用侧开(侧滑)菜单设计。试了不少控件,感觉GHSidebarNav最成熟,尤其对纯代码创建的界面兼容性最好。在Storyboard中使用GHSidebarNav侧开菜单控件



iOS-Slide-Menu - 能够类似Facebook和Path那样弹出左右边栏侧滑菜单,还支持手势。多种可以自定义的属性
(非常不错)。



ECSlidingViewController - 侧滑菜单。



animated-tab-bar - 让 Tabbar items能显示萌萌的动画。



RESideMenu - 侧开菜单,qq类似。



JHMenuTableViewDemo - 仿网易邮箱列表侧滑菜单。



QQConfiguration - swift,QQ-iPhone端框架,左侧菜单栏拖动手势。



KGFloatingDrawer - 侧滑菜单,qq类似,KyleGoddard/KGFloatingDrawer:一款适合于大屏手机或平板的浮动抽屉式导航界面组件。效果很赞-
侧开菜单,qq类似(与RESideMenu类似)。



AIFlatSwitch - 一款带平滑过渡动画的 Switch 组件类,类相同风格的 Menu/BackHamburgerButton,类似相同风格的
Menu/Closehamburger-button.



SwiftPages - 高可定制类似 Instagram 视图滑动切换功能类库。API 简单、易用。



FlipBoardNavigationController - FlipBoardNavigationController。





MMDrawerController - 最多人用的一个有关侧边“抽屉”导航框架,里面还有很多你意想不到的交互效果,侧滑。



UIWebView翻页返回效果 -
UIWebView翻页返回效果(变通方法)。



LLSlideMenu - 一个弹性侧滑菜单,弹性动画原理借鉴该项目中阻尼函数实现。



FlowingMenu.swift - 菜单如此出场方式(橡皮筋弹跳式动画)好玩又有趣。



源码连接

iOS开源项目推荐|侧滑与右滑返回手势

几句代码快速集成自定义转场效果+ 全手势驱动
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: