iPhone: จะสลับแท็บด้วยภาพเคลื่อนไหวได้อย่างไร?


106

UITabBarController.selectedIndexฉันเปลี่ยนแท็บโปรแกรมในการประยุกต์ใช้บาร์แท็บขับเคลื่อนโดยใช้ ปัญหาที่ฉันกำลังพยายามแก้ไขคือวิธีทำให้การเปลี่ยนแปลงระหว่างมุมมองเป็นภาพเคลื่อนไหว กล่าวคือ. จากมุมมองของแท็บปัจจุบันไปยังมุมมองของแท็บที่เลือก

ความคิดแรกคือการใช้ประโยชน์จากสิ่งUITabBarControllerDelegateนี้ แต่ดูเหมือนว่าจะไม่ถูกเรียกเมื่อสลับแท็บโดยใช้โปรแกรม ตอนนี้ฉันกำลังพิจารณาUITabBarDelegate.didSelectItem: เป็นตะขอที่เป็นไปได้ในการตั้งค่าภาพเคลื่อนไหวการเปลี่ยนแปลง

มีใครจัดการทำให้การเปลี่ยนภาพเคลื่อนไหวได้บ้าง? ถ้าใช่อย่างไร


1
FWIW หลายเหล่านี้สูงได้รับการโหวตคำตอบก่อนวันจริงในการเปลี่ยนกำหนดเองที่ระบุไว้ในคำตอบ Runo ของและHeberti ของเกินไป นี่คือวิธีที่เหมาะสมในการจัดการกับภาพเคลื่อนไหวที่กำหนดเองเหล่านี้ ดู WWDC 2013 วิดีโอการเปลี่ยนกำหนดเองการใช้ดูตัวควบคุม
Rob

คำตอบ:


154

Update 04/2016: 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

อืมไม่ได้เกิดขึ้นกับรหัสของฉัน ฟังดูเหมือนเป็นปัญหาที่ฉันเคยเห็นมาก่อนซึ่งการวางตำแหน่งของกรอบมุมมองใหม่ไม่ถูกต้องโดยสัมพันธ์กับหน้าต่างและแถบสถานะ ลองรันโค้ด toe เพื่อสลับมุมมองโดยไม่ต้องทำการเปลี่ยนแปลงและดูว่ายังคงเกิดขึ้นหรือไม่
drekka

ใช่มันยังคงเกิดขึ้นโดยไม่ต้องทำการเปลี่ยนแปลง ฉันลองวิธีแรกแล้ว อาจเป็นการวางตำแหน่งของเฟรมฉันจะเล่นกับมันอีกเล็กน้อย ฉันลองขยับเฟรมขึ้นและลองจับคู่เฟรมกับ fromView แล้ว แต่ยังไม่มีโชคเลย ...
Enrico Susatyo

2
@EmileCormier ใส่ไว้ในshouldSelectViewControllerวิธีการของผู้แทน TabBar และส่งคืน NO ที่นั่น
cheesus

2
@drekka นี่ใช้ไม่ได้สำหรับฉัน อธิบายได้ไหมว่า ControllerIndex มาจากไหน แล้วทำไมคุณไม่ใช้ [viewController view] จากเมธอด tabBarControllerDelegate สำหรับ 'toView' ล่ะ? Thnaks
shannoga

25

