883 lines
33 KiB
Objective-C
883 lines
33 KiB
Objective-C
//
|
|
// MFViewController.m
|
|
// myverizon
|
|
//
|
|
// Created by Scott Pfeil on 11/26/13.
|
|
// Copyright (c) 2013 Verizon Wireless. All rights reserved.
|
|
//
|
|
|
|
#import "MFViewController.h"
|
|
@import MVMCore.MVMCoreAlertHandler;
|
|
@import MVMCore.MVMCoreSessionTimeHandler;
|
|
@import MVMCore.MVMCoreAlertObject;
|
|
@import MVMCore.MVMCoreLoadHandler;
|
|
@import MVMCore.MVMCoreNavigationHandler;
|
|
@import MVMCore.MVMCoreActionHandler;
|
|
@import MVMCore.MVMCoreTopAlertObject;
|
|
@import MVMCore.MVMCoreDispatchUtility;
|
|
@import MVMCore.MVMCoreActionUtility;
|
|
@import MVMCore.NSDictionary_MFConvenience;
|
|
@import MVMCore.MVMCoreLoadObject;
|
|
@import MVMCore.NSArray_MFConvenience;
|
|
@import MVMCore.MVMCoreGetterUtility;
|
|
@import MVMCore.MVMCoreConstants;
|
|
@import MVMCore.MVMCoreCache;
|
|
#import "NSLayoutConstraint+MFConvenience.h"
|
|
#import "UIColor+MFConvenience.h"
|
|
#import "MVMCoreUICommonViewsUtility.h"
|
|
#import "MFStyler.h"
|
|
#import "MVMCoreUISplitViewController.h"
|
|
#import "MVMCoreUITabBarPageControlViewController.h"
|
|
#import "MFFonts.h"
|
|
#import <MVMCoreUI/MFSizeObject.h>
|
|
#import "MVMCoreUIUtility.h"
|
|
#import "MVMCoreUIConstants.h"
|
|
#import "MVMCoreUISession.h"
|
|
#import "MVMCoreUILoggingHandler.h"
|
|
#import "MVMCoreUITabBarPageControlViewController.h"
|
|
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
|
@import MVMAnimationFramework;
|
|
|
|
@interface MFViewController()
|
|
|
|
// A flag for if this view controller is observing for cache updates or not.
|
|
@property (nonatomic) BOOL observingForResponseJSONUpdates;
|
|
|
|
// A flag for if the initial load is finished or not.
|
|
@property (nonatomic) BOOL initialLoadFinished;
|
|
|
|
// A mapping for mapping text fields for error keys
|
|
@property (strong, nonatomic) NSMutableDictionary *textFieldErrorMapping;
|
|
|
|
// title view for navigation bar, used for custom navigation titles
|
|
@property (weak, nonatomic) UILabel *titleLabel;
|
|
|
|
@end
|
|
|
|
@implementation MFViewController
|
|
|
|
- (void)dismiss {
|
|
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
|
if (self.presentingViewController) {
|
|
[[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self animated:YES];
|
|
} else if (self.navigationController) {
|
|
[[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:self animated:YES];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (BOOL)isVisibleViewController {
|
|
return ((self.presentingViewController && !self.presentedViewController) || [self.navigationController topViewController] == self);
|
|
}
|
|
|
|
- (void)registerTextField:(nonnull MFTextField *)textField forErrorKey:(nonnull NSString *)errorKey {
|
|
|
|
if (!self.textFieldErrorMapping) {
|
|
self.textFieldErrorMapping = [NSMutableDictionary dictionary];
|
|
}
|
|
[self.textFieldErrorMapping setObject:textField forKey:errorKey];
|
|
}
|
|
|
|
- (BOOL)screenSizeChanged {
|
|
return !CGSizeEqualToSize(self.previousScreenSize, CGSizeZero) && !fequalwiththreshold(self.previousScreenSize.width, self.view.bounds.size.width, .1);
|
|
}
|
|
|
|
#pragma mark - Functions To Subclass
|
|
|
|
- (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
|
|
self.pageType = loadObject.pageType;
|
|
self.loadObject = loadObject;
|
|
|
|
NSError *parseError = nil;
|
|
[self parsePageJSON:&parseError];
|
|
if (parseError) {
|
|
if (error) {
|
|
MVMCoreErrorObject *errorObject = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
|
|
errorObject.messageToDisplay = [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
|
|
*error = errorObject;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Verifies all modules needed are loaded.
|
|
return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error];
|
|
}
|
|
|
|
- (void)parsePageJSON:(NSError * _Nullable * _Nullable)error {
|
|
|
|
}
|
|
|
|
// Sets the screen to use the screen heading.
|
|
// it is required in device flow, where we are showing greeting name as screen heading,
|
|
// device details screen heading needs to be updated/refreshed again, if user has changed device nick name
|
|
- (NSString *)screenHeading {
|
|
NSString *screenHeading = [self.loadObject.pageJSON string:KeyScreenHeading];
|
|
return screenHeading;
|
|
}
|
|
|
|
- (void)setScreenHeadingMessage:(nullable NSString *)screenHeadingMessage {
|
|
if (screenHeadingMessage.length > 0) {
|
|
if (self.titleLabel) {
|
|
self.titleLabel.text = screenHeadingMessage;
|
|
} else {
|
|
self.navigationItem.title = screenHeadingMessage;
|
|
}
|
|
self.navigationItem.title = screenHeadingMessage;
|
|
self.navigationItem.accessibilityLabel = screenHeadingMessage;
|
|
if (self.navigationItem.titleView) {
|
|
self.navigationItem.titleView.accessibilityLabel = screenHeadingMessage;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)newDataBuildScreen {
|
|
UIView *titleView = [[MVMCoreUISession sharedGlobal] titleViewForController:self];
|
|
if (titleView) {
|
|
self.navigationItem.titleView = titleView;
|
|
} else {
|
|
// Sets the screen to use the screen heading.
|
|
NSString *screenHeading = [self screenHeading];
|
|
if (screenHeading) {
|
|
[self setScreenHeadingMessage:screenHeading];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)initialLoad {
|
|
|
|
// Creates an initial load object if needed.
|
|
if (!self.loadObject) {
|
|
self.loadObject = [[MVMCoreLoadObject alloc] initWithDelegateObject:[self delegateObject]];
|
|
}
|
|
|
|
// Avoid the setter so we are only setting the bool and wait for view will appear to update the navigation bar.
|
|
_masterShouldBeAccessible = [self isMasterInitiallyAccessible];
|
|
_supportShouldBeAccessible = [self isSupportInitiallyAccessible];
|
|
|
|
// Observe for cache updates if desired.
|
|
[self observeForResponseJSONUpdates];
|
|
}
|
|
|
|
- (void)updateViews {
|
|
|
|
}
|
|
|
|
- (void)backButtonPressed {
|
|
|
|
// Sends keep alive to the server.
|
|
[[MVMCoreSessionTimeHandler sharedSessionHandler] sendKeepAliveToServer:NO];
|
|
[[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:(self.manager ?: self) animated:YES];
|
|
}
|
|
|
|
- (void)handleErrorAsPopup:(nonnull MVMCoreErrorObject *)error {
|
|
|
|
// Logs the error.
|
|
if (error.logError) {
|
|
[MVMCoreLoggingHandler addErrorToLog:error];
|
|
}
|
|
|
|
MVMCoreLog(@"Error: %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay);
|
|
if (!error.silentError) {
|
|
|
|
// Show alert
|
|
MVMCoreAlertObject *alertObject = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO];
|
|
[[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject];
|
|
}
|
|
}
|
|
|
|
- (BOOL)shouldCacheInManager {
|
|
return YES;
|
|
}
|
|
|
|
- (nullable DelegateObject *)delegateObject {
|
|
return [MVMCoreUIDelegateObject createWithDelegateForAll:self];
|
|
}
|
|
|
|
#pragma mark - Response Handling
|
|
|
|
- (void)observeForResponseJSONUpdates {
|
|
if (!self.observingForResponseJSONUpdates && (self.pageTypesToListenFor.count > 0 || self.modulesToListenFor.count > 0)) {
|
|
self.observingForResponseJSONUpdates = YES;
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(responseJSONUpdated:) name:NotificationResponseLoaded object:nil];
|
|
}
|
|
}
|
|
|
|
- (void)stopObservingForResponseJSONUpdates {
|
|
if (self.observingForResponseJSONUpdates) {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:NotificationResponseLoaded object:nil];
|
|
self.observingForResponseJSONUpdates = NO;
|
|
}
|
|
}
|
|
|
|
- (nullable NSArray *)pageTypesToListenFor {
|
|
return nil;
|
|
}
|
|
|
|
- (nullable NSArray *)modulesToListenFor {
|
|
return nil;
|
|
}
|
|
|
|
- (void)responseJSONUpdated:(nonnull NSNotification *)notification {
|
|
|
|
// Checks for a page we are listening for.
|
|
NSArray *pageTypesListeningFor = [self pageTypesToListenFor];
|
|
NSDictionary *pages = [notification.userInfo dict:KeyPageMap];
|
|
__block BOOL newData = NO;
|
|
[pageTypesListeningFor enumerateObjectsUsingBlock:^(NSString * _Nonnull pageToListenFor, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
NSDictionary *page = [pages dict:pageToListenFor];
|
|
NSString *pageType = [page string:KeyPageType];
|
|
if (page && [pageType isEqualToString:self.pageType]) {
|
|
newData = [self newPageLoaded:page];
|
|
*stop = YES;
|
|
}
|
|
}];
|
|
|
|
// Checks for modules we are listening for.
|
|
NSArray *modulesListeningFor = [self modulesToListenFor];
|
|
NSDictionary *modulesReceived = [notification.userInfo dict:KeyModuleMap];
|
|
__block NSMutableDictionary *modulesUpdated = [NSMutableDictionary dictionary];
|
|
[modulesListeningFor enumerateObjectsUsingBlock:^(NSString * _Nonnull moduleToListenFor, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
NSDictionary *module = [modulesReceived dict:moduleToListenFor];
|
|
if (module) {
|
|
[modulesUpdated setObject:module forKey:moduleToListenFor];
|
|
}
|
|
}];
|
|
if (modulesUpdated.count > 0) {
|
|
newData = [self newModulesLoaded:modulesUpdated];
|
|
}
|
|
|
|
if (newData) {
|
|
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
|
[self newDataBuildAndUpdate];
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page {
|
|
self.loadObject.pageJSON = page;
|
|
NSError *parseError = nil;
|
|
[self parsePageJSON:&parseError];
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)newModulesLoaded:(nonnull NSDictionary *)modules {
|
|
if (self.loadObject.modulesJSON) {
|
|
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:self.loadObject.modulesJSON];
|
|
[mutableDictionary addEntriesFromDictionary:modules];
|
|
self.loadObject.modulesJSON = [NSDictionary dictionaryWithDictionary:mutableDictionary];
|
|
} else {
|
|
self.loadObject.modulesJSON = modules;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
+ (BOOL)verifyRequiredModulesLoadedForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
|
|
|
|
// Check if all needed modules are loaded.
|
|
__block NSMutableArray *modulesRequired = [NSMutableArray arrayWithArray:[[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] modulesRequiredForPageType:loadObject.pageType]];
|
|
if (modulesRequired.count > 0) {
|
|
|
|
[[loadObject.modulesJSON allKeys] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
|
|
if (modulesRequired.count == 0) {
|
|
*stop = YES;
|
|
} else {
|
|
NSUInteger index = [modulesRequired indexOfObject:obj];
|
|
if (index != NSNotFound) {
|
|
[modulesRequired removeObjectAtIndex:index];
|
|
}
|
|
}
|
|
}];
|
|
|
|
if (modulesRequired.count == 0) {
|
|
return YES;
|
|
} else {
|
|
|
|
// Error, not all needed modules are loaded.
|
|
if (error) {
|
|
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:[modulesRequired description] code:ErrorCodeRequiredModuleNotPresent domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
|
|
}
|
|
return NO;
|
|
}
|
|
} else {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
#pragma mark - Navigation Item, Menu, Support
|
|
|
|
- (nonnull UIColor *)navigationBarColor {
|
|
return [UIColor whiteColor];
|
|
}
|
|
|
|
- (BOOL)navigationBarHidden {
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)navigationBarTransparent {
|
|
return NO;
|
|
}
|
|
|
|
- (void)updateNavigationBarUI:(nonnull UINavigationController *)navigationController {
|
|
if (navigationController) {
|
|
[navigationController setNavigationBarHidden:[self navigationBarHidden] animated:YES];
|
|
|
|
[UIColor mfSetBackgroundColorForNavigationBar:[self navigationBarColor] navigationBar:navigationController.navigationBar transparent:[self navigationBarTransparent]];
|
|
|
|
UIColor *navigationBarTintColor = [self navigationBarTintColor] ?: [UIColor blackColor];
|
|
navigationController.navigationBar.tintColor = navigationBarTintColor;
|
|
|
|
// Have the navigation title match the tint color
|
|
if (self.titleLabel) {
|
|
self.titleLabel.textColor = navigationBarTintColor;
|
|
} else {
|
|
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithDictionary:navigationController.navigationBar.titleTextAttributes];
|
|
[attributes setObject:navigationBarTintColor forKey:NSForegroundColorAttributeName];
|
|
navigationController.navigationBar.titleTextAttributes = attributes;
|
|
}
|
|
|
|
if (navigationController == [MVMCoreUISplitViewController mainSplitViewController].navigationController) {
|
|
// Update icons if main navigation controller.
|
|
[[MVMCoreUISession sharedGlobal].splitViewController setupPanels];
|
|
[self setMasterShouldBeAccessible:self.masterShouldBeAccessible];
|
|
[self setSupportShouldBeAccessible:self.supportShouldBeAccessible];
|
|
[self showBottomProgressBar];
|
|
[[MVMCoreUISession sharedGlobal].splitViewController setNavigationIconColor:navigationBarTintColor];
|
|
|
|
// Update separator.
|
|
UIView *separatorView = (UIView *)[MVMCoreUISession sharedGlobal].navigationController.separatorView;
|
|
separatorView.hidden = ([self isKindOfClass:[MVMCoreUITabBarPageControlViewController class]]
|
|
|| self.manager
|
|
|| self.loadObject.requestParameters.tabWasPressed);
|
|
}
|
|
} else {
|
|
[[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate];
|
|
}
|
|
}
|
|
|
|
- (BOOL)isMasterInitiallyAccessible {
|
|
if ([self.loadObject.pageJSON boolForKey:KeyHideMainMenu]) {
|
|
return NO;
|
|
}
|
|
return [MVMCoreUISession sharedGlobal].launchAppLoadedSuccessfully;
|
|
}
|
|
|
|
- (void)setMasterShouldBeAccessible:(BOOL)masterShouldBeAccessible {
|
|
|
|
MVMCoreUISplitViewController *splitViewController = [MVMCoreUISession sharedGlobal].splitViewController;
|
|
if (self.manager && [self.manager respondsToSelector:@selector(setMasterShouldBeAccessible:)]) {
|
|
[(MFViewController *)self.manager setMasterShouldBeAccessible:masterShouldBeAccessible];
|
|
} else if ([self isVisibleViewController]) {
|
|
[splitViewController setLeftPanelIsAccessible:masterShouldBeAccessible forViewController:self];
|
|
}
|
|
_masterShouldBeAccessible = masterShouldBeAccessible;
|
|
}
|
|
|
|
- (BOOL)isSupportInitiallyAccessible {
|
|
return [MVMCoreUISession sharedGlobal].launchAppLoadedSuccessfully || [self showRightPanelForScreenBeforeLaunchApp];
|
|
}
|
|
|
|
- (BOOL)showRightPanelForScreenBeforeLaunchApp {
|
|
return [self.loadObject.pageJSON lenientBoolForKey:@"showRightPanel"];
|
|
}
|
|
|
|
|
|
- (BOOL)isOverridingRightButton {
|
|
NSDictionary *rightPanelLinkDict = [self.loadObject.pageJSON dict:@"rightPanelButtonLink"];
|
|
if (rightPanelLinkDict) {
|
|
[[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:rightPanelLinkDict additionalData:nil delegateObject:[self delegateObject]];
|
|
return YES;
|
|
} else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (BOOL)isOverridingLeftButton {
|
|
NSDictionary *leftPanelLinkDict = [self.loadObject.pageJSON dict:@"leftPanelButtonLink"];
|
|
if (leftPanelLinkDict) {
|
|
[[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:leftPanelLinkDict additionalData:nil delegateObject:[self delegateObject]];
|
|
return YES;
|
|
} else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
|
|
- (void)setSupportShouldBeAccessible:(BOOL)supportShouldBeAccessible {
|
|
|
|
MVMCoreUISplitViewController *splitViewController = [MVMCoreUISession sharedGlobal].splitViewController;
|
|
if (self.manager && [self.manager respondsToSelector:@selector(setSupportShouldBeAccessible:)]) {
|
|
[(MFViewController *)self.manager setSupportShouldBeAccessible:supportShouldBeAccessible];
|
|
} else if ([self isVisibleViewController]) {
|
|
[splitViewController setRightPanelIsAccessible:supportShouldBeAccessible forViewController:self];
|
|
}
|
|
_supportShouldBeAccessible = supportShouldBeAccessible;
|
|
}
|
|
|
|
- (BOOL)mainTableView:(MainMenuViewController *)mainTableView shouldSelectOptionAtIndexPath:(NSIndexPath *)indexPath {
|
|
return YES;
|
|
}
|
|
|
|
// sub class to change the navigation bar color
|
|
- (UIColor *)navigationBarTintColor {
|
|
return nil;
|
|
}
|
|
|
|
- (void)showBottomProgressBar {
|
|
|
|
MVMCoreUISplitViewController *splitViewController = [MVMCoreUISession sharedGlobal].splitViewController;
|
|
if ([self isVisibleViewController]) {
|
|
|
|
NSString *progressAsString = [self.loadObject.pageJSON stringForKey:KeyProgressPercent];
|
|
float progress = progressAsString.length > 0 ? [progressAsString floatValue]/100 : 0;
|
|
[splitViewController setBottomProgressBarProgress:progress];
|
|
}
|
|
}
|
|
|
|
#pragma mark - View lifecycle
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
// Do any additional setup after loading the view.
|
|
|
|
if ([MVMCoreUISplitViewController mainSplitViewController]) {
|
|
self.navigationItem.hidesBackButton = YES;
|
|
}
|
|
|
|
// init intro animation manager
|
|
MVMAnimationManager *introAnimationManager = [[MVMAnimationManager alloc] init];
|
|
self.introAnimationManager = introAnimationManager;
|
|
|
|
// Present the tooltip from the bottom over the tab bar controller.
|
|
if ([MVMCoreGetterUtility isOnIPad]) {
|
|
|
|
// Different style for ipad.
|
|
self.modalPresentationStyle = UIModalPresentationFormSheet;
|
|
} else {
|
|
|
|
// Standard.
|
|
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
|
}
|
|
|
|
MVMCoreLog(@"View Controller Loaded : %@",NSStringFromClass([self class]));
|
|
// Do some initial loading.
|
|
if (!self.initialLoadFinished) {
|
|
self.initialLoadFinished = YES;
|
|
[self initialLoad];
|
|
}
|
|
|
|
// Since we have new data, build stuff for the screen and update the ui once the screen is done laying out.
|
|
[self newDataBuildAndUpdate];
|
|
|
|
self.needToupdateUIOnScreenSizeChanges = YES;
|
|
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
self.disableAnimations = YES;
|
|
}
|
|
|
|
if (!self.disableAnimations) {
|
|
[self setupIntroAnimations];
|
|
}
|
|
|
|
[self startValidation];
|
|
}
|
|
|
|
- (void)newDataBuildAndUpdate {
|
|
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
|
[self newDataBuildScreen];
|
|
self.needToUpdateUI = YES;
|
|
[self.view setNeedsLayout];
|
|
}];
|
|
}
|
|
|
|
- (void)didReceiveMemoryWarning {
|
|
[super didReceiveMemoryWarning];
|
|
// Dispose of any resources that can be recreated.
|
|
|
|
[[MVMCoreCache sharedCache] clearImageCache];
|
|
}
|
|
|
|
- (void)viewDidLayoutSubviews {
|
|
// Updates the views if necessary
|
|
if (self.needToUpdateUI || (self.needToupdateUIOnScreenSizeChanges && [self screenSizeChanged])) {
|
|
|
|
// Add to fix a strange bug where the width is zero and things get messed up.
|
|
if ([self isViewLoaded] && (NSInteger)CGRectGetWidth(self.view.bounds) > 1) {
|
|
|
|
[self updateViews];
|
|
self.needToUpdateUI = NO;
|
|
}
|
|
}
|
|
|
|
self.previousScreenSize = self.view.bounds.size;
|
|
|
|
[super viewDidLayoutSubviews];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
[super viewWillAppear:animated];
|
|
|
|
// Update the navigation bar ui when view is appearing. Don't handle if there is a tab bar page control, it will be handled later.
|
|
if (!self.manager) {
|
|
[self updateNavigationBarUI:self.navigationController];
|
|
}
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
|
[super viewDidAppear:animated];
|
|
|
|
// Don't track page state if there is a tab bar page control, it will be handled later.
|
|
if (!self.manager) {
|
|
[MVMCoreUISession sharedGlobal].currentPageType = self.pageType;
|
|
[self adobeTrackPageState];
|
|
}
|
|
|
|
// perform intro animations
|
|
if (!self.disableAnimations) {
|
|
[self.introAnimationManager performAnimations];
|
|
}
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
[super viewWillDisappear:animated];
|
|
if (self.selectedField) {
|
|
[self.selectedField resignFirstResponder];
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[self stopObservingForResponseJSONUpdates];
|
|
MVMCoreLog(@"%@ deallocated", [[self class] description]);
|
|
}
|
|
|
|
- (BOOL)shouldAutorotate {
|
|
return YES;
|
|
}
|
|
|
|
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
|
|
|
if ([MVMCoreGetterUtility isOnIPad]) {
|
|
return UIInterfaceOrientationMaskAll;
|
|
} else {
|
|
return UIInterfaceOrientationMaskPortrait;
|
|
}
|
|
}
|
|
|
|
//this method is needed for getting status bar style from present viewcotnroller
|
|
- (UIStatusBarStyle)preferredStatusBarStyle {
|
|
if ([self respondsToSelector:@selector(defaultStatusBarStyle)]) {
|
|
return [self defaultStatusBarStyle];
|
|
} else {
|
|
return UIStatusBarStyleDefault;
|
|
}
|
|
}
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
|
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
|
|
// Updates the detail view width.
|
|
void (^completion)(id<UIViewControllerTransitionCoordinatorContext>) = ^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
[self.view setNeedsLayout];
|
|
};
|
|
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
} completion:completion];
|
|
}
|
|
|
|
- (BOOL)viewRespectsSystemMinimumLayoutMargins {
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark - UITextField Functions
|
|
|
|
// To Remove TextFields Bug: Keyboard is not dismissing after reaching textfield max length limit
|
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
|
[textField resignFirstResponder];
|
|
return YES;
|
|
}
|
|
|
|
- (IBAction)textFieldDoneEditing:(id)sender {
|
|
[sender resignFirstResponder];
|
|
}
|
|
|
|
- (void)textFieldDidBeginEditing:(UITextField *)textField {
|
|
self.selectedField = textField;
|
|
//Accessibility
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
if ([self textFieldHasDoneButton:textField]) {//If there is no toolbar and done, input view can't be dismissed
|
|
[self.view setAccessibilityElements:@[textField.inputView,textField.inputAccessoryView]];
|
|
}
|
|
[self performSelector:@selector(focusElement:) withObject:textField.inputView afterDelay:0.1];
|
|
}
|
|
}
|
|
|
|
- (BOOL)textFieldHasDoneButton:(UITextField *)textField {
|
|
return [textField.inputAccessoryView isKindOfClass:[UIToolbar class]] && [[[((UIToolbar *)textField.inputAccessoryView) items] lastObject] isKindOfClass:[UIBarButtonItem class]] && [textField.inputView isKindOfClass:[UIPickerView class]];
|
|
}
|
|
|
|
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
|
|
|
if (textField == self.selectedField) {
|
|
//Accessibility
|
|
if (UIAccessibilityIsVoiceOverRunning()) {
|
|
[self.view setAccessibilityElements:nil];
|
|
[self performSelector:@selector(focusElement:) withObject:textField afterDelay:0.1];
|
|
}
|
|
self.selectedField = nil;
|
|
}
|
|
}
|
|
|
|
- (void)dismissFieldInput:(id)sender {
|
|
if (self.selectedField) {
|
|
[self.selectedField resignFirstResponder];
|
|
}
|
|
}
|
|
|
|
#pragma mark - UITextView Functions
|
|
|
|
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
|
|
self.selectedField = textView;
|
|
return YES;
|
|
}
|
|
|
|
- (void)textViewDidEndEditing:(UITextView *)textView {
|
|
if (textView == self.selectedField) {
|
|
self.selectedField = nil;
|
|
}
|
|
}
|
|
|
|
#pragma mark - TableView
|
|
|
|
- (NSInteger)getNumberOfSections {
|
|
NSInteger numberOfSections = 1;
|
|
return numberOfSections;
|
|
}
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
NSInteger numberOfSections = 0;
|
|
|
|
// Fixes a funky issue where the table is being loaded before it should be.
|
|
if (CGRectGetWidth(tableView.bounds) > 1) {
|
|
numberOfSections = [self getNumberOfSections];
|
|
}
|
|
|
|
return numberOfSections;
|
|
}
|
|
|
|
#pragma mark - ButtonDelegateProtocol
|
|
|
|
- (BOOL)button:(nonnull NSObject <MFButtonProtocol> *)button shouldPerformActionWithMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark - MVMCoreActionDelegateProtocol
|
|
|
|
- (void)logActionWithActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
[[MVMCoreUILoggingHandler sharedLoggingHandler] defaultLogActionForController:self actionInformation:actionInformation additionalData:additionalData];
|
|
}
|
|
|
|
- (void)handleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
|
|
if (requestParameters.openSupportPanel) {
|
|
[[MVMCoreUISession sharedGlobal].splitViewController.rightPanel willOpenWithActionInformation:actionInformation];
|
|
}
|
|
|
|
[self addFormParamsWithRequestParameters:requestParameters];
|
|
requestParameters.parentPageType = [self.loadObject.pageJSON stringForKey:@"parentPageType"];
|
|
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:[self delegateObject]];
|
|
}
|
|
|
|
- (void)handleBackAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
BOOL openSupportPanel = [actionInformation boolForKey:KeyOpenSupport];
|
|
if (openSupportPanel) {
|
|
[[MVMCoreUISession sharedGlobal].splitViewController.rightPanel willOpenWithActionInformation:actionInformation];
|
|
}
|
|
|
|
if ([self.manager respondsToSelector:@selector(handleBackAction:additionalData:)]) {
|
|
[((UIViewController <MVMCoreActionDelegateProtocol>*)self.manager) handleBackAction:actionInformation additionalData:additionalData];
|
|
} else {
|
|
[self dismiss];
|
|
}
|
|
}
|
|
|
|
- (void)shouldLinkAwayWithURL:(nullable NSURL *)URL appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData linkAwayBlock:(nonnull void (^)(NSURL * _Nullable, NSURL * _Nullable, NSDictionary * _Nullable, NSDictionary * _Nullable))linkAwayBlock {
|
|
linkAwayBlock(appURL,URL,actionInformation,additionalData);
|
|
}
|
|
|
|
- (void)prepareRequestForPreviousSubmission:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData submit:(nonnull void (^)(MVMCoreRequestParameters * _Nonnull requestParameters, NSDictionary * _Nullable dataForPage))submit {
|
|
|
|
// Combines data
|
|
NSDictionary *dataForPage = self.loadObject.dataForPage;
|
|
if (dataForPage && additionalData) {
|
|
|
|
NSMutableDictionary *mutableData = [NSMutableDictionary dictionaryWithDictionary:dataForPage];
|
|
[mutableData addEntriesFromDictionary:additionalData];
|
|
dataForPage = mutableData;
|
|
} else if (additionalData) {
|
|
dataForPage = additionalData;
|
|
}
|
|
|
|
// Makes previous request
|
|
submit(self.loadObject.requestParameters,dataForPage);
|
|
}
|
|
|
|
- (void)willShowPopupWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject alertJson:(nonnull NSDictionary *)alertJson {
|
|
}
|
|
|
|
- (nullable MVMCoreAlertObject *)willShowTopAlertWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject alertJson:(nonnull NSDictionary *)alertJson {
|
|
if (self.currentTopAlertPageType && [self.currentTopAlertPageType isEqualToString:[alertJson string:KeyPageType]]) {
|
|
return nil;
|
|
} else {
|
|
return alertObject;
|
|
}
|
|
}
|
|
|
|
// Handle cancel
|
|
- (void)handleCancel:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
|
|
}
|
|
|
|
- (void)handleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
|
|
[MVMCoreActionHandler defaultHandleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:[self delegateObject]];
|
|
}
|
|
|
|
- (void)handleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData {
|
|
|
|
[MVMCoreActionHandler defaultHandleActionError:error additionalData:additionalData];
|
|
}
|
|
|
|
- (void)prepareForOpenOtherAppModule:(nullable NSString *)module {
|
|
|
|
}
|
|
|
|
#pragma mark - MVMCoreLoadDelegateProtocol
|
|
|
|
- (BOOL)checkForDelegateSpecificErrors:(nullable MVMCoreErrorObject *)errorObject loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreLoadObject * _Nullable loadObject, MVMCoreErrorObject * _Nullable error))completionHandler {
|
|
return [MVMCoreLoadHandler defaultCheckForSpecificErrors:errorObject loadObject:loadObject];
|
|
}
|
|
|
|
- (BOOL)shouldContinueToErrorPage:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)handleModuleError:(nonnull NSString *)module loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nonnull MVMCoreErrorObject *)error {
|
|
return [MVMCoreLoadHandler defaultHandleModuleError:module loadObject:loadObject error:error];
|
|
}
|
|
|
|
- (nullable MVMCoreAlertObject *)alertObjectToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)errorObject {
|
|
return [MVMCoreAlertObject alertObjectForLoadObject:loadObject error:errorObject delegateObject:[self delegateObject]];
|
|
}
|
|
|
|
- (void)handleFieldErrors:(nullable NSArray *)fieldErrors loadObject:(nonnull MVMCoreLoadObject *)loadObject {
|
|
|
|
if (self.textFieldErrorMapping.count > 0) {
|
|
NSMutableArray *enabledTextField = [NSMutableArray new];
|
|
for (NSUInteger i = 0; i < fieldErrors.count; i++) {
|
|
|
|
NSDictionary *info = [fieldErrors objectAtIndex:i ofType:[NSDictionary class]];
|
|
NSString *fieldName = [info stringForKey:KeyFieldName];
|
|
MFTextField *textField = [self.textFieldErrorMapping objectForKey:fieldName ofType:[MFTextField class]];
|
|
if (textField && textField.enabled) {
|
|
[textField setErrorMessage:[info stringForKey:KeyUserMessage]];
|
|
[enabledTextField addObject:textField];
|
|
}
|
|
}
|
|
if ([enabledTextField count] > 0) {
|
|
MFTextField *firstErrorTextField = enabledTextField[0];
|
|
[firstErrorTextField pushAccessibilityNotification];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)loadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable MFViewController *)loadedViewController error:(nullable MVMCoreErrorObject *)error {
|
|
|
|
// Open the support panel
|
|
#warning Scott/Chris, decide if we want to do this if there is an error.
|
|
if (!error && (loadObject.requestParameters.openSupportPanel || [loadObject.systemParametersJSON boolForKey:KeyOpenSupport])) {
|
|
[[MVMCoreUISession sharedGlobal].splitViewController showRightPanelAnimated:YES];
|
|
}
|
|
[MVMCoreUILoggingHandler logWithDelegateLoadFinished:loadObject loadedViewController:loadedViewController error:error];
|
|
}
|
|
|
|
- (void)loadCancelled:(nullable MVMCoreLoadObject *)loadObject {
|
|
|
|
}
|
|
|
|
#pragma mark - TopAlertDelegateProtocol
|
|
|
|
- (void)topAlertViewShown:(nonnull id)topAlert topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject {
|
|
self.currentTopAlertPageType = topAlertObject.pageType;
|
|
}
|
|
|
|
- (void)topAlertViewDismissed:(nonnull id)topAlert {
|
|
self.currentTopAlertPageType = nil;
|
|
}
|
|
|
|
- (BOOL)shouldLoadTopAlertAction:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
|
|
if (![[actionMap stringForKey:KeyPageType] isEqualToString:self.loadObject.requestParameters.pageType]) {
|
|
return YES;
|
|
} else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
#pragma mark - MVMCoreViewManagerViewControllerProtocol
|
|
|
|
- (void)viewControllerReadyInManager:(nonnull id <MVMCoreViewManagerProtocol>)manager {
|
|
// The screen is officially ready. Only update the navigation bar if initial load has finished.
|
|
if (self.initialLoadFinished) {
|
|
[self updateNavigationBarUI:self.manager.navigationController];
|
|
}
|
|
[MVMCoreUISession sharedGlobal].currentPageType = self.pageType;
|
|
[self adobeTrackPageState];
|
|
}
|
|
|
|
- (void)setManager:(UIViewController <MVMCoreViewManagerProtocol>*)manager {
|
|
_manager = manager;
|
|
if ([manager isKindOfClass:MVMCoreUITabBarPageControlViewController.class]) {
|
|
self.tabBarPageControl = (MVMCoreUITabBarPageControlViewController *)manager;
|
|
}
|
|
}
|
|
|
|
#pragma mark - adobe analytics
|
|
|
|
- (nullable NSArray <NSDictionary *> *)additionalActionsToTrackWithMainActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
return nil;
|
|
}
|
|
|
|
- (nullable NSDictionary *)getActionTrackDataDictionaryForActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
return [[MVMCoreUILoggingHandler sharedLoggingHandler] defaultGetActionTrackDataDictionaryForController:self actionInformation:actionInformation additionalData:additionalData];
|
|
}
|
|
|
|
- (void)adobeTrackPageState {
|
|
[[MVMCoreUILoggingHandler sharedLoggingHandler] defaultLogPageStateForController:self];
|
|
}
|
|
|
|
- (nullable NSDictionary *)additionalDataToTrackActionWithActionInformation:(nullable NSDictionary *)actionInformation {
|
|
return nil;
|
|
}
|
|
|
|
- (nullable NSDictionary *)additionalDataToTrackForPage {
|
|
return nil;
|
|
}
|
|
|
|
- (nullable NSArray *)dynamicPageNameValuesToTrackPage {
|
|
return nil;
|
|
}
|
|
|
|
//Accessibility
|
|
- (void) focusElement:(id) element {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element);
|
|
});
|
|
}
|
|
|
|
#pragma mark - Animations
|
|
// subclass to override default intro animations
|
|
-(void) setupIntroAnimations {
|
|
|
|
}
|
|
|
|
@end
|