Controller design coordination template in Cocoa Touch

I am building an iOS application with a lot of custom views, so using Cocoa standard views was not an option. Then I decided to go with the Coordinator / Mediator design pattern (I found out in Apress - Pro Objective-C Design Patterns for iOS).

From the delegate, I create a rootViewController pointing to the view in my coordination controller:

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; coordinatingController = [C6CoordinatingController sharedInstance]; self.window.rootViewController = coordinatingController.activeVC; [self.window makeKeyAndVisible]; return YES; 

Then, in the coordination controller, I have methods for creating singleton elements:

 + (C6CoordinatingController *) sharedInstance{ if (sharedCoordinator == nil){ C6Log(@"New Shared Coordinator"); sharedCoordinator = [[super allocWithZone:NULL] init]; [sharedCoordinator initialize]; } else { C6Log(@"Return Singleton Shared Coordinator"); } return sharedCoordinator; } + (id) allocWithZone:(NSZone *)zone{ return [self sharedInstance]; } - (void) initialize{ C6Log(@""); [self checkDevice]; _mainVC = [C6MainViewController initWithDevice:device]; _activeVC = _mainVC; [self checkLanguage]; [self chooseFirstView]; } 

I also have a selector to select the first view (currently I have only two):

 -(void) chooseFirstView{ // If a language was not setted, go to language settings view if (!language) { C6Log(@"Going to Language Settings"); C6LanguageSettingsViewController *languageVC = [C6LanguageSettingsViewController initWithDevice:device]; [_mainVC.view addSubview:languageVC.view]; } else { C6Log(@"Going to User Settings", language); C6AccountSettingsViewController *accountVC = [C6AccountSettingsViewController initWithDevice:device]; [_mainVC.view addSubview:accountVC.view]; } } 

Then I have an IBAction that will be used by my views:

 - (IBAction) requestViewChangeByObject:(id)object { int buttonTag = [object tag]; // dividend int viewTag = buttonTag / divisor; // quotient int actionTag = buttonTag - (divisor * viewTag); // remainder C6Log(@"viewTag: %d | actionTag %d", viewTag, actionTag); switch (viewTag) { case LanguageTags: C6Log(@"LanguageTags"); break; case AccountTags: C6Log(@"AccountTags"); break; default: break; } 

In NIB, I created Obect (coordination controller) and I call IBAction from there. It works very well, and I can change my mind (it still needs to be implemented) .........

BUT ... I also want to change the language, but since this is not a navigation issue, I want to do this from the C6LanguageSettingsViewController, not from the C6CoodinatingController.

So, I created another IBAction in the C6LanguageSettingsViewController:

 - (IBAction)chooseLang:(id)sender{ UIImage *bt; [self resetImagesToNormalState]; C6Log(@""); C6Log(@"%@", [sender tag]); C6Log(@"%@", sender); . . . 

When I connect this button to this IBAction (via File Owner OR through the LanguageSettingsViewController object), the application crashes, and once it shows no errors, and sometimes it represents an Unrecognized selector sent to the instance OR EXC_BAD_ACCESS (code = 1, address = 0x. ........) in the UIApplicationMain.

I believe the problem is that the NIB does not find the owner of the file ... but I'm not sure how to solve it.

+4
source share
1 answer

Okay ... this seems absurd, but I answer: P

I managed to get it working (finaly!), And I found that I was controlling viewControllers using the BAAAAD method, so I changed the code in the coordination controller:

Firstly, I have no "real" mainViewController with NIB and stuf no more ...

OLD initialize

 - (void) initialize{ C6Log(@""); [self checkDevice]; _mainVC = [C6MainViewController initWithDevice:device]; _activeVC = _mainVC; [self checkLanguage]; [self chooseFirstView]; } 

NEW initialize

 - (void) initialize{ C6Log(@""); [self checkDevice]; [self checkLanguage]; [self chooseFirstView]; } 

checkDevice checks if it is an iPhone or iPad, so I can choose the right NIB.

checkLanguage checks [NSUserDefaults standardUserDefaults] for the language

Finally, I call selectFirstView:

OLD selectFirstView

 -(void) chooseFirstView{ // If a language was not setted, go to language settings view if (!language) { C6Log(@"Going to Language Settings"); C6LanguageSettingsViewController *languageVC = [C6LanguageSettingsViewController initWithDevice:device]; [_mainVC.view addSubview:languageVC.view]; } else { C6Log(@"Going to User Settings", language); C6AccountSettingsViewController *accountVC = [C6AccountSettingsViewController initWithDevice:device]; [_mainVC.view addSubview:accountVC.view]; } } 

NEW selectFirstView

 -(void) chooseFirstView{ // If a language was not setted, go to language settings view _activeVC = [[UIViewController alloc] init]; UIImage *bgImage = [UIImage imageNamed:@"bg.png"]; UIImageView *bgView = [[UIImageView alloc] initWithImage:bgImage]; [_activeVC.view addSubview:bgView]; if (!language) { C6Log(@"Going to Language Settings"); languageVC = [C6LanguageSettingsViewController initWithDevice:device]; [_activeVC.view addSubview:languageVC.view]; } else { C6Log(@"Going to User Settings", language); accountVC = [C6AccountSettingsViewController initWithDevice:device]; [_activeVC.view addSubview:accountVC.view]; } } 

The big change is WHEN and HOW I initiated _activeVC ... And the fact that both _languageVC and _accountVC are now global variables.

Well, after this change, the NIB button calls both IBAction methods: this is the file owner and coordination controller.

Another BIG thing about using this type of template is switching from one view to another without exploding the iOS device’s memory ... here's how I do it inside the coordination controller:

 - (IBAction) requestViewChangeByObject:(id)object { int buttonTag = [object tag]; // dividend int viewTag = buttonTag / divisor; // quotient int actionTag = buttonTag - (divisor * viewTag); // remainder C6Log(@"ViewTag: %d", viewTag); switch (viewTag) { case LanguageTags:{ C6Log(@"LanguageTags - button %d", actionTag); accountVC = [C6AccountSettingsViewController initWithDevice:device]; UIView *fromView = languageVC.view; UIView *toView = accountVC.view; [self switchFrom:fromView To:toView usingAnimation:AnimationPushFromRigh]; } break; case AccountTags:{ C6Log(@"AccountTags - button %d", actionTag); switch (actionTag) { case 0:{ C6Log(@"Go back"); languageVC = [C6LanguageSettingsViewController initWithDevice:device]; UIView *fromView = accountVC.view; UIView *toView = languageVC.view; [self switchFrom:fromView To:toView usingAnimation:AnimationPushFromLeft]; } break; default: break; } } break; default: break; } } 

At the beginning of the method, I do a lot of mathematics ... I "created" a template in which each NIB should have its tags starting at 100 times ... so that the language starts at 0, the score is from 100 ........ .

 #define divisor 100 #define LanguageTags 0 #define AccountTags 1 

Then it changes the view that I pass from one view to another:

 -(void) switchFrom:(UIView*) fromView To:(UIView*) toView usingAnimation:(int) animation{ C6Log(@""); /*************** SET ALL DEFAULT TRANSITION SETTINGS ***************/ // Get the current view frame, width and height CGRect pageFrame = fromView.frame; CGFloat pageWidth = pageFrame.size.width; // Create the animation [UIView beginAnimations:nil context:nil]; // Create the delegate, so the "fromView" is removed after the transition [UIView setAnimationDelegate: fromView]; [UIView setAnimationDidStopSelector:@selector(removeFromSuperview)]; // Set the transition duration [UIView setAnimationDuration: 0.4]; // Add the "toView" as subview of "fromView" superview [fromView.superview addSubview:toView]; switch (animation) { case AnimationPushFromRigh:{ // Position the "toView" to the right corner of the page toView.frame = CGRectOffset(pageFrame, pageWidth,0); // Animate the "fromView" to the left corner of the page fromView.frame = CGRectOffset(pageFrame, -pageWidth,0); // Animate the "toView" to the center of the page toView.frame = pageFrame; // Animate the "fromView" alpha fromView.alpha = 0; // Set and animate the "toView" alpha toView.alpha = 0; toView.alpha = 1; // Commit the animation [UIView commitAnimations]; } break; case AnimationPushFromLeft:{ // Position the "toView" to the left corner of the page toView.frame = CGRectOffset(pageFrame, -pageWidth,0); // Animate the "fromView" to the right corner of the page fromView.frame = CGRectOffset(pageFrame, pageWidth,0); // Animate the "toView" to the center of the page toView.frame = pageFrame; // Animate the "fromView" alpha fromView.alpha = 0; // Set and animate the "toView" alpha toView.alpha = 0; toView.alpha = 1; // Commit the animation [UIView commitAnimations]; } break; default: break; } } 

I really hope this helps those trying to use this focal point pattern: P

+1
source

Source: https://habr.com/ru/post/1415375/


All Articles