iPhone : 애니메이션으로 탭을 전환하는 방법은 무엇입니까?


106

.NET을 사용하여 탭 모음 기반 응용 프로그램에서 프로그래밍 방식으로 탭을 전환하고 UITabBarController.selectedIndex있습니다. 제가 해결하려는 문제는 뷰 간의 전환을 애니메이션으로 만드는 방법입니다. 즉. 현재 탭의보기에서 선택한 탭의보기로.

첫 번째 생각은를 사용하는 것이 UITabBarControllerDelegate었지만 프로그래밍 방식으로 탭을 전환 할 때 호출되지 않는 것으로 보입니다. 나는 이제 UITabBarDelegate.didSelectItem전환 애니메이션을 설정하는 가능한 후크로 고려하고 있습니다.

누구든지 전환을 애니메이션으로 만들 수 있습니까? 그렇다면 어떻게?


1
FWIW, 이러한 투표율이 높은 답변 중 다수는 Runo의 답변Heberti의 답변에 설명 된 사용자 지정 전환보다 앞서 있습니다 . 이것이 이러한 커스텀 애니메이션을 다루는 올바른 방법입니다. WWDC 2013 비디오 뷰 컨트롤러를 사용한 사용자 지정 전환을 참조하십시오 .
Rob

답변:


154

2016 년 4업데이트 : Justed는 모든 투표에 대해 모든 사람에게 감사를 표하기 위해이 내용을 업데이트하고 싶었습니다. 또한 이것은 원래 ARC 이전, 제약 이전, 이전 ... 많은 것들로 돌아가서 작성되었습니다! 따라서 이러한 기술을 사용할지 여부를 결정할 때이 점을 고려하십시오. 더 현대적인 접근 방식이있을 수 있습니다. 아, 그리고 찾으면. 모든 사람이 볼 수 있도록 응답을 추가하세요. 감사.

얼마 후 ...

많은 연구 끝에 두 가지 작업 솔루션을 찾았습니다. 둘 다 작동하고 탭 사이의 애니메이션을 수행했습니다.

솔루션 1 :보기에서 전환 (단순)

이것은 가장 쉽고 미리 정의 된 UIView 전환 방법을 사용합니다. 이 솔루션을 사용하면 방법이 우리를 위해 일하기 때문에 뷰를 관리 할 필요가 없습니다.

// 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;
                    }
                }];

해결 방법 2 : 스크롤 (더 복잡함)

더 복잡한 솔루션이지만 애니메이션을 더 많이 제어 할 수 있습니다. 이 예에서는 슬라이드를 켜고 끌 수있는 뷰를 얻습니다. 이것으로 우리는 뷰를 직접 관리해야합니다.

// 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의이 솔루션 :

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
   }
}

1
답변 주셔서 대단히 감사합니다. 정말 잘 작동합니다. 그러나 두 솔루션 모두에서 하나의 버그를 발견했습니다. 이것이 모든 사람에게 발생하는지 확실하지 않지만 페이지가 전환되면 탐색 표시 줄과 상태 표시 줄 사이에 간격이있는 것 같습니다. 그런 다음 애니메이션이 완료된 후 간격이 닫힙니다. 이로 인해 애니메이션의 엔딩이 약간 불안해집니다. 왜 이런 일이 일어나는지 아십니까?
Enrico Susatyo 2011-06-17

흠, 내 코드에서 발생하지 않았습니다. 이는 창과 상태 표시 줄과 관련하여 새 뷰 프레임의 위치가 올바르지 않은 이전에 본 문제와 매우 유사하게 들립니다. 토 코드를 실행하여 전환없이 뷰를 교체하고 여전히 발생하는지 확인하십시오.
drekka

예, 전환을하지 않고 여전히 발생합니다. 첫 번째 방법을 시도했습니다. 프레임의 위치가 될 수 있습니다. 좀 더 다루겠습니다. 나는 ... 프레임을 이동하려하고 fromView와 프레임을 일치하려고했지만 지금까지 운이 없다
엔리코 Susatyo에게

2
@EmileCormier는 TabBar의 대의원에 넣어 shouldSelectViewController방법 및 NO가 돌아 오지
cheesus

2
@drekka 이것은 나를 위해 작동하지 않습니다. controllerIndex의 출처를 설명 할 수 있습니까? 그리고 'toView'에 대해 tabBarControllerDelegate 메서드에서 [viewController view]를 사용하지 않는 이유는 무엇입니까? Thnaks
shannoga

25

다음은 drekka 코드를 delegate (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;
}

