Reputation: 445
This question is LONG, so please be patient.
As we know there are two ways to show a controller: push or present. there are also two composite controllers: tabbarcontroller & navigation controller. with these can make complicated viewcontrollers tree. Now i have a complicated viewcontroller tree like this.
T: tabbarcontroller N: navigationcontroller V: normal viewcontroller P: push S: present
So PV2 means push a viewcontroller , this are six combinations ,but PN is forbiddened, you cannot push a navigation controller. and as i research
T1 --------------------- N1 N2 N3 | | PV1 PV3 | PV2
now the current view controller is V2. i wanna to jump to N3 and push a V3. i wrote this code:
[self.navigationcontroller popToRootViewControllerAnimated:NO]; // No is important
tabbarcontroller.selectIndex = 2;
[N3 pushViewController:V3];
it works, but still bad, too bad. 1: N3 must know v3, the coupling between viewcontrollers is strong. 2: it cannot work in complicated situations…also cause animation problems.
A friend of mine told me they made a PageConductor that can alternate between ANY viewcontrollers easyily. that really confused me...
Upvotes: 0
Views: 76
Reputation: 4428
What's wrong with N3 knowing about V3? Did you mean V2 knowing about V3? Anyway I see a much bigger problem in V2 knowing about T. If you really have to do this complex screen flow delegate it to a dedicated object (like PageConductor your friend mentioned). Talk to this object in terms of user actions and not controller manipulations (like [screens showUserProfile:userId]
or something), this will give you a real decoupling between controllers.
What are the 'complicated situations' examples? As for animation problems in this particular case try to switch to T3 before popping T1 stack.
Update
A small contrived example of screen manager of some imaginary app.
@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
Here we have 3 actions available to user - "Show user profile by ID", "Show user list" and "Show some location on the map". One may tweak this basic solution to meet their application requirements but the idea it to decouple tab/screen management logic from local controller logic. All actions return a boolean indicating if tabs was switched during it so client code can make some tweaks depending on this (like popping its navigation stack in case tab was changed).
Upvotes: 1