这个问题很长,请耐心等待。
我们知道有两种显示控制器的方法:推送或呈现。还有两个复合控制器:tabbarcontroller&导航控制器。用这些可以制作复杂的viewcontrollers树。 现在我有一个像这样的复杂的viewcontroller树。
T:tabbarcontroller N:navigationcontroller V:普通的viewcontroller P:推 S:现在
所以PV2意味着推送一个viewcontroller,这是六种组合,但是PN是禁止的,你不能推动导航控制器。并且正如我研究
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,viewcontrollers之间的耦合很强。 2:它不能在复杂的情况下工作......也会引起动画问题。
我的一位朋友告诉我他们制作了一个PageConductor,可以轻松地在任何一个视图控制器之间切换。 那让我很困惑......
答案 0 :(得分:1)
N3知道V3有什么问题?你的意思是V2了解V3吗?无论如何,我在V2中看到一个更大的问题,知道T。如果你真的必须这样做,那么复杂的屏幕流程会将它委托给一个专用对象(比如你朋友提到的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显示用户个人资料”,“显示用户列表”和“在地图上显示某个位置”。人们可以调整这个基本的解决方案,以满足他们的应用程序要求,但想法将选项卡/屏幕管理逻辑与本地控制器逻辑分离。所有操作都返回一个布尔值,指示选项卡是否在其中切换,因此客户端代码可以根据此进行一些调整(例如在更改的情况下弹出其导航堆栈)。