在任何 UIViewControllers 之间交替?

Posted

技术标签:

【中文标题】在任何 UIViewControllers 之间交替?【英文标题】:Alternate between ANY UIViewControllers? 【发布时间】:2013-01-20 06:39:13 【问题描述】:

这个问题很长,请耐心等待。

我们知道有两种显示控制器的方法:推送或呈现。还有两个复合控制器:tabbarcontroller 和导航控制器。有了这些可以制作复杂的视图控制器树。 现在我有一个像这样的复杂视图控制器树。

T: 标签栏控制器 N:导航控制器 V:普通视图控制器 P:推 S:在场

所以PV2就是push一个viewcontroller,这是六种组合,但是禁止PN,不能push一个navigation controller。在我研究的时候

T1 --------------------- N1 N2 N3 | | PV1 PV3 | PV2

现在当前的视图控制器是 V2。我想跳到N3并推V3。 我写了这段代码:

[self.navigationcontroller popToRootViewControllerAnimated:NO]; // No is important
tabbarcontroller.selectIndex = 2;
[N3 pushViewController:V3];

它有效,但仍然很糟糕,太糟糕了。 1:N3必须知道v3,viewcontroller之间的耦合性很强。 2:不能在复杂的情况下工作……也会导致动画问题。

我的一个朋友告诉我,他们制作了一个 PageConductor,可以轻松地在任何视图控制器之间切换。 这真的让我很困惑......

【问题讨论】:

【参考方案1】:

    N3 知道 V3 有什么问题?您是说 V2 知道 V3 吗?无论如何,我在了解 T 的 V2 中看到了一个更大的问题。如果您真的必须执行此复杂的屏幕流程,请将其委托给专用对象(例如您朋友提到的 PageConductor)。根据用户操作而不是控制器操作(如[screens showUserProfile:userId] 或其他东西)与此对象交谈,这将为您提供控制器之间的真正解耦。

    有哪些“复杂情况”示例?至于这种特殊情况下的动画问题,请在弹出 T1 堆栈之前尝试切换到 T3。

更新

一个虚构的应用程序的屏幕管理器的小人为示例。

@implementation ScreenManager 


@synthesize tabController = _tabController;

- (BOOL)goToUserList

    // user list is a root controller of tab 0 navigation controller
    BOOL switched = [self switchToTab:0];
    UINavigationController *nc = [self rootNavigationAtTab:0];
    [nc popToRootViewControllerAnimated:!switched]; // we should animate popping if we didn't change tabs
    return switched;


- (BOOL)showUserProfile:(NSUInteger)userId

    BOOL switched = [self goToUserList];
    UIViewController *uc = [[UserDetailsController alloc] initWithUserId:userId];
    UINavigationController *nc = [self rootNavigationAtTab:0];
    [nc pushViewController:uc animated:YES];
    return switched;


- (BOOL)showMapAtLocation:(CLLocation *)location

    MapController *mc = [self downcast:[self rootControllerAtTab:1] to:[MapController class]];
    mc.location = location;
    return [self switchToTab:1];


/* returns if we actually switched tabs as a result of this action */
- (BOOL)switchToTab:(NSUInteger)tabIdx

    NSUInteger prevTabIdx = _tabController.selectedIndex;
    _tabController.selectedIndex = tabIdx;
    return prevTabIdx != tabIdx;


- (BOOL)pushController:(UIViewController *)controller animated:(BOOL)animated toTab:(NSUInteger)tabIdx

    BOOL switchedTabs = [self switchToTab:tabIdx];
    UINavigationController *nc = [self rootNavigationAtTab:tabIdx];
    [nc pushViewController:controller animated:animated];
    return switchedTabs;


- (UINavigationController *)rootNavigationAtTab:(NSUInteger)tabIdx

    return [self downcast:[self rootControllerAtTab:tabIdx] to:[UIViewController class]];


- (id)downcast:(id)obj to:(Class)klass

    return [obj isKindOfClass:klass] ? obj : nil;


- (UINavigationController *)rootControllerAtTab:(NSUInteger)tabIdx

    return [_tabController.viewControllers objectAtIndex:tabIdx];


+ (ScreenManager *)currentManager

    static ScreenManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    manager = [ScreenManager new];
);

    return manager;



@end

这里我们有 3 个操作可供用户使用 - “按 ID 显示用户个人资料”、“显示用户列表”和“在地图上显示某个位置”。人们可能会调整这个基本解决方案以满足他们的应用程序需求,但它的想法是将标签/屏幕管理逻辑与本地控制器逻辑分离。所有操作都返回一个布尔值,指示在此期间是否切换了选项卡,因此客户端代码可以根据此进行一些调整(例如在选项卡更改时弹出其导航堆栈)。

【讨论】:

1.我在N3.m中导入V3只是为了一句话;我使用通知来解耦,所以 V2 不知道 T,pageConductor 是最好的解决方案,但我不知道如何实现它(就像你说的[screens showUserProfile:userId])。 2.复杂情况的例子是V3出现了一个tabbarcontroller T2,我想跳到V3。它会导致很多动画问题,引用:“至于这种特殊情况下的动画问题,请在弹出 T1 堆栈之前尝试切换到 T3。”这不是一个常见的解决方案。 抱歉这么久才回复,工作比较忙。 1. N3是导航,你实现自己的导航控制器吗?无论如何,这不是行数问题,而是应用程序语义问题。 2. 我不确定我是否在关注您:V3 出现在选项卡 2 上,您想切换到它吗?这里有什么复杂的?至于页面指挥 - 请参阅更新。

以上是关于在任何 UIViewControllers 之间交替?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIViewControllers 之间使用委托?

在 UISplitViewController 和 UIViewControllers 之间导航 [重复]

UIScrollview 中的两个 UIviewControllers - 如何在它们之间调用方法

在两个 UiViewControllers 和 UIViews 之间实现拖动和滑动转换? [关闭]

如何在IOS7中实现UIViewControllers之间的前后转换导航?

如何从“菜单”视图控制器在现有 UIViewControllers 之间导航?