mvm_core_ui/MVMCoreUI/BaseControllers/MFScrollingViewController.m

352 lines
12 KiB
Objective-C

//
// MFScrollingViewController.m
// MobileFirstFramework
//
// Created by Pfeil, Scott Robert on 11/21/17.
// Copyright © 2017 Verizon Wireless. All rights reserved.
//
#import "MFScrollingViewController.h"
@import MVMCore.MVMCoreDispatchUtility;
@import MVMCore.MVMCoreGetterUtility;
@import MVMCore.MVMCoreLoadObject;
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreJSONConstants;
#import "NSLayoutConstraint+MFConvenience.h"
#import "MVMCoreUIUtility.h"
#import "MVMCoreUIConstants.h"
#import "MVMCoreUISession.h"
#import <MVMCoreUI/MVMCoreUI-Swift.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.navigationController && 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