// // MFScrollingViewController.m // MobileFirstFramework // // Created by Pfeil, Scott Robert on 11/21/17. // Copyright © 2017 Verizon Wireless. All rights reserved. // #import "MFScrollingViewController.h" #import #import "MFTransparentGIFView.h" #import #import #import #import "NSLayoutConstraint+MVMCoreUIConvenience.h" #import #import "MVMCoreUIUtility.h" #import "MVMCoreUIConstants.h" #import "MVMCoreUISession.h" @interface MFScrollingViewController () // A flag for if the Keyboard Notifications are added. @property (assign, nonatomic) BOOL keyboardNotificationsAdded; // Stores the pre-keyboard content inset. @property (assign, nonatomic) UIEdgeInsets preKeyboardContentInset; // Boolean for if the keyboard is showing or now. @property (assign, nonatomic, readwrite) BOOL keyboardIsShowing; // Boolean for if the scroll indicator has flashed @property (assign, nonatomic, readwrite) BOOL isScrollIndicatorFlashed; // Property for reference of hand scroll animation gif @property (strong, nonatomic) MFTransparentGIFView *gifView; @property (assign, nonatomic, readwrite) BOOL isPageTypeEnabledForHandScrollAnimation; @property (assign) __block BOOL isHandScrollAnimationRendered; @property (nullable, strong, nonatomic) id scrollFlashNotificationObserver; // To add gif view for scroll animation - (void)addScrollDownHandAnimation; @end @implementation MFScrollingViewController static CGFloat const HandScrollMargin = 10.f; static NSTimeInterval const HandScrollAnimationTiming = 7.f; - (BOOL)isFlashScrollIndicatorAccesible { return YES; } - (void)newDataBuildScreen { [super newDataBuildScreen]; [self updatePageHandScrollAnimationStatus]; if ([self shouldEnableHandScrollAnimation]) { [self addScrollDownHandAnimation]; } } - (void)initialLoad { [super initialLoad]; // Adds the tap gesture to dismiss the keyboard. self.dismissKeyboardTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissFieldInput:)]; self.dismissKeyboardTapGesture.enabled = NO; //This flag is required to offset for footer for tableView self.isHandScrollAnimationRendered = NO; self.isPageTypeEnabledForHandScrollAnimation = NO; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.view addGestureRecognizer:self.dismissKeyboardTapGesture]; self.scrollView.alwaysBounceVertical = NO; self.scrollView.delegate = self; self.isScrollIndicatorFlashed = NO; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; BOOL automaticInset = NO; if (@available(iOS 11.0, *)) { automaticInset = self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic; } // Takes into account the navigation bar. if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) { CGFloat top = 0; if (self.navigationController && ![self.navigationController isNavigationBarHidden] && ![self navigationBarTransparent] && (self.navigationController.extendedLayoutIncludesOpaqueBars || !self.navigationController.navigationBar.opaque)) { // Navigation bar is showing, set the top content inset to its height. top = CGRectGetMaxY(self.navigationController.navigationBar.frame); } else { // No navigation bar, account for the status bar if need be top = [MVMCoreUIUtility getTopSpaceWithStatusBarForView:self.scrollView]; } UIEdgeInsets inset; if (self.keyboardIsShowing) { inset = self.preKeyboardContentInset; } else { inset = self.scrollView.contentInset; } if (!fequal(top, inset.top)) { inset = UIEdgeInsetsMake(top, inset.left, inset.bottom, inset.right); if (self.keyboardIsShowing) { self.preKeyboardContentInset = inset; } else { self.scrollView.contentInset = inset; } // Fixes an ios bug where setting the scroll content doesn't redraw the scroll content at offset 0. if (self.scrollView.contentOffset.y < 1) { [self.scrollView scrollRectToVisible:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 1) animated:NO]; } } } // Update any constraints. [self.view setNeedsUpdateConstraints]; [self.view layoutSubviews]; } - (void)updateViewConstraints { [super updateViewConstraints]; // Sets the width of the content to the width of the screen. self.contentWidthConstraint.constant = CGRectGetWidth(self.view.bounds) - self.scrollView.contentInset.left - self.scrollView.contentInset.right; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self registerForKeyboardNotifications]; [self registerForScrollFlashWhenLaunch]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self unregisterForKeyboardNotifications]; [self unregisterForScrollFlashWhenLaunch]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (!self.shouldTriggerStartAnimations || DisableAnimations) { [self pageAnimationEnded]; } } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if (!CGAffineTransformIsIdentity(self.view.transform)) { self.view.transform = CGAffineTransformIdentity; self.scrollView.contentInset = UIEdgeInsetsMake(0,0,0,0); } } #pragma mark - keyboard handling - (void)keyboardWillShow:(nonnull NSNotification *)notification { // Stores the current scroll insets if the keyboard was hidden. if (!self.keyboardIsShowing) { self.preKeyboardContentInset = self.scrollView.contentInset; } self.keyboardIsShowing = YES; // Enables the tap gesture. self.dismissKeyboardTapGesture.enabled = YES; [MVMCoreUIUtility setScrollViewInsetForKeyboardShow:notification scrollView:self.scrollView viewController:self rectToScrollTo:^CGRect{ return [self rectToScrollToWhenKeyboardPopsUp]; }]; } - (void)keyboardWillBeHidden:(nonnull NSNotification *)notification { self.keyboardIsShowing = NO; // Disables the tap gesture. self.dismissKeyboardTapGesture.enabled = NO; [MVMCoreUIUtility setScrollViewInsetForKeyboardHide:notification scrollView:self.scrollView viewController:self contentInset:self.preKeyboardContentInset]; } - (CGRect)rectToScrollToWhenKeyboardPopsUp { return [self.scrollView convertRect:[self.selectedField frame] fromView:[self.selectedField superview]]; } - (void)registerForKeyboardNotifications { if (!self.keyboardNotificationsAdded) { self.keyboardNotificationsAdded = YES; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } } - (void)unregisterForKeyboardNotifications { if (self.keyboardNotificationsAdded) { self.keyboardNotificationsAdded = NO; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } } #pragma mark - UIScrollViewDelegate Functions - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self stopHandScrollAnimation:YES]; } #pragma mark - Scroll Flash Handling - (void)registerForScrollFlashWhenLaunch { if ([self isFlashScrollIndicatorAccesible]) { __weak MFScrollingViewController *weakSelf = self; self.scrollFlashNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"PageAnimationEnded" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { [weakSelf pageAnimationEnded]; }]; } } - (void)unregisterForScrollFlashWhenLaunch { if (self.scrollFlashNotificationObserver) { [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PageAnimationEnded" object:nil]; self.scrollFlashNotificationObserver = nil; } } - (void)pageAnimationEnded { //Implemented for Single View Appearance for now //Can be enabled for every time view appear if (self.isScrollIndicatorFlashed == NO) { __weak MFScrollingViewController *weakSelf = self; [MVMCoreDispatchUtility performBlockOnMainThread:^{ [weakSelf.scrollView flashScrollIndicators]; weakSelf.isScrollIndicatorFlashed = YES; }]; } // To Show or Hide Hand Scroll Animation if (![self shouldEnableHandScrollAnimation]) { return; } if ([self isHandScrollAnimationRequired]) { __weak MFScrollingViewController *weakSelf = self; [MVMCoreDispatchUtility performBlockOnMainThread: ^{ [weakSelf startHandScrollAnimation]; }]; } else { [self stopHandScrollAnimation:NO]; } } #pragma mark - Hand Scroll Animation // this can be ovveriden by subclass to enable or disable the same. but should check for server flag in subclass implementation - (BOOL)shouldEnableHandScrollAnimation { // depends on server master flag for app // to check if the hand scroll animation is enabled for the page return self.isPageTypeEnabledForHandScrollAnimation; } - (void)updatePageHandScrollAnimationStatus { if ([MVMCoreUISession sharedGlobal].enableHandScrollAnimation) { self.isPageTypeEnabledForHandScrollAnimation = [self.loadObject.pageJSON boolForKey:KeyHandScrollAnimation]; } } - (void)addScrollDownHandAnimation { if (!self.gifView) { MFTransparentGIFView *gifView = [[MFTransparentGIFView alloc] initWithFrame:CGRectZero ImageName:KeyHandScroll StartImmediately:YES Duration:-1 LoopCompletionBlock:nil]; gifView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:gifView]; gifView.contentMode = UIViewContentModeScaleAspectFit; // width is changed to match aspect ratio of asset [NSLayoutConstraint constraintPinView:gifView heightConstraint:YES heightConstant:50 widthConstraint:YES widthConstant:25]; [NSLayoutConstraint constraintPinSubview:gifView pinCenterX:YES pinCenterY:NO]; [NSLayoutConstraint constraintWithItem:gifView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeBottom multiplier:1.f constant:-30].active = YES; self.gifView = gifView; } } - (BOOL)isHandScrollAnimationRequired { return (self.scrollView.contentSize.height - self.scrollView.frame.size.height) > HandScrollMargin; } - (void)startHandScrollAnimation { if (self.isHandScrollAnimationRendered) { return; } self.isHandScrollAnimationRendered = YES; self.gifView.alpha = 1.f; __weak MFScrollingViewController *weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(HandScrollAnimationTiming * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf stopHandScrollAnimation:YES]; }); } - (void)stopHandScrollAnimation:(BOOL)animated { __weak MFScrollingViewController *weakSelf = self; [MVMCoreDispatchUtility performBlockOnMainThread:^{ if (animated) { [UIView animateWithDuration:0.5 animations:^{ weakSelf.gifView.alpha = 0.f; } completion:nil]; } else { weakSelf.gifView.alpha = 0.f; } }]; } @end