此库作为
SHUIKitBlocks
的组成部分被使用,覆盖了 Foundation、UIKit、CoreLocation、GameKit、MapKit 和 iOS 应用程序架构的许多其他方面,以填补这些方面缺失的部分。
通过 blocks 提供动画过渡和交互式过渡的 API。交互式过渡带有处理手势和进度的 block 辅助工具。所有这些都在 UIViewController 的基础上进行分类,因此与 UINavigationController 和 UITabBarController 配合良好
pod 'SHTransitionBlocks'
将这些放在指定的文件或您的项目前缀文件中
#import "UIViewController+SHTransitionBlocks.h"
或者
#import "SHTransitionBlocks.h"
因此,我们将向 navigationControllers 视图的左侧边缘添加一个 pan 手势,用于弹出堆栈。这使得任何在 navigationController 上推入的控制器都可以通过左侧 pan 手势弹出自身。动画本身没有特别之处。
您始终可以查看 示例。只需下载 zip 文件并构建和运行。
-(void)viewDidLoad; {
[super viewDidLoad];
我们可以在 UINavigationController 堆栈的第一个视图控制器上添加块,但是这里必须执行
viewDidLoad
,但是您可以轻松提取出来(只要取出整个块!)
[self.navigationController SH_setAnimationDuration:0.5 withPreparedTransitionBlock:^(UIView *containerView, UIViewController *fromVC, UIViewController *toVC, NSTimeInterval duration, id<SHViewControllerAnimatedTransitioning> transitionObject, SHViewControllerAnimationCompletionBlock transitionDidComplete) {
有很多回调变量,让我们来分解它。
if (transitionObject.isReversed == NO) {
toVC.view.layer.affineTransform = CGAffineTransformMakeTranslation(CGRectGetWidth(toVC.view.frame), 0);
}
else {
toVC.view.layer.affineTransform = CGAffineTransformMakeTranslation(-CGRectGetWidth(toVC.view.frame), 0);
}
请注意,我们使用
transitionObject
访问isReversed
标志来检查弹出或推送
[UIView animateWithDuration:duration delay:0 options:kNilOptions animations:^{
toVC.view.layer.affineTransform = CGAffineTransformIdentity;
if(transitionObject.isReversed) {
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformMakeTranslation(CGRectGetWidth(fromVC.view.frame), 0);
// fromView.layer.affineTransform = CGAffineTransformScale(t, 0.5, 0.5);
fromVC.view.layer.affineTransform = t;
}
else {
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformMakeTranslation(-CGRectGetWidth(fromVC.view.frame), 0);
fromVC.view.layer.affineTransform = t;
}
没有复杂的事情,只是一个正常的动画。注意我们使用了 duration 变量来进行 UIView Animation。
} completion:^(BOOL finished) {
toVC.view.layer.affineTransform = CGAffineTransformIdentity;
fromVC.view.layer.affineTransform = CGAffineTransformIdentity;
transitionDidComplete();
}];
}];
注意我们如何调用 transitionDidComplete(); 来清理块。
[self.navigationController SH_setInteractiveTransitionWithGestureBlock:^UIGestureRecognizer *(
UIScreenEdgePanGestureRecognizer *edgeRecognizer) {
edgeRecognizer.edges = UIRectEdgeLeft;
return edgeRecognizer;
我们可以使用跟随 block 的 UIScreenEdgePanGestureRecognizer,或者创建我们自己的并返回它。任何 UIGestureRecognizer 的子类都适用。在我们当前的情况下,我们将其设置为 UIRectEdgeLeft pans,因为我们正在弹出。
} onGestureCallbackBlock:^void(UIViewController * controller, UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
UIScreenEdgePanGestureRecognizer * recognizer = (UIScreenEdgePanGestureRecognizer*)sender;
CGFloat progress = [recognizer translationInView:sender.view].x / (recognizer.view.bounds.size.width * 1.0);
progress = MIN(1.0, MAX(0.0, progress));
if (state == UIGestureRecognizerStateBegan) {
// Create a interactive transition and pop the view controller
controller.SH_interactiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[(UINavigationController *)controller popViewControllerAnimated:YES];
}
else if (state == UIGestureRecognizerStateChanged) {
// Update the interactive transition's progress
[controller.SH_interactiveTransition updateInteractiveTransition:progress];
}
else if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled) {
// Finish or cancel the interactive transition
if (progress > 0.5) {
[controller.SH_interactiveTransition finishInteractiveTransition];
}
else {
[controller.SH_interactiveTransition cancelInteractiveTransition];
}
controller.SH_interactiveTransition = nil;
}
}];
稍微复杂一些,但拆解开来,一旦开始手势操作,我们将
controller
(在我们的例子中,即navigationController)的SH_interactiveTransition
设置为UIPercentDrivenInteractiveTransition
。我们仅在即将弹出时执行此操作,而不会在任何其他时候执行,因为我们希望在没有任何手势更新时弹出SH_interactiveTransition
为nil。请注意,我们会在手势结束后将其设置为nil。
[self.navigationController SH_setAnimatedControllerBlock:^id<UIViewControllerAnimatedTransitioning>(UINavigationController *navigationController, UINavigationControllerOperation operation, UIViewController *fromVC, UIViewController *toVC) {
navigationController.SH_animatedTransition.reversed = operation == UINavigationControllerOperationPop;
return navigationController.SH_animatedTransition;
}];
在这里,我们使用SHNavigationControllerBlocks功能来实现这里的动画过渡,而不是使用代理回调。一旦创建原型,它也更容易提取出来。与代理相比,使用代码块进行原型设计更方便。
[self.navigationController SH_setInteractiveControllerBlock:^id<UIViewControllerInteractiveTransitioning>(UINavigationController *navigationController, id<UIViewControllerAnimatedTransitioning> animationController) {
return navigationController.SH_interactiveTransition;
}];
}
如果我们当前没有任何正在处理的手势,则
navigationController.SH_interactiveTransition
将是nil,并发生普通弹出。但是,如果我们正在处理某个手势,则navigationController.SH_interactiveTransition
将不是nil,并且会被作为手势进度处理的交互控制器传递。
@protocol SHViewControllerAnimatedTransitioning <UIViewControllerAnimatedTransitioning>
@property(nonatomic,assign,getter = isReversed) BOOL reversed;
@property(nonatomic,strong) NSMutableDictionary * userInfo;
@property(nonatomic,readonly) id<UIViewControllerContextTransitioning> transitionContext;
@end
typedef void(^SHTransitionAnimationCompletionBlock)();
typedef void(^SHTransitionPreparedAnimationBlock)(UIView * containerView,
UIViewController * fromVC,
UIViewController * toVC,
NSTimeInterval duration,
id<SHViewControllerAnimatedTransitioning> transitionObject,
SHTransitionAnimationCompletionBlock transitionDidComplete
);
typedef void(^SHTransitionAnimationBlock)(id<SHViewControllerAnimatedTransitioning> transitionObject);
typedef NSTimeInterval(^SHTransitionDurationBlock)(id<SHViewControllerAnimatedTransitioning> transitionObject);
typedef UIGestureRecognizer *(^SHTransitionGestureRecognizerCreationBlock)(UIScreenEdgePanGestureRecognizer * edgeRecognizer);
typedef void(^SHTransitionCallbackGestureRecognizerBlock)(UIViewController * controller,
UIGestureRecognizer * recognizer,
UIGestureRecognizerState state,
CGPoint location
);
@interface UIViewController (SHTransitionBlocks)
@property(nonatomic,strong, setter = SH_setInteractiveTransition:) UIPercentDrivenInteractiveTransition * SH_interactiveTransition;
-(void)SH_setInteractiveTransitionWithGestureBlock:(SHTransitionGestureRecognizerCreationBlock)theGestureCreateBlock
onGestureCallbackBlock:(SHTransitionCallbackGestureRecognizerBlock)theGestureCallbackBlock;
-(id<SHViewControllerAnimatedTransitioning>)SH_animatedTransition;
-(void)SH_setAnimationDuration:(NSTimeInterval)theDuration
withPreparedTransitionBlock:(SHTransitionPreparedAnimationBlock)theBlock;
-(void)SH_setAnimatedTransitionBlock:(SHTransitionAnimationBlock)theBlock;
-(void)SH_setDurationTransitionBlock:(SHTransitionDurationBlock)theBlock;
@property(nonatomic,readonly) SHTransitionPreparedAnimationBlock SH_blockAnimationDurationWithPreparedTransition;
@property(nonatomic,readonly) SHTransitionAnimationBlock SH_blockAnimatedTransition;
@property(nonatomic,readonly) SHTransitionDurationBlock SH_blockDurationTransition;
@end
如果您在项目中使用SHTransitionBlocks,我非常愿意听到您的想法。
电子邮件:[email protected]
推特:@seivanheidari
SHTransitionBlocks是由Seivan于2013年创作的,并根据MIT许可协议免费分发。请参阅LICENSE.md
文件。