2
메서드 선언에 따라 값을 반환해야하지만이 접근 방식은 잘 작동합니다. +1
voromax 2013 년

2
self.delegate = self를 추가하여 UITabController 구현 파일에 대리자를 설정할 수 있습니다. viewDidLoad () 함수에서. 이렇게하면 위의 함수를 호출 할 수 있습니다.
Chris Fremgen 2015

21

iOS7.0 이상에 대한 내 솔루션입니다.

탭 모음의 대리자에서 사용자 지정 애니메이션 컨트롤러를 지정할 수 있습니다.

다음과 같은 애니메이션 컨트롤러를 구현합니다.

@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];
}

2
그리고 델리게이트를 TabViewController의 델리게이트 아웃렛에 연결하는 것을 잊지 마십시오. 아름답게 일했습니다. 여기에서 가장 깨끗한 솔루션입니다.
Andrew Duncan

IOS 10.x에서이 기능을 살펴보고 있으므로 스토리 보드를 통해 신속하게이 작업을 수행 할 수 있습니까?
mobibob

16

사용하는 대신 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()
    }
}

3
이것이 가장 좋은 대답 인 것 같습니다. 이 솔루션에는 문제가 없습니다.
sabiland

15

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"];
}

도움이 되었기를 바랍니다 :)


1
나는 우리가 대신 "didSelectViewController"의 "shouldSelectViewController"를 사용할 수 있습니다 생각
라이언 우

13

내가 쓴 글을 여기에 다양한 답변을 시도한 후.

코드는 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
        })
}

모든 탭 변경에 애니메이션이 포함되도록하려면 다음 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
}

이것은 매우 깨끗하고 아름답게 움직입니다.
Mohammad Zekrallah

9

Swift의 내 솔루션 :

사용자 지정 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
 }

}

이것은 ios9에서 작동하지 않습니다- '[UIViewController]?'에서 다운 캐스트와 같은 찾기 메서드에서 오류가 반환되었습니다. to '[UIViewController]'는 선택 사항 만 언 래핑합니다. '!'를 사용하려고 하셨나요?
lozflan

애니메이션이되지 않는 버그 ( finished거짓이 됨)를 발견 한 것을 제외하고는 거의 좋았습니다 . 왜 그런지 모르겠지만, "애니메이션화 할 것이 없다"고 생각하는 CA 변신과 관련이 있다고 생각합니다. 프레임 애니메이션으로 전환했는데 효과가있었습니다.
samwize

3

@Mofumofu의 솔루션을 사용하고 Swift 1.2로 업그레이드하고 업 / 다운 애니메이션도 구현했습니다. 의미, 새 ViewController가 나타나고 새 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
                })

        }
    }

}

컨테이너 ViewController에서 :

extension XYViewController: UITabBarControllerDelegate {

    func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return TabScrollPageAnimationController(tabBarController: tabBarController)
    }

}

3

내 Swift 3 솔루션은 다음과 같습니다.

내 UITabBarViewController의 selectedIndex를 다음과 같이 재정의합니다.

override var selectedIndex: Int{
    get{
        return super.selectedIndex
    }
    set{
        animateToTab(toIndex: newValue)
        super.selectedIndex = newValue
    }
}

그런 다음 네이티브 푸시 / 팝 애니메이션을 모방 한이 함수를 사용합니다.

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
    })
}

나는 그것이 도움이되기를 바랍니다 :)


2

불안전 한 애니메이션 수정 ...

UIView * fromView = self.view.superview;


2

이것은 두 가지 방법으로 해결할 수 있습니다

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){
                    }];
}

이 도움을 바랍니다 ...!


1
내비게이션 컨트롤러 간의 전환을 애니메이션하려면 어떻게해야합니까? tabBarControllerDelegate는 뷰 컨트롤러에서만 작동합니다.
saeppi

나는 둘 다 시도했는데, 첫 번째는 새로운 뷰를 표시하고 이상하게 보이는 애니메이션보다. 두 번째는 아무런 영향을 미치지 않은 것 같습니다. tab2와 관련된보기로 이동하여 viewWillAppear에 코드를 추가하고 테스트했는데 탭간에 눈에 띄는 애니메이션이 없었습니다.
Shannon Cole

기본 Xcode TabBarController 프로젝트로 이것을 시도했습니다. 1이나 2는 운이 없다. 나는 그들이 일하기를 정말로 원했다. :) 내가 뭔가를 놓치고 있습니까?
Andrew Duncan

나도, 운이 없다 .. 어떤 아이디어?
Jon

