Commit 581132ea authored by Stepan Khapugin's avatar Stepan Khapugin Committed by Chromium LUCI CQ

[iOS][Getaway] Add UI to block the BVC when necessary.

Adds the blocking UI to the BVC.
This required a move of the protocol registration to the BVWrangler.

Bug: none
Change-Id: I81d0a868989eee7fea7e8a5d53b03c380e42ee15
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2547700Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833242}
parent 81f43d4d
...@@ -102,6 +102,9 @@ source_set("browser_view") { ...@@ -102,6 +102,9 @@ source_set("browser_view") {
"//ios/chrome/browser/ui/gestures", "//ios/chrome/browser/ui/gestures",
"//ios/chrome/browser/ui/history", "//ios/chrome/browser/ui/history",
"//ios/chrome/browser/ui/image_util:web", "//ios/chrome/browser/ui/image_util:web",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_commands",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_ui",
"//ios/chrome/browser/ui/infobars", "//ios/chrome/browser/ui/infobars",
"//ios/chrome/browser/ui/infobars:feature_flags", "//ios/chrome/browser/ui/infobars:feature_flags",
"//ios/chrome/browser/ui/infobars:public", "//ios/chrome/browser/ui/infobars:public",
...@@ -239,6 +242,7 @@ source_set("unit_tests") { ...@@ -239,6 +242,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/ui/browser_container:ui", "//ios/chrome/browser/ui/browser_container:ui",
"//ios/chrome/browser/ui/commands", "//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/fullscreen:feature_flags", "//ios/chrome/browser/ui/fullscreen:feature_flags",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/ui/main:scene_state_header", "//ios/chrome/browser/ui/main:scene_state_header",
"//ios/chrome/browser/ui/toolbar/public", "//ios/chrome/browser/ui/toolbar/public",
"//ios/chrome/browser/ui/toolbar/test", "//ios/chrome/browser/ui/toolbar/test",
...@@ -248,6 +252,7 @@ source_set("unit_tests") { ...@@ -248,6 +252,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/web_state_list", "//ios/chrome/browser/web_state_list",
"//ios/chrome/browser/web_state_list:test_support", "//ios/chrome/browser/web_state_list:test_support",
"//ios/chrome/browser/web_state_list/web_usage_enabler", "//ios/chrome/browser/web_state_list/web_usage_enabler",
"//ios/chrome/common/ui/reauthentication:reauthentication",
"//ios/chrome/test:block_cleanup_test", "//ios/chrome/test:block_cleanup_test",
"//ios/chrome/test:test_support", "//ios/chrome/test:test_support",
"//ios/net", "//ios/net",
......
...@@ -45,7 +45,10 @@ ...@@ -45,7 +45,10 @@
#import "ios/chrome/browser/ui/download/pass_kit_coordinator.h" #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h" #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h" #import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_mediator.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/infobars/infobar_feature.h" #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#import "ios/chrome/browser/ui/open_in/open_in_mediator.h" #import "ios/chrome/browser/ui/open_in/open_in_mediator.h"
#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h" #import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
#import "ios/chrome/browser/ui/page_info/page_info_coordinator.h" #import "ios/chrome/browser/ui/page_info/page_info_coordinator.h"
...@@ -104,6 +107,9 @@ ...@@ -104,6 +107,9 @@
// Mediator between OpenIn TabHelper and OpenIn UI. // Mediator between OpenIn TabHelper and OpenIn UI.
@property(nonatomic, strong) OpenInMediator* openInMediator; @property(nonatomic, strong) OpenInMediator* openInMediator;
// Mediator for incognito reauth.
@property(nonatomic, strong) IncognitoReauthMediator* incognitoAuthMediator;
// ================================================= // =================================================
// Child Coordinators, listed in alphabetical order. // Child Coordinators, listed in alphabetical order.
// ================================================= // =================================================
...@@ -226,6 +232,7 @@ ...@@ -226,6 +232,7 @@
[self startBrowserContainer]; [self startBrowserContainer];
[self createViewController]; [self createViewController];
[self startChildCoordinators]; [self startChildCoordinators];
[self startMediators];
[self installDelegatesForAllWebStates]; [self installDelegatesForAllWebStates];
[self installDelegatesForBrowser]; [self installDelegatesForBrowser];
[self addWebStateListObserver]; [self addWebStateListObserver];
...@@ -470,6 +477,27 @@ ...@@ -470,6 +477,27 @@
self.defaultBrowserPromoCoordinator = nil; self.defaultBrowserPromoCoordinator = nil;
} }
// Starts mediators owned by this coordinator.
- (void)startMediators {
if (self.browser->GetBrowserState()->IsOffTheRecord()) {
IncognitoReauthSceneAgent* reauthAgent = nil;
for (id agent in SceneStateBrowserAgent::FromBrowser(self.browser)
->GetSceneState()
.connectedAgents) {
if ([agent isKindOfClass:[IncognitoReauthSceneAgent class]]) {
reauthAgent = agent;
}
}
self.incognitoAuthMediator =
[[IncognitoReauthMediator alloc] initWithConsumer:self.viewController
reauthAgent:reauthAgent];
}
self.viewController.reauthHandler =
HandlerForProtocol(self.dispatcher, IncognitoReauthCommands);
}
#pragma mark - ActivityServiceCommands #pragma mark - ActivityServiceCommands
- (void)sharePage { - (void)sharePage {
......
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
#import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h" #import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h" #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h" #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/main/scene_state.h" #import "ios/chrome/browser/ui/main/scene_state.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h" #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h"
#import "ios/chrome/browser/web/web_navigation_browser_agent.h" #import "ios/chrome/browser/web/web_navigation_browser_agent.h"
#import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
#include "ios/web/public/test/web_task_environment.h" #include "ios/web/public/test/web_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h" #include "testing/gtest_mac.h"
...@@ -37,6 +39,14 @@ class BrowserCoordinatorTest : public PlatformTest { ...@@ -37,6 +39,14 @@ class BrowserCoordinatorTest : public PlatformTest {
UrlLoadingNotifierBrowserAgent::CreateForBrowser(browser_.get()); UrlLoadingNotifierBrowserAgent::CreateForBrowser(browser_.get());
SceneStateBrowserAgent::CreateForBrowser(browser_.get(), scene_state_); SceneStateBrowserAgent::CreateForBrowser(browser_.get(), scene_state_);
WebNavigationBrowserAgent::CreateForBrowser(browser_.get()); WebNavigationBrowserAgent::CreateForBrowser(browser_.get());
IncognitoReauthSceneAgent* reauthAgent = [[IncognitoReauthSceneAgent alloc]
initWithReauthModule:[[ReauthenticationModule alloc] init]];
[scene_state_ addAgent:reauthAgent];
CommandDispatcher* dispatcher = browser_->GetCommandDispatcher();
[dispatcher startDispatchingToTarget:reauthAgent
forProtocol:@protocol(IncognitoReauthCommands)];
} }
BrowserCoordinator* GetBrowserCoordinator() { BrowserCoordinator* GetBrowserCoordinator() {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#import "base/ios/block_types.h" #import "base/ios/block_types.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h" #import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
#import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h" #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_consumer.h"
#import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h" #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
#import "ios/chrome/browser/ui/settings/sync/utils/sync_presenter.h" #import "ios/chrome/browser/ui/settings/sync/utils/sync_presenter.h"
#import "ios/chrome/browser/ui/thumb_strip/thumb_strip_attacher.h" #import "ios/chrome/browser/ui/thumb_strip/thumb_strip_attacher.h"
...@@ -23,12 +24,14 @@ class Browser; ...@@ -23,12 +24,14 @@ class Browser;
@class BrowserViewControllerDependencyFactory; @class BrowserViewControllerDependencyFactory;
@class CommandDispatcher; @class CommandDispatcher;
@class ToolbarAccessoryPresenter; @class ToolbarAccessoryPresenter;
@protocol IncognitoReauthCommands;
// The top-level view controller for the browser UI. Manages other controllers // The top-level view controller for the browser UI. Manages other controllers
// which implement the interface. // which implement the interface.
@interface BrowserViewController @interface BrowserViewController
: UIViewController <LogoAnimationControllerOwnerOwner, : UIViewController <FindBarPresentationDelegate,
FindBarPresentationDelegate, IncognitoReauthConsumer,
LogoAnimationControllerOwnerOwner,
PageInfoPresentation, PageInfoPresentation,
SyncPresenter, SyncPresenter,
ThumbStripAttacher, ThumbStripAttacher,
...@@ -56,6 +59,9 @@ class Browser; ...@@ -56,6 +59,9 @@ class Browser;
// Command dispatcher. // Command dispatcher.
@property(nonatomic, weak) CommandDispatcher* commandDispatcher; @property(nonatomic, weak) CommandDispatcher* commandDispatcher;
// Handler for reauth commands.
@property(nonatomic, weak) id<IncognitoReauthCommands> reauthHandler;
// Returns whether or not text to speech is playing. // Returns whether or not text to speech is playing.
@property(nonatomic, assign, readonly, getter=isPlayingTTS) BOOL playingTTS; @property(nonatomic, assign, readonly, getter=isPlayingTTS) BOOL playingTTS;
......
...@@ -94,6 +94,8 @@ ...@@ -94,6 +94,8 @@
#import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h" #import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h"
#import "ios/chrome/browser/ui/image_util/image_copier.h" #import "ios/chrome/browser/ui/image_util/image_copier.h"
#import "ios/chrome/browser/ui/image_util/image_saver.h" #import "ios/chrome/browser/ui/image_util/image_saver.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_commands.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_view.h"
#import "ios/chrome/browser/ui/infobars/infobar_container_coordinator.h" #import "ios/chrome/browser/ui/infobars/infobar_container_coordinator.h"
#import "ios/chrome/browser/ui/infobars/infobar_feature.h" #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
#import "ios/chrome/browser/ui/infobars/infobar_positioner.h" #import "ios/chrome/browser/ui/infobars/infobar_positioner.h"
...@@ -500,6 +502,9 @@ NSString* const kBrowserViewControllerSnackbarCategory = ...@@ -500,6 +502,9 @@ NSString* const kBrowserViewControllerSnackbarCategory =
@property(nonatomic, assign) BOOL viewVisible; @property(nonatomic, assign) BOOL viewVisible;
// Whether the controller should broadcast its UI. // Whether the controller should broadcast its UI.
@property(nonatomic, assign, getter=isBroadcasting) BOOL broadcasting; @property(nonatomic, assign, getter=isBroadcasting) BOOL broadcasting;
// A view to obscure incognito content when the user isn't authorized to
// see it.
@property(nonatomic, strong) IncognitoReauthView* blockingView;
// Whether the controller is currently dismissing a presented view controller. // Whether the controller is currently dismissing a presented view controller.
@property(nonatomic, assign, getter=isDismissingModal) BOOL dismissingModal; @property(nonatomic, assign, getter=isDismissingModal) BOOL dismissingModal;
// Whether web usage is enabled for the WebStates in |self.browser|. // Whether web usage is enabled for the WebStates in |self.browser|.
...@@ -4697,6 +4702,33 @@ NSString* const kBrowserViewControllerSnackbarCategory = ...@@ -4697,6 +4702,33 @@ NSString* const kBrowserViewControllerSnackbarCategory =
return self.contentArea; return self.contentArea;
} }
#pragma mark - IncognitoReauthConsumer
- (void)setItemsRequireAuthentication:(BOOL)require {
if (require) {
if (!self.blockingView) {
self.blockingView = [[IncognitoReauthView alloc] init];
self.blockingView.translatesAutoresizingMaskIntoConstraints = NO;
self.blockingView.layer.zPosition = FLT_MAX;
[self.blockingView.authenticateButton
addTarget:self.reauthHandler
action:@selector(authenticateIncognitoContent)
forControlEvents:UIControlEventTouchUpInside];
[self.blockingView.tabSwitcherButton
addTarget:self.dispatcher
action:@selector(displayRegularTabSwitcherInGridLayout)
forControlEvents:UIControlEventTouchUpInside];
}
[self.view addSubview:self.blockingView];
AddSameConstraints(self.view, self.blockingView);
} else {
[self.blockingView removeFromSuperview];
}
}
#pragma mark - UIGestureRecognizerDelegate #pragma mark - UIGestureRecognizerDelegate
// Always return yes, as this tap should work with various recognizers, // Always return yes, as this tap should work with various recognizers,
......
...@@ -112,6 +112,10 @@ enum class KeyRetrievalTriggerForUMA; ...@@ -112,6 +112,10 @@ enum class KeyRetrievalTriggerForUMA;
// TabSwitcher UI, specifically in its grid layout. // TabSwitcher UI, specifically in its grid layout.
- (void)displayTabSwitcherInGridLayout; - (void)displayTabSwitcherInGridLayout;
// Same as displayTabSwitcherInGridLayout, but also force tab switcher to
// regular tabs page.
- (void)displayRegularTabSwitcherInGridLayout;
// TODO(crbug.com/779791) : Do not pass baseViewController through dispatcher. // TODO(crbug.com/779791) : Do not pass baseViewController through dispatcher.
// Shows the Autofill Settings UI, presenting from |baseViewController|. // Shows the Autofill Settings UI, presenting from |baseViewController|.
- (void)showAutofillSettingsFromViewController: - (void)showAutofillSettingsFromViewController:
......
...@@ -37,6 +37,10 @@ ...@@ -37,6 +37,10 @@
return self; return self;
} }
- (void)dealloc {
[_reauthAgent removeObserver:self];
}
#pragma mark - IncognitoReauthObserver #pragma mark - IncognitoReauthObserver
- (void)reauthAgent:(IncognitoReauthSceneAgent*)agent - (void)reauthAgent:(IncognitoReauthSceneAgent*)agent
......
...@@ -57,6 +57,9 @@ const CGFloat kButtonSpacing = 16.0f; ...@@ -57,6 +57,9 @@ const CGFloat kButtonSpacing = 16.0f;
+ (NSString*)authenticationActionLabel { + (NSString*)authenticationActionLabel {
LAContext* ctx = [[LAContext alloc] init]; LAContext* ctx = [[LAContext alloc] init];
// Call canEvaluatePolicy:error: once to populate biometrics type
[ctx canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
error:nil];
switch (ctx.biometryType) { switch (ctx.biometryType) {
case LABiometryTypeFaceID: case LABiometryTypeFaceID:
return @"Face ID"; return @"Face ID";
......
...@@ -187,6 +187,7 @@ source_set("main") { ...@@ -187,6 +187,7 @@ source_set("main") {
"//ios/chrome/browser/ui/commands", "//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/download", "//ios/chrome/browser/ui/download",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/ui/page_info:coordinator", "//ios/chrome/browser/ui/page_info:coordinator",
"//ios/chrome/browser/ui/print", "//ios/chrome/browser/ui/print",
"//ios/chrome/browser/ui/qr_scanner:coordinator", "//ios/chrome/browser/ui/qr_scanner:coordinator",
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/browsing_data_commands.h" #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h" #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/main/scene_state.h" #import "ios/chrome/browser/ui/main/scene_state.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h" #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#import "ios/chrome/browser/ui/util/multi_window_support.h" #import "ios/chrome/browser/ui/util/multi_window_support.h"
...@@ -432,9 +433,20 @@ ...@@ -432,9 +433,20 @@
} }
- (void)dispatchToEndpointsForBrowser:(Browser*)browser { - (void)dispatchToEndpointsForBrowser:(Browser*)browser {
[browser->GetCommandDispatcher() IncognitoReauthSceneAgent* reauthAgent = nil;
startDispatchingToTarget:_applicationCommandEndpoint for (id agent in _sceneState.connectedAgents) {
forProtocol:@protocol(ApplicationCommands)]; if ([agent isKindOfClass:[IncognitoReauthSceneAgent class]]) {
reauthAgent = agent;
}
}
CommandDispatcher* dispatcher = browser->GetCommandDispatcher();
[dispatcher startDispatchingToTarget:reauthAgent
forProtocol:@protocol(IncognitoReauthCommands)];
[dispatcher startDispatchingToTarget:_applicationCommandEndpoint
forProtocol:@protocol(ApplicationCommands)];
// -startDispatchingToTarget:forProtocol: doesn't pick up protocols the // -startDispatchingToTarget:forProtocol: doesn't pick up protocols the
// passed protocol conforms to, so ApplicationSettingsCommands is explicitly // passed protocol conforms to, so ApplicationSettingsCommands is explicitly
// dispatched to the endpoint as well. Since this is potentially // dispatched to the endpoint as well. Since this is potentially
...@@ -442,12 +454,10 @@ ...@@ -442,12 +454,10 @@
DCHECK(!_applicationCommandEndpoint || DCHECK(!_applicationCommandEndpoint ||
[_applicationCommandEndpoint [_applicationCommandEndpoint
conformsToProtocol:@protocol(ApplicationSettingsCommands)]); conformsToProtocol:@protocol(ApplicationSettingsCommands)]);
[browser->GetCommandDispatcher() [dispatcher startDispatchingToTarget:_applicationCommandEndpoint
startDispatchingToTarget:_applicationCommandEndpoint forProtocol:@protocol(ApplicationSettingsCommands)];
forProtocol:@protocol(ApplicationSettingsCommands)]; [dispatcher startDispatchingToTarget:_browsingDataCommandEndpoint
[browser->GetCommandDispatcher() forProtocol:@protocol(BrowsingDataCommands)];
startDispatchingToTarget:_browsingDataCommandEndpoint
forProtocol:@protocol(BrowsingDataCommands)];
} }
@end @end
...@@ -1057,7 +1057,15 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1057,7 +1057,15 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
[self.mainCoordinator prepareToShowTabGrid]; [self.mainCoordinator prepareToShowTabGrid];
} }
- (void)displayRegularTabSwitcherInGridLayout {
[self displayTabSwitcherForcingRegularTabs:YES];
}
- (void)displayTabSwitcherInGridLayout { - (void)displayTabSwitcherInGridLayout {
[self displayTabSwitcherForcingRegularTabs:NO];
}
- (void)displayTabSwitcherForcingRegularTabs:(BOOL)forcing {
if (!IsThumbStripEnabled()) { if (!IsThumbStripEnabled()) {
// When the thumb strip feature is enabled, |self.tabSwitcherIsActive| could // When the thumb strip feature is enabled, |self.tabSwitcherIsActive| could
// be YES if the tab switcher button is tapped while the thumb strip is // be YES if the tab switcher button is tapped while the thumb strip is
...@@ -1068,6 +1076,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -1068,6 +1076,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
} }
if (!self.isProcessingVoiceSearchCommand) { if (!self.isProcessingVoiceSearchCommand) {
[self.currentInterface.bvc userEnteredTabSwitcher]; [self.currentInterface.bvc userEnteredTabSwitcher];
if (forcing && self.currentInterface.incognito) {
[self setCurrentInterfaceForMode:ApplicationMode::NORMAL];
}
[self showTabSwitcher]; [self showTabSwitcher];
self.isProcessingTabSwitcherCommand = YES; self.isProcessingTabSwitcherCommand = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
...@@ -2361,7 +2374,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -2361,7 +2374,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Shows the tab switcher UI. // Shows the tab switcher UI.
- (void)showTabSwitcher { - (void)showTabSwitcher {
DCHECK(self.mainCoordinator); DCHECK(self.mainCoordinator);
[self.mainCoordinator setActivePage:[self activePage]]; [self.mainCoordinator setActivePage:self.activePage];
self.tabSwitcherIsActive = YES; self.tabSwitcherIsActive = YES;
self.mainCoordinator.delegate = self; self.mainCoordinator.delegate = self;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment