iPhone:如何切换animation制表符?
我使用UITabBarController.selectedIndex
在标签栏驱动的应用程序中以编程方式切换选项卡。 我试图解决的问题是如何animation之间的意见过渡。 即。 从当前选项卡的视图到选定选项卡的视图。
第一个想法是利用UITabBarControllerDelegate
,但似乎这是不会调用时编程切换标签。 我现在正在考虑UITabBarDelegate.didSelectItem
:作为一个可能的钩子来设置一个过渡animation。
有没有人设法animation的过渡? 如果是的话,怎么样?
更新04/2016: Justed想要更新这个说声谢谢大家的所有选票。 还请注意,这是最初写回来的时候…在ARC之前,在约束之前,…很多东西! 所以请在决定是否使用这些技术时考虑到这一点。 可能有更多的现代方法。 哦,如果你find一个。 请添加一个响应,以便大家可以看到。 谢谢。
一段时间之后 …
经过很多研究,我提出了两个可行的解决scheme。 这两个工作和做标签之间的animation。
解决scheme1:从视图(简单)
这是最简单的,并使用预定义的UIView转换方法。 有了这个解决scheme,我们不需要pipe理视图,因为这个方法为我们做了工作。
// Get views. controllerIndex is passed in as the controller we want to go to. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Transition using a page curl. [UIView transitionFromView:fromView toView:toView duration:0.5 options:(controllerIndex > tabBarController.selectedIndex ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown) completion:^(BOOL finished) { if (finished) { tabBarController.selectedIndex = controllerIndex; } }];
解决scheme2:滚动(更复杂)
一个更复杂的解决scheme,但给你更多的animation控制。 在这个例子中,我们可以看到幻灯片的打开和closures。 有了这个,我们需要自己pipe理观点。
// Get the views. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Get the size of the view area. CGRect viewSize = fromView.frame; BOOL scrollRight = controllerIndex > tabBarController.selectedIndex; // Add the to view to the tab bar view. [fromView.superview addSubview:toView]; // Position it off screen. toView.frame = CGRectMake((scrollRight ? 320 : -320), viewSize.origin.y, 320, viewSize.size.height); [UIView animateWithDuration:0.3 animations: ^{ // Animate the views on and off the screen. This will appear to slide. fromView.frame =CGRectMake((scrollRight ? -320 : 320), viewSize.origin.y, 320, viewSize.size.height); toView.frame =CGRectMake(0, viewSize.origin.y, 320, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Remove the old view from the tabbar view. [fromView removeFromSuperview]; tabBarController.selectedIndex = controllerIndex; } }];
Swift中的这个解决scheme:
extension TabViewController: UITabBarControllerDelegate { public func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let fromView: UIView = tabBarController.selectedViewController!.view let toView : UIView = viewController.view if fromView == toView { return false } UIView.transitionFromView(fromView, toView: toView, duration: 0.3, options: UIViewAnimationOptions.TransitionCrossDissolve) { (finished:Bool) in } return true } }
以下是我尝试使用代码formsdrekka进入委托(UITabBarControllerDelegate)的方法
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { NSArray *tabViewControllers = tabBarController.viewControllers; UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = viewController.view; if (fromView == toView) return false; NSUInteger fromIndex = [tabViewControllers indexOfObject:tabBarController.selectedViewController]; NSUInteger toIndex = [tabViewControllers indexOfObject:viewController]; [UIView transitionFromView:fromView toView:toView duration:0.3 options: toIndex > fromIndex ? UIViewAnimationOptionTransitionFlipFromLeft : UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) { if (finished) { tabBarController.selectedIndex = toIndex; } }]; return true; }
我的解决scheme为iOS7.0或以上。
您可以在选项卡的代理中指定自定义animation控制器。
像这样实现一个animation控制器:
@interface TabSwitchAnimationController : NSObject <UIViewControllerAnimatedTransitioning> @end @implementation TabSwitchAnimationController - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.2; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView* toView = toVC.view; UIView* fromView = fromVC.view; UIView* containerView = [transitionContext containerView]; [containerView addSubview:toView]; toView.frame = [transitionContext finalFrameForViewController:toVC]; // Animate by fading toView.alpha = 0.0; [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{ toView.alpha = 1.0; } completion:^(BOOL finished) { toView.alpha = 1.0; [fromView removeFromSuperview]; [transitionContext completeTransition:YES]; }]; } @end
然后在你的UITabBarControllerDelegate中使用它:
- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { return [[TabSwitchAnimationController alloc] init]; }
我认为你可以使用CATransition轻松实现UITabBarControlelr的转换; 这也将解决使用transitionFromView:toView的任何副作用:
在自定义的从UITabBarController扩展的TabBarController类中使用这个。
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController (UIViewController*)viewController { CATransition *animation = [CATransition animation]; [animation setType:kCATransitionFade]; [animation setDuration:0.25]; [animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]]; [self.view.window.layer addAnimation:animation forKey:@"fadeTransition"]; }
希望这可以帮助 :)
我在这里尝试了各种答案后写了一篇文章 。
代码在Swift中,你可以通过调用animateToTab
以编程方式改变选项卡。
func animateToTab(toIndex: Int) { let tabViewControllers = viewControllers! let fromView = selectedViewController!.view let toView = tabViewControllers[toIndex].view let fromIndex = tabViewControllers.indexOf(selectedViewController!) guard fromIndex != toIndex else {return} // Add the toView to the tab bar view fromView.superview!.addSubview(toView) // Position toView off screen (to the left/right of fromView) let screenWidth = UIScreen.mainScreen().bounds.size.width; let scrollRight = toIndex > fromIndex; let offset = (scrollRight ? screenWidth : -screenWidth) toView.center = CGPoint(x: fromView.center.x + offset, y: toView.center.y) // Disable interaction during animation view.userInteractionEnabled = false UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { // Slide the views by -offset fromView.center = CGPoint(x: fromView.center.x - offset, y: fromView.center.y); toView.center = CGPoint(x: toView.center.x - offset, y: toView.center.y); }, completion: { finished in // Remove the old view from the tabbar view. fromView.removeFromSuperview() self.selectedIndex = toIndex self.view.userInteractionEnabled = true }) }
如果你想要所有的选项卡更改为具有animation,然后像这样挂钩到UITabBarControllerDelegate
:
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let tabViewControllers = tabBarController.viewControllers! guard let toIndex = tabViewControllers.indexOf(viewController) else { return false } // Our method animateToTab(toIndex) return true }
我在Swift中的解决scheme:
创build自定义的TabBar类并将其设置在故事板TabBar中
class MainTabBarController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self // Do any additional setup after loading the view. } func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let tabViewControllers = tabBarController.viewControllers! let fromView = tabBarController.selectedViewController!.view let toView = viewController.view if (fromView == toView) { return false } let fromIndex = tabViewControllers.indexOf(tabBarController.selectedViewController!) let toIndex = tabViewControllers.indexOf(viewController) let offScreenRight = CGAffineTransformMakeTranslation(toView.frame.width, 0) let offScreenLeft = CGAffineTransformMakeTranslation(-toView.frame.width, 0) // start the toView to the right of the screen if (toIndex < fromIndex) { toView.transform = offScreenLeft fromView.transform = offScreenRight } else { toView.transform = offScreenRight fromView.transform = offScreenLeft } fromView.tag = 124 toView.addSubview(fromView) self.view.userInteractionEnabled = false UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { toView.transform = CGAffineTransformIdentity }, completion: { finished in let subViews = toView.subviews for subview in subViews{ if (subview.tag == 124) { subview.removeFromSuperview() } } tabBarController.selectedIndex = toIndex! self.view.userInteractionEnabled = true }) return true } }
而不是使用tabBarController:shouldSelectViewController:
更好的实现tabBarController:animationControllerForTransitionFromViewController:toViewController:
TransitioningObject.swift
import UIKit class TransitioningObject: NSObject, UIViewControllerAnimatedTransitioning { func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromView: UIView = transitionContext.viewForKey(UITransitionContextFromViewKey)! let toView: UIView = transitionContext.viewForKey(UITransitionContextToViewKey)! transitionContext.containerView().addSubview(fromView) transitionContext.containerView().addSubview(toView) UIView.transitionFromView(fromView, toView: toView, duration: transitionDuration(transitionContext), options: UIViewAnimationOptions.TransitionCrossDissolve) { finished in transitionContext.completeTransition(true) } } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.25 } }
TabBarViewController.swift
import UIKit class TabBarViewController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self } // MARK: - Tabbar delegate func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TransitioningObject() } }
修正了跳动animation
UIView * fromView = self.view.superview;
这可以通过两种方式来解决
1 – 在你的AppDelegate.m文件中写一次。 请记住在AppDelegate.h中使用冒号(:)后面包含UITabBarControllerDelegate
-(void)tabBarController:(UITabBarController *)tabBarControllerThis didSelectViewController:(UIViewController *)viewController { [UIView transitionWithView:viewController.view duration:0.1 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve animations:^(void){ } completion:^(BOOL finished){ [UIView beginAnimations:@"animation" context:nil]; [UIView setAnimationDuration:0.7]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:viewController.view cache:NO]; [UIView commitAnimations]; }]; }
2 – 在您的每个ViewController.m文件中写入
-(void)viewWillAppear:(BOOL)animated { [UIView transitionWithView:self.view duration:1.0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve animations:^(void){ [super viewWillAppear:YES]; } completion:^(BOOL finished){ }]; }
希望这个帮助…!
drekka的回答真的很棒。 我调整了滚动过渡,所以animation看起来更像苹果的推animation。 我在第一个animation完成后添加了一个额外的animation,以使滑动效果看起来正确。
// Disable interaction during animation to avoids bugs. self.tabBarController.view.userInteractionEnabled = NO; // Get the views. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Get the size of the view area. CGRect viewSize = fromView.frame; BOOL scrollRight = controllerIndex > tabBarController.selectedIndex; // Add the to view to the tab bar view. [fromView.superview addSubview:toView]; [fromView.superview addSubview:fromView]; self.tabBarController.selectedIndex = 0; // Position it off screen. toView.frame = CGRectMake((scrollRight ? (viewSize.size.width *.25) : -(viewSize.size.width * .25 )), viewSize.origin.y, viewSize.size.width, viewSize.size.height); [UIView animateWithDuration:0.25 animations: ^{ // Animate the views on and off the screen. [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; fromView.frame = CGRectMake(viewSize.size.width * .95, viewSize.origin.y, viewSize.size.width, viewSize.size.height); toView.frame = CGRectMake((viewSize.origin.x * .90), viewSize.origin.y, viewSize.size.width, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Being new animation. [UIView animateWithDuration:0.2 animations: ^{ [UIView setAnimationCurve:UIViewAnimationCurveLinear]; fromView.frame = CGRectMake(viewSize.size.width, viewSize.origin.y, viewSize.size.width, viewSize.size.height); toView.frame = CGRectMake((viewSize.origin.x), viewSize.origin.y, viewSize.size.width, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Remove the old view from the tabbar view. [fromView removeFromSuperview]; // Restore interaction. self.tabBarController.view.userInteractionEnabled = YES; } }]; } }];
我使用@ Mofumofu的解决scheme,并将其升级到Swift 1.2,并实施了一个向上/向下的animation。 意思是说,如果新的viewcontroller的索引大于旧的索引,那么新的ViewController就会popup,并将旧的ViewController推回去。 否则,方向就会下降。
class TabScrollPageAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let tabBarController: UITabBarController init(tabBarController: UITabBarController) { self.tabBarController = tabBarController } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.5 } func animateTransition(transitionContext: UIViewControllerContextTransitioning) { if let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey), let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) { let fromView = fromVC.view let toView = toVC.view let containerView = transitionContext.containerView() var directionUpwardMultiplier: CGFloat = 1.0 if let vcs = tabBarController.viewControllers as? [UIViewController], let fIndex = find(vcs, fromVC), let tIndex = find(vcs, toVC) { directionUpwardMultiplier = (fIndex < tIndex) ? +1.0 : -1.0 } containerView.clipsToBounds = false containerView.addSubview(toView) var fromViewEndFrame = fromView.frame fromViewEndFrame.origin.y -= (containerView.frame.height * directionUpwardMultiplier) let toViewEndFrame = transitionContext.finalFrameForViewController(toVC) var toViewStartFrame = toViewEndFrame toViewStartFrame.origin.y += (containerView.frame.height * directionUpwardMultiplier) toView.frame = toViewStartFrame toView.alpha = 0.0 UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in toView.alpha = 1.0 toView.frame = toViewEndFrame fromView.alpha = 0.0 fromView.frame = fromViewEndFrame }, completion: { (completed) -> Void in toView.alpha = 1.0 fromView.removeFromSuperview() transitionContext.completeTransition(completed) containerView.clipsToBounds = true }) } } }
在Container ViewController中:
extension XYViewController: UITabBarControllerDelegate { func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TabScrollPageAnimationController(tabBarController: tabBarController) } }
这是我的Swift 3解决scheme:
我重写我的UITabBarViewController selectedIndex像这样:
override var selectedIndex: Int{ get{ return super.selectedIndex } set{ animateToTab(toIndex: newValue) super.selectedIndex = newValue } }
然后我使用这个模仿本地推/animationanimation的函数:
func animateToTab(toIndex: Int) { guard let tabViewControllers = viewControllers, tabViewControllers.count > toIndex, let fromViewController = selectedViewController, let fromIndex = tabViewControllers.index(of: fromViewController), fromIndex != toIndex else {return} view.isUserInteractionEnabled = false let toViewController = tabViewControllers[toIndex] let push = toIndex > fromIndex let bounds = UIScreen.main.bounds let offScreenCenter = CGPoint(x: fromViewController.view.center.x + bounds.width, y: toViewController.view.center.y) let partiallyOffCenter = CGPoint(x: fromViewController.view.center.x - bounds.width*0.25, y: fromViewController.view.center.y) if push{ fromViewController.view.superview?.addSubview(toViewController.view) toViewController.view.center = offScreenCenter }else{ fromViewController.view.superview?.insertSubview(toViewController.view, belowSubview: fromViewController.view) toViewController.view.center = partiallyOffCenter } UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseIn, animations: { toViewController.view.center = fromViewController.view.center fromViewController.view.center = push ? partiallyOffCenter : offScreenCenter }, completion: { finished in fromViewController.view.removeFromSuperview() self.view.isUserInteractionEnabled = true }) }
我希望它有助于:)
我想在一个button上使用两个子视图控制器之间的翻转过渡,并实现它如下:
-(IBAction)flipViewControllers:(id)sender{ NSUInteger index = self.selectedIndex; index++; if(index >= self.childViewControllers.count){ index = 0; } self.selectedIndex = index; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.75]; [UIView setAnimationTransition:index % 2 ? UIViewAnimationTransitionFlipFromLeft : UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES]; [UIView commitAnimations]; }
我也设置背景颜色为黑色,在我的情况下,我通过设置navigationController.view.backgroundColor,但在你的情况下,它可能是window.backgroundColor可以很容易地在应用程序委托设置。
这是我的工作代码( 对于3个选项卡 ,还没有尝试过更多!!)来animation制表之间的过渡。 它主要基于drekka的解决scheme,但已经在tabbar的委托方法中实现,所以如果你只是复制/粘贴它,它应该做的工作..(你永远不知道!)
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { // Important! We validate that the selected tab is not the current tab, to avoid misplacing views if (tabBarController.selectedViewController == viewController) { return NO; } // Find the selected view's index NSUInteger controllerIndex = 0; for (UIViewController *vc in tabBarController.viewControllers) { if (vc == viewController) { controllerIndex = [tabBarController.viewControllers indexOfObject:vc]; } } CGFloat screenWidth = SCREEN_SIZE.width; // Note: We must invert the views according to the direction of the scrolling ( FROM Left TO right or FROM right TO left ) UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = viewController.view; [fromView.superview addSubview:toView]; CGRect fromViewInitialFrame = fromView.frame; CGRect fromViewNewframe = fromView.frame; CGRect toViewInitialFrame = toView.frame; if ( controllerIndex > tabBarController.selectedIndex ) { // FROM left TO right ( tab0 to tab1 or tab2 ) // The final frame for the current view. It will be displaced to the left fromViewNewframe.origin.x = -screenWidth; // The initial frame for the new view. It will be displaced to the left toViewInitialFrame.origin.x = screenWidth; toView.frame = toViewInitialFrame; } else { // FROM right TO left ( tab2 to tab1 or tab0 ) // The final frame for the current view. It will be displaced to the right fromViewNewframe.origin.x = screenWidth; // The initial frame for the new view. It will be displaced to the right toViewInitialFrame.origin.x = -screenWidth; toView.frame = toViewInitialFrame; } [UIView animateWithDuration:0.2 animations:^{ // The new view will be placed where the initial view was placed toView.frame = fromViewInitialFrame; // The initial view will be place outside the screen bounds fromView.frame = fromViewNewframe; tabBarController.selectedIndex = controllerIndex; // To prevent user interaction during the animation [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; } completion:^(BOOL finished) { // Before removing the initial view, we adjust its frame to avoid visual lags fromView.frame = CGRectMake(0, 0, fromView.frame.size.width, fromView.frame.size.height); [fromView removeFromSuperview]; [[UIApplication sharedApplication] endIgnoringInteractionEvents]; }]; return NO;
}
这在Swift 3中适用于我:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { if let fromView = tabBarController.selectedViewController?.view, let toView = viewController.view { if fromView == toView { return false } UIView.transition(from: fromView, to: toView, duration: 0.2, options: .transitionCrossDissolve) { (finished) in } } return true }
@samwize答案翻译成斯威夫特3 – 2竖起大拇指在这一个,创造一个左赖页效果:
func animateToTab(toIndex: Int) { let tabViewControllers = viewControllers! let fromView = selectedViewController!.view let toView = tabViewControllers[toIndex].view let fromIndex = tabViewControllers.index(of: selectedViewController!) guard fromIndex != toIndex else {return} // Add the toView to the tab bar view fromView?.superview!.addSubview(toView!) // Position toView off screen (to the left/right of fromView) let screenWidth = screenSize.width let scrollRight = toIndex > fromIndex! let offset = (scrollRight ? screenWidth : -screenWidth) toView?.center = CGPoint(x: (fromView?.center.x)! + offset, y: (toView?.center.y)!) // Disable interaction during animation view.isUserInteractionEnabled = false UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { // Slide the views by -offset fromView?.center = CGPoint(x: (fromView?.center.x)! - offset, y: (fromView?.center.y)!); toView?.center = CGPoint(x: (toView?.center.x)! - offset, y: (toView?.center.y)!); }, completion: { finished in // Remove the old view from the tabbar view. fromView?.removeFromSuperview() self.selectedIndex = toIndex self.view.isUserInteractionEnabled = true }) }