0

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

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

T:tabbarcontroller N:navigationcontroller V:普通viewcontroller P:push S:present

所以 PV2 表示 push a viewcontroller ,这是六种组合,但是 PN 是被禁止的,你不能 push a 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,可以轻松地在任何视图控制器之间切换。这真的让我很困惑......

4

1 回答 1

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

  2. 什么是“复杂情况”的例子?至于这种特殊情况下的动画问题,请在弹出 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 显示用户个人资料”、“显示用户列表”和“在地图上显示某些位置”。人们可能会调整这个基本解决方案以满足他们的应用程序需求,但它的想法是将标签/屏幕管理逻辑与本地控制器逻辑分离。所有操作都返回一个布尔值,指示在此期间是否切换了选项卡,因此客户端代码可以根据此进行一些调整(例如在选项卡更改时弹出其导航堆栈)。

于 2013-01-20T08:25:05.493 回答