如何以编程方式单击UITabBarItem时将ViewController推送到NavigationController?

时间:2016-04-05 05:22:20

标签: ios swift

首先对不起,如果标题有点令人困惑,我会尝试更好地解释它。

我不在我的应用程序中使用StoryBoard,所以一切都是以编程方式完成的。

所以我有一个UITabBarController,这是rootViewController中定义的应用的AppDelegate

 window?.rootViewController = CustomTabBarController()

如您所见,自定义TabBar在CustomTabBar类中定义如下:

class CustomTabBarController: UITabBarController {

override func viewDidLoad() {

    super.viewDidLoad()

    let mainController = MainViewController()
    let mainNavigationController = UINavigationController(rootViewController: mainController)
    mainNavigationController.title = "Home"
    mainNavigationController.tabBarItem.image = UIImage(named: "icon_home")

    let searchController = SearchViewController()
    let searchNavigationController = UINavigationController(rootViewController: searchController)
    searchNavigationController.title = "Search"
    searchNavigationController.tabBarItem.image = UIImage(named: "icon_search")

    // I have four items in the TabBar, all defined the same way as  above.
    //I removed them to keep the code a bit shorter.

    viewControllers = [mainNavigationController, cameraNavigationController, searchNavigationController, profileNavigationController]
      }
     }

TabBar基本上如下所示:

enter image description here

因此,当我点击TabItem时,特定TabItem的NavigationController会显示其rootViewController;这里的一切都很好。

但是,相机的根视图控制器是全屏的,这意味着TabBar对用户不可见。我在视图上添加了一个关闭按钮,以便用户可以“退出”视图。问题是:由于每个项目都有自己的导航控制器,当选择摄像机项目时,CameraViewController是NavigationController堆栈中的第一个,因此,如果用户点击关闭,我无法弹出它按钮。

我设法用answer找到了解决方法。

//close button clicked
self.tabBarController.selectedIndex = 0;

它可以工作,但这样,用户每次都被“重定向”到同一个标签页。

我想要做的是将用户“重定向”到他所在的上一个标签。例如,假设他在“搜索”中,然后他点击了相机TabItem,如果他决定关闭,他将回到“搜索”部分。

解决方案是拥有一个存储实际ViewController关联位置的变量,因此当单击按钮关闭时,我只需将选定的索引设置为此变量。但是,我认为这不是很优雅。

另一个是只有三个NavigationController(删除相机的一个),只需按实际NavigationController推送/弹出CameraViewController(根据用户的部分而改变) 。这个解决方案似乎更好,但我找不到让它工作的方法。 我试图实现函数didSelectViewController并检查它是否等于cameraViewController,(如果为true)只需将其推送到实际的NavigationController。当我尝试执行此操作时,它不起作用,因为实际的NavigationController是“nil”。

我的问题是:执行任务的最佳方式是什么?

感谢您抽出宝贵时间,感谢抱歉。

编辑2:

所以我尝试了Joe Benton提出的解决方案,这与我收到的评论类似。

我的想法是用shouldSelectViewController模态显示相机屏幕,他的代码在Objective-C中,所以这里是Swift 2版本:

func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
    if viewController == cameraViewController {
        self.presentViewController(cameraViewController, animated: true, completion: nil)
        return false
    }
    return true
    }
 }

如您所见,我直接呈现ViewController而不是NavigationController,但这是一个细节。

不要忘记继承UITabBarControllerDelegate,如此:

class CustomTabBarController: UITabBarController, UITabBarControllerDelegate 

并将委托设置为自我:

self.delegate = self

但是,此解决方案不起作用,如果您尝试此代码并选择CameraTab,您将收到以下错误:

Application tried to present modally an active controller UITabBarController

在线搜索后,我似乎无法以ViewController的形式呈现TabBarController,这意味着您无法将presentViewController用于viewController 1}}以前用这种方式定义:

viewControllers = [mainNavigationController, cameraViewController, searchNavigationController, profileNavigationController]

您可以详细了解here

所以我做的是在类的顶部实例化两个CameraViewController(所以我可以随意访问它们):

class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {

    let cameraControllerTest = CameraViewController()
    let cameraViewController = CameraViewController()

    override func viewDidLoad() {...}
    ...
}

然后,我在ViewControllers数组中添加了其中一个,以便它出现在UITabBarController

我无所谓。

之后,在shouldSelectViewController函数中,您只需检查点击了哪个标签,如果它是您CameraViewController数组中TabBar的实例,则只需显示不在数组中的其他实例,否则,您只返回true。它看起来像这样:

func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
        if viewController == cameraViewControllerInArray {
            self.presentViewController(cameraViewControllerNotInArray, animated: true, completion: nil)
            return false
        }
        return true
        }

我不知道这是否是解决此错误的最佳方式,所以请告诉我。谢谢大家的答案!

2 个答案:

答案 0 :(得分:3)

您可以采用不同的方法来显示相机屏幕。您可以以模态方式显示此内容,这意味着它将从标签栏的底部向上滑动。您可以在每次要更改索引时运行的委托方法shouldSelectViewController中实现此目的。如果它是相机,那么你呈现你的VC,但返回NO,所以它不会改变标签索引:

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
    if(viewController == cameraNavigationController) {
        [self presentViewController:cameraNavigationController animated:YES completion:nil];
        return NO;
    }
    return YES;
}

然后,您可以创建关闭按钮以将视图控制器关闭回视图:

[self dismissViewControllerAnimated:YES completion:nil];

答案 1 :(得分:1)

我认为您的概念性UI模型正是导致问题的原因。实际上,选项卡控制器上的所有选项卡通常都在那里。单击选项卡时,您将用户切换到选项卡式体验。用户实际上并不想离开(根据正常的UI行为)体验,直到他们点击不同的标签。

但是你有效地对用户说:“点击相机标签开始相机体验,我将决定你关闭相机体验时的去向。”然后在这篇文章中,你有点说,“但是我觉得将用户重定向到相同的标签,或者从你到达相机体验的标签页面感觉不够优雅。”我会说你的感官告诉你一些事情是不对的(我同意他们的意见)。

如果您要实施决定用户无法保持相机体验的模型,直到他们自己切换出来,那么您必须让自己的情绪满意。而且你必须决定他们关闭相机时的去向。真的只有几个选择 - 总是回到某些标签(呃);回到他们之前的选项卡,将相机选项卡视为模态体验(与整个选项卡模型不一致,但优于固定的返回选项卡);或者为了说明一个观点,将用户重定向到一些随机标签(双重)。

我认为如果相机标签的行为类似于标签,则需要分两个步骤。切换到摄像头选项卡,然后从摄像头选项卡以模态方式显示全屏摄像头,当他们关闭全屏时返回摄像头选项卡。

否则,我会说放弃标签控制器,然后将按钮放在下方,这样您就可以通过按钮全屏显示相机体验。