2

탭한 항목에 따라 애니메이션 할 수 있습니다.이 예에서 탭한 색인이 이전에 선택한 색인보다>이면 flipFromLeft를, 탭한 색인이 이전에 선택한 색인보다 작 으면 flipFromRight를 플립합니다. 이것은 Swift 4 : UITabBarControllerDelegate 메소드 구현

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

    let fromView: UIView = tabBarController.selectedViewController!.view
    let toView: UIView = viewController.view

    if fromView == toView {
        return false
    }

    if let tappedIndex = tabBarController.viewControllers?.index(of: viewController) {
        if tappedIndex > tabBarController.selectedIndex {
            UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, completion: nil)
        } else {
            UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromRight, completion: nil)
        }
    }
    return true
}

이것은 작동하지 않습니다. 내가보기 컨트롤러를 구현했습니다
devedv

@devedv이 솔루션으로 작동하지 않는 것은 무엇입니까? UITabBarControllerDelegate를 ViewController로 설정 했습니까?
Teetz

예, AppDelegate 클래스 AppDelegate에서 UIResponder, UIApplicationDelegate, UITabBarControllerDelegate {}를 수행했습니다. 나는 신속하게 처음입니다 답변 pls의 단계를 자세히 설명해 주시겠습니까?
devedv

@devdev이 AppDelegate에 클래스가 AppDelegate에에서 위의 기능을 넣고 작업해야하는 경우
Teetz


1

drekka의 대답은 정말 좋습니다. 스크롤 전환을 약간 조정하여 애니메이션이 Apple의 푸시 애니메이션처럼 보이도록했습니다. 첫 번째 애니메이션이 완료되면 추가 애니메이션을 추가하여 해당 슬라이딩 효과가 올바르게 보이도록했습니다.

// 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;
                                              }
                                          }];
                 }
             }];

0

버튼을 누를 때 두 개의 자식 뷰 컨트롤러간에 플립 전환을 사용하고 싶었고 다음과 같이 달성했습니다.

-(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 일 수 있습니다.


0

다음 은 탭 간 전환을 애니메이션으로 만드는 작업 코드입니다 ( 3 개의 탭 에 대해 더 이상 시도하지 않았습니다 !!). 주로 drekka의 솔루션을 기반으로하지만 이미 탭바의 델리게이트 메서드에 구현되어 있으므로 복사 / 붙여 넣기 만하면 작업을 수행해야합니다.

-(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;

}


이 코드 스 니펫은 질문을 해결할 수 있지만 설명을 포함하면 게시물의 품질을 개선하는 데 실제로 도움이됩니다. 앞으로 독자를위한 질문에 답하고 있으며, 해당 사용자는 코드 제안 이유를 모를 수 있습니다.
Ferrybig

팁 Ferrybig에 감사드립니다! 나는 코드를 이해하기 쉽도록 최대한 문서화하려고 노력했습니다. 도움이되기를 바랍니다.
Nahuel Roldan

0

이것은 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
}

0

@samwize 답변이 Swift 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
        })
    }

0

@samwize의 답변 이 Swift 5로 업데이트되었습니다.

모든 탭 변경에 애니메이션이 포함되도록하려면 UITabBarControllerDelegate를 사용하고 다음 메서드를 구현합니다.

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
  let tabViewControllers = tabBarController.viewControllers!
  guard let toIndex = tabViewControllers.indexOf(value:viewController) else {
    return false
  }
  animateToTab(toIndex: toIndex, fadeOutFromView: false, fadeInToView: false)
  return true
}

다음을 호출하여 애니메이션으로 탭을 프로그래밍 방식으로 변경합니다 animateToTab.

func animateToTab(toIndex: Int, fadeOutFromView: Bool, fadeInToView: Bool) {
  let tabViewControllers = viewControllers!
  let fromView = selectedViewController!.view
  let toView = tabViewControllers[toIndex].view
  let fromIndex = tabViewControllers.indexOf(value: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.main.bounds.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
  if fadeInToView {
    toView!.alpha = 0.1
  }

  UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [.curveEaseOut], animations: {

    if fadeOutFromView {
      fromView!.alpha = 0.0
    }

    if fadeInToView {
      toView!.alpha = 1.0
    }

    // 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
  })
}

-2

Swift 4+

당신이 UITabBarControllerDelegate방법은 다음과 같이해야한다

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

    animateToTab(toIndex: (tabBarController.viewControllers?.index(of: viewController))!)
    return true
}

그리고 방법은,

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 = UIScreen.main.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.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
    });

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.