ต่อไปนี้คือความพยายามของฉันที่จะใช้รูปแบบรหัส drekka ในวิธีการมอบหมาย (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

2
คุณสามารถตั้งค่าผู้รับมอบสิทธิ์เป็นไฟล์การใช้งาน UITabController ของคุณได้โดยการเพิ่ม self.delegate = self; ในฟังก์ชัน viewDidLoad () ของคุณ ซึ่งจะทำให้สามารถเรียกใช้ฟังก์ชันข้างต้นได้
Chris Fremgen

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
และอย่าลืมเชื่อมต่อ Delegate ของคุณเข้ากับเต้ารับตัวแทนของ TabViewController ทำงานได้อย่างสวยงาม น้ำยาที่นี่สะอาดที่สุด
Andrew Duncan

สามารถทำได้ผ่านสตอรี่บอร์ดและตอนนี้ฉันกำลังดูฟีเจอร์นี้ใน IOS 10.x หรือไม่
mobibob

17

แทนที่จะใช้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

ฉันคิดว่าคุณสามารถบรรลุการเปลี่ยนสำหรับ UITabBarControlelr ได้อย่างง่ายดายโดยใช้ CATransition นอกจากนี้ยังจะแก้ปัญหาผลข้างเคียงของการใช้ transitionFromView: toView:

ใช้สิ่งนี้ภายในคลาส TabBarController แบบกำหนดเองของคุณที่ขยายจาก UITabBarController

- (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
ฉันคิดว่าเราสามารถใช้ "shouldSelectViewController" แทน "didSelectViewController"
Ryan Wu

13

ฉันเขียนโพสต์หลังจากลองตอบคำถามต่างๆที่นี่

รหัสที่อยู่ในสวิฟท์และคุณ programatically 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 - ข้อผิดพลาดที่ส่งกลับจากวิธีการค้นหาเช่น Downcast จาก '[UIViewController]? ถึง '[UIViewController]' จะแกะตัวเลือกเท่านั้น คุณตั้งใจจะใช้ "!" หรือไม่?
lozflan

เกือบจะดียกเว้นฉันพบข้อผิดพลาดที่จะไม่เคลื่อนไหว ( finishedจะเป็นเท็จ) ฉันไม่รู้ว่าทำไมถึงเกิดขึ้น แต่ฉันคิดว่ามันเกี่ยวข้องกับการเปลี่ยนแปลงของ CA ซึ่งคิดว่าไม่มีอะไรให้เคลื่อนไหวได้ ฉันเปลี่ยนไปใช้ภาพเคลื่อนไหวด้วยเฟรมและก็ใช้ได้ผล
samwize

3

ฉันใช้โซลูชันของ @ Mofumofu และอัปเกรดเป็น Swift 1.2 และใช้ภาพเคลื่อนไหวขึ้น / ลง ความหมาย 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)
    }

}

3

นี่คือโซลูชัน Swift 3 ของฉัน:

ฉันแทนที่ selectedIndex ของ UITabBarViewController ของฉันเช่นนี้:

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 ของคุณหนึ่งครั้ง อย่าลืมรวม UITabBarControllerDelegate โดยใช้ <> หลังเครื่องหมายจุดคู่ (:) ใน AppDelegate.h ของคุณ

-(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 ใช้งานได้กับ View Controllers เท่านั้น
saeppi

ฉันลองทั้งสองอย่างอันแรกแสดงมุมมองใหม่และเป็นภาพเคลื่อนไหวซึ่งดูแปลก ๆ อันที่ 2 ดูเหมือนจะไม่ส่งผลใด ๆ ฉันเข้าไปในมุมมองที่เชื่อมโยงกับ tab2 และเพิ่มโค้ดลงใน viewWillAppear และทดสอบและไม่มีภาพเคลื่อนไหวที่มองเห็นได้ระหว่างแท็บ
Shannon Cole

ลองใช้อันนี้กับโปรเจ็กต์ Xcode TabBarController เริ่มต้น ไม่มีโชคกับ 1 หรือ 2 ฉันอยากให้พวกเขาทำงานจริงๆ :) ฉันขาดอะไรไปหรือเปล่า?
Andrew Duncan

ฉันก็เช่นกันไม่มีโชค .. ความคิดใด ๆ ?
จอน

2

คุณสามารถเคลื่อนไหวได้โดยขึ้นอยู่กับรายการที่แตะ - ในตัวอย่างนี้เราจะพลิก FromLeft หากดัชนีการแตะ> กว่าดัชนีที่เลือกก่อนหน้านี้และเราจะพลิกกลับจากขวาหากดัชนีที่แตะเป็น <กว่าดัชนีที่เลือกก่อนหน้านี้ นี่คือ 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
}

สิ่งนี้ไม่ทำงาน ฉันได้ติดตั้งใน view controller แล้ว
devedv

@devedv อะไรที่ใช้ไม่ได้กับโซลูชันนี้ คุณตั้งค่า UITabBarControllerDelegate เป็น ViewController ของคุณหรือไม่?
Teetz

ใช่ฉันทำสิ่งต่อไปนี้ในคลาส AppDelegate AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate {} ฉันยังใหม่กับความรวดเร็วคุณสามารถอธิบายขั้นตอนในคำตอบของคุณอย่างละเอียดได้ไหม
devedv

@devdev ถ้านี่คือคลาส AppDelegate ของคุณให้วางฟังก์ชันไว้ข้างบนใน AppDelegate ของคุณและควรใช้งานได้
Teetz

1
ขอให้เรายังคงอภิปรายนี้ในการแชท
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 ซึ่งสามารถตั้งค่าได้อย่างง่ายดายใน app delegate


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.