使用UINavigationController从右侧向左侧推入ViewController

38

众所周知,UINavigationController将ViewController从左侧推入,有没有办法将View从右侧推入?就像后退按钮的动画一样。
目前我有这个:


[self.navigationController pushViewController:viewController animated:YES];
11个回答

55

您可以从导航控制器的viewControllers数组创建一个NSMutableArray,并在当前视图控制器之前插入新的视图控制器。然后设置viewControllers数组而不使用动画,并弹回。

UIViewController *newVC = ...;
NSMutableArray *vcs =  [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[vcs insertObject:newVC atIndex:[vcs count]-1];
[self.navigationController setViewControllers:vcs animated:NO];
[self.navigationController popViewControllerAnimated:YES];

1
我添加了一个代码片段,说明了这种方法。这绝对不符合HIG的要求,可能会让用户感到困惑。但它应该能够满足您的要求。不过我没有测试过。 - Felix Lamouroux
2
我根据这个想法创建了一个有效的UINavigationController子类:http://bit.ly/iOS-rtl - Tal Bereznitskey
问题在于当我想回到之前的视图时,我需要再次初始化它,这会导致它重新加载并调用viewDidLoad方法,而这不是我想要的。有什么解决办法吗? - ytpm
生成的动画与“从左侧”动画不同,后者是指视图从左侧覆盖当前视图。 - Guy Moreillon
3
太巧妙了。使用CATransition应该是正确的方法。 - user2387149
显示剩余6条评论

37
请尝试这个。
HomePageController  *pageView = [[HomePageController alloc] initWithNibName:@"HomePageController_iPhone" bundle:nil];

CATransition *transition = [CATransition animation];
transition.duration = 0.45;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
transition.delegate = self;
[self.navigationController.view.layer addAnimation:transition forKey:nil];

self.navigationController.navigationBarHidden = NO;
[self.navigationController pushViewController:pageView animated:NO];

1
你为什么要两次设置过渡的类型? - Yarneo
类型应该是kCATransitionPush,子类型为kCATransitionFromLeft。设置类型两次是多余的。我会编辑答案。 - jomafer

9
Try this :  
//Push effect in reverse way
    CATransition* transition = [CATransition animation];
    transition.duration = 0.75;
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromLeft;
    [self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
    [self.navigationController pushViewController:vc animated:NO];

6
这段代码运行良好,请尝试。
CATransition *transition = [CATransition animation];
transition.duration = 0.3f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionReveal;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController pushViewController:phoneServicesViewController animated:NO];

终于!我已经尝试了几个小时,但都没有成功,直到看到你的代码。 - Trianna Brannon
@TriannaBrannon 很高兴听到代码片段对您有所帮助。 - Yousuf Qureshi

2
基于@felixlam的回答,我进行了升级并创建了一个“反向”导航控制器,覆盖了默认的推送/弹出方法,使其操作如下。
class LeftPushNavigationController: BaseNavigationController {

  override func viewDidLoad() {
    super.viewDidLoad()
    interactivePopGestureRecognizer?.isEnabled = false
  }

  override func pushViewController(_ viewController: UIViewController, animated: Bool) {
    // If the push is not animated, simply pass to parent
    guard animated else { return super.pushViewController(viewController, animated: animated) }

    // Setup back button
    do {
      // Hide original back button
      viewController.navigationItem.setHidesBackButton(true, animated: false)
      // Add new one
      viewController.navigationItem.rightBarButtonItem = UIBarButtonItem(
        image: #imageLiteral(resourceName: "iconBackReverse"),
        style: .plain,
        target: self,
        action: #selector(self.pop)
      )
    }

    // Calculate final order
    let finalVCs = self.viewControllers + [viewController]

    // Insert the viewController to the before-last position (so it can be popped to)
    var viewControllers = self.viewControllers
    viewControllers.insert(viewController, at: viewControllers.count - 1)

    // Insert viewcontroller before the last one without animation
    super.setViewControllers(viewControllers, animated: false)
    // Pop with the animation
    super.popViewController(animated: animated)
    // Set the right order without animation
    super.setViewControllers(finalVCs, animated: false)
  }

  override func popViewController(animated: Bool) -> UIViewController? {
    // If the push is not animated, simply pass to parent
    guard animated else { return super.popViewController(animated: animated) }

    guard self.viewControllers.count > 1 else { return nil }

    // Calculate final order
    var finalVCs = self.viewControllers
    let viewController = finalVCs.removeLast()

    // Remove the parent ViewController (so it can be pushed)
    var viewControllers = self.viewControllers
    let parentVC = viewControllers.remove(at: viewControllers.count - 2)

    // Set the viewcontrollers without the parent & without animation
    super.setViewControllers(viewControllers, animated: false)
    // Create push animation with the parent
    super.pushViewController(parentVC, animated: animated)
    // Set the right final order without animation
    super.setViewControllers(finalVCs, animated: false)

    // Return removed viewController
    return viewController
  }

  @objc
  private func pop() {
    _ = popViewController(animated: true)
  }

}

2
不,从右到左的效果是保留给从导航栏中弹出视图控制器的。但是你可以通过自己动画来实现这种效果。像这样:
[UIView beginAnimations:@"rightToLeft" context:NULL];
CGRect newFrame = aView.frame;
newFrame.origin.x -= newFrame.size.width;
aView.frame = newFrame;
[UIView commitAnimations];

然而,这不会对您的导航控制器产生任何影响。


需要知道的内容,以及如果我从导航堆栈中弹出视图应该怎么做? - ludo
@Ludo:你不能从导航栈中弹出视图。 "stack" 包含视图控制器。 - Felix Lamouroux
对不起,我的错。我输入了views,但是当然是想到了viewcontrollers。如何弹出一个viewcontroller在其他答案中已经解释过了。 - Johan Kool

2

Swift 5.0

 let pageView = TaskFolderListViewController.init(nibName: "HomePageController_iPhone", bundle: nil)
 let transition = CATransition()
 transition.duration = 0.45
 transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
 transition.type = CATransitionType.push
 transition.subtype = CATransitionSubtype.fromLeft
 self.navigationController?.view.layer.add(transition, forKey: kCATransition)
 self.navigationController?.pushViewController(pageView, animated: false)

2

您似乎想要“弹回”。您可以通过以下三种方法之一在UINavigationController实例上实现:

返回“根”控制器:

-(NSArray *)popToRootViewControllerAnimated:(BOOL)animated

或者返回先前的一个控制器:

-(NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated

或者返回之前推送的控制器:

-(UIViewController *)popViewControllerAnimated:(BOOL)animated


谢谢你的帮助,我只是想尝试一下Felix的答案,但不太清楚如何操作。 - ludo

2

我认为这里最佳答案的改进如下:

UIViewController *newVC = ...;
[self.navigationController setViewControllers:@[newVC, self] animated:NO];
[self.navigationController popViewControllerAnimated:YES];

要回到初始控制器,只需对其进行推送。


0

我曾经遇到过同样的问题,这是我解决它的方法

[self.navigationController popViewControllerAnimated:YES];

这将类似于“返回”,即点击后退按钮并导航到上一页的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接