繁体   English   中英

替换 UINavigationController 层次结构中的 UIViewController

[英]Replace a UIViewController in the UINavigationController hierarchy

我有一个 iPhone 应用程序,它使用UINavigationController的标准实现来进行应用程序导航。

我试图找出一种方法来替换层次结构中的视图控制器。 换句话说,我的应用程序加载到rootViewController ,当用户按下按钮时,应用程序会推送到firstViewController 然后用户按下另一个按钮将应用程序导航到secondViewController 再次使用向下导航到另一个视图控制器,thirdViewController。 但是,我希望第三个thirdViewController的BackButton 弹回firstViewController。

本质上,当用户推送到thirdViewController ,我希望它替换导航层次结构中的secondViewController

这可能吗? 我知道它正在使用 Three20,但我不是在这种情况下。 尽管如此,如果可以在 Three20 中实现,那么它当然应该使用直接的 SDK 调用。 有人有想法吗?

干杯,布雷特

很简单,当即将推,而不是做一个简单的thirdViewController pushViewController做到这一点:

NSArray * viewControllers = [self.navigationController viewControllers];
NSArray * newViewControllers = [NSArray arrayWithObjects:[viewControllers objectAtIndex:0], [viewControllers objectAtIndex:1], thirdController,nil];
[self.navigationController setViewControllers:newViewControllers];

其中[viewControllers objectAtIndex:0][viewControllers objectAtIndex:1]是您的 rootViewController 和您的 FirstViewController。

NSMutableArray *viewController = [NSMutableArray arrayWithArray:[navController viewControllers]];
[viewController replaceObjectAtIndex:1 withObject:replacementController];
[navController setViewControllers:viewController];

有关更多信息,请参阅UINavigationController 类参考

Swift 中更简洁的方法应该是:

extension UINavigationController {
  func replaceTopViewController(with viewController: UIViewController, animated: Bool) {
    var vcs = viewControllers
    vcs[vcs.count - 1] = viewController
    setViewControllers(vcs, animated: animated)
  }
}

如果您只是替换导航控制器视图控制器数组中的viewController ,则无法为过渡设置动画。 我建议在第三个视图控制器的viewWillAppear方法中执行以下操作。

-(void) viewWillAppear:(BOOL)animated
{
   NSArray *vCs=[[self navigationController] viewControllers];
   NSMutableArray *nvCs=nil;
   //remove the view controller before the current view controller
   nvCs=[[NSMutableArray alloc]initWithArray:vCs];
   [nvCs removeObjectAtIndex:([nvCs count]-2)];
   [[self navigationController] setViewControllers:nvCs];
   [super viewWillAppear:animated];
}

斯威夫特 4 版本:

if var viewControllers = navigationController?.viewControllers {
    viewControllers[viewControllers.count - 1] = newViewController
    navigationController?.viewControllers = viewControllers
}

由于您只是尝试从 ViewControllers 堆栈中弹出两次,因此您可以通过调用获得相同的结果

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

从第三个thirdViewController的后退按钮

- (void)swapTopViewController:(UIViewController *)topViewController{
    NSArray *viewControllers = [self.navigationController viewControllers];
    NSMutableArray *editableViewControllers = [NSMutableArray arrayWithArray:viewControllers];
    [editableViewControllers removeLastObject];
    [editableViewControllers addObject:topViewController];
    [self.navigationController setViewControllers:editableViewControllers];
}
extension UINavigationController {

    func setTop(viewController: UIViewController) {
        viewControllers = [viewController]
    }

}

Swift 5 UINavigationController 扩展,在替换时也支持Fade动画。

extension UINavigationController {
    func find(of type: UIViewController.Type) -> UIViewController? {
        for controller in self.viewControllers {
            if controller.isKind(of: type) {
                return controller
            }
        }
        return nil
    }
    
    func go(to controller: UIViewController, animated: Bool = true) {
        if let old = self.find(of: type(of: controller)) {
            self.popToViewController(old, animated: animated)
        }
        else {
            self.pushViewController(controller, animated: animated)
        }
    }
    
    fileprivate func prepareFade() {
        let transition: CATransition = CATransition()
        transition.duration = 0.3
        transition.type = CATransitionType.fade
        view.layer.add(transition, forKey: nil)
    }
    
    func replace(to controller: UIViewController, animated: Bool = true, fade: Bool = true) {
        guard let current = self.topViewController else {
            return
        }
        
        var controllers = self.viewControllers
        controllers.remove(current)
        controllers.append(controller)
        
        if fade {
            prepareFade()
            self.setViewControllers(controllers, animated: false)
        }
        else {
            self.setViewControllers(controllers, animated: animated)
        }
    }
}

extension Array where Element: Equatable {
    mutating func remove(_ element: Element) {
        if let index = self.firstIndex(of: element) {
            self.remove(at: index)
        }
    }
}

通常,您需要将堆栈清除到您的“基础”,然后将新的堆栈放在:

来自@duan 的这个答案是完美的,它会用一个新项目替换最上面的项目:

extension UINavigationController {
    func replaceTopViewController(with vc: UIViewController, animated: Bool) {
        var vcs = viewControllers
        vcs[vcs.count - 1] = vc // NB watch for bizarre zero size
        setViewControllers(vcs, animated: animated)
    }
}

然而,很多时候,你会有一个“Base”控制器,你希望它一直在那里。

如果您想将所有内容安全地清除到 Base 并更改为新的(没有在 Base 中运行 viewWillAppear),则:

extension UINavigationController {
    func popToRoot(andPushOnly: UIViewController) {
        var vcs = viewControllers
        while vcs.count > 1 { vcs.removeLast() }
        if vcs.count < 1 { return }
        vcs.append(andPushOnly)
        setViewControllers(vcs, animated: false)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM