From e4ffe134c3094fece6778cb447a545b66745a525 Mon Sep 17 00:00:00 2001 From: Mazyad Alabduljaleel Date: Sun, 15 Jun 2014 15:10:36 +0400 Subject: [PATCH] Another code design change.. [NEW]: make use of the parent/child relationship to propagate the deltaY down the hierarchy! --- TLYShyNavBar/TLYShyNavBarController.h | 2 + TLYShyNavBar/TLYShyNavBarController.m | 67 +++++++++++++-------------- TLYShyNavBar/TLYShyViewController.h | 15 +++++- TLYShyNavBar/TLYShyViewController.m | 43 +++++++++++++---- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/TLYShyNavBar/TLYShyNavBarController.h b/TLYShyNavBar/TLYShyNavBarController.h index 0508b14..8b81f36 100644 --- a/TLYShyNavBar/TLYShyNavBarController.h +++ b/TLYShyNavBar/TLYShyNavBarController.h @@ -21,6 +21,8 @@ @property (nonatomic, weak) UIScrollView *scrollView; - (void)addExtensionView:(UIView *)view; + +- (void)layoutViews; - (void)scrollViewDidEndScrolling; @end diff --git a/TLYShyNavBar/TLYShyNavBarController.m b/TLYShyNavBar/TLYShyNavBarController.m index d61d42c..d09c491 100644 --- a/TLYShyNavBar/TLYShyNavBarController.m +++ b/TLYShyNavBar/TLYShyNavBarController.m @@ -21,7 +21,6 @@ static inline CGFloat AACStatusBarHeight() @interface TLYShyNavBarManager () -@property (nonatomic, strong) TLYShyViewController *extensionController; @property (nonatomic, strong) TLYShyViewController *navBarController; @property (nonatomic, strong) UIView *extensionViewsContainer; @@ -34,6 +33,8 @@ static inline CGFloat AACStatusBarHeight() @implementation TLYShyNavBarManager +#pragma mark - Init & Dealloc + - (instancetype)init { self = [super init]; @@ -57,20 +58,21 @@ static inline CGFloat AACStatusBarHeight() self.extensionViewsContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.f, 0.f)]; self.extensionViewsContainer.backgroundColor = [UIColor clearColor]; - self.extensionController = [[TLYShyViewController alloc] init]; - self.extensionController.hidesAfterContraction = YES; - self.extensionController.view = self.extensionViewsContainer; - self.extensionController.contractionAmount = ^(UIView *view) + TLYShyViewController *extensionController = [[TLYShyViewController alloc] init]; + extensionController.view = self.extensionViewsContainer; + extensionController.contractionAmount = ^(UIView *view) { return CGRectGetHeight(view.bounds); }; __weak typeof(self) weakSelf = self; - self.extensionController.expandedCenter = ^(UIView *view) + extensionController.expandedCenter = ^(UIView *view) { return CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds) + weakSelf.viewController.topLayoutGuide.length); }; + + self.navBarController.child = extensionController; } return self; } @@ -82,13 +84,6 @@ static inline CGFloat AACStatusBarHeight() #pragma mark - Properties -- (void)addExtensionView:(UIView *)view -{ - // TODO: expand the container instead of just adding it on top - self.extensionViewsContainer.frame = view.bounds; - [self.extensionViewsContainer addSubview:view]; -} - - (void)setViewController:(UIViewController *)viewController { _viewController = viewController; @@ -98,7 +93,7 @@ static inline CGFloat AACStatusBarHeight() self.navBarController.view = viewController.navigationController.navigationBar; - [self _layoutViews]; + [self layoutViews]; } - (void)setScrollView:(UIScrollView *)scrollView @@ -110,17 +105,6 @@ static inline CGFloat AACStatusBarHeight() #pragma mark - Private methods -- (void)_layoutViews -{ - [self.extensionController expand]; - - UIEdgeInsets scrollInsets = self.scrollView.contentInset; - scrollInsets.top = CGRectGetHeight(self.extensionViewsContainer.bounds) + self.viewController.topLayoutGuide.length; - - self.scrollView.contentInset = scrollInsets; - self.scrollView.scrollIndicatorInsets = scrollInsets; -} - - (void)_handleScrolling { if (!isnan(self.previousYOffset)) @@ -145,8 +129,7 @@ static inline CGFloat AACStatusBarHeight() self.isContracting = deltaY < 0; } - deltaY = [(self.isContracting ? self.extensionController : self.navBarController) updateYOffset:deltaY]; - [(self.isContracting ? self.navBarController : self.extensionController) updateYOffset:deltaY]; + [self.navBarController updateYOffset:deltaY]; } self.previousYOffset = self.scrollView.contentOffset.y; @@ -154,16 +137,10 @@ static inline CGFloat AACStatusBarHeight() - (void)_handleScrollingEnded { - TLYShyViewController *first = (self.isContracting ? self.extensionController : self.navBarController); - TLYShyViewController *second = (self.isContracting ? self.navBarController : self.extensionController); - NSTimeInterval duration = 0; CGFloat deltaY = 0; - deltaY = [first snap:self.isContracting afterDelay:duration]; - duration = fabs(deltaY/contractionVelocity); - - deltaY += [second snap:self.isContracting afterDelay:duration]; + deltaY = [self.navBarController snap:self.isContracting afterDelay:duration]; duration = fabs(deltaY/contractionVelocity); CGPoint newContentOffset = self.scrollView.contentOffset; @@ -175,11 +152,33 @@ static inline CGFloat AACStatusBarHeight() }]; } +#pragma mark - public methods + +- (void)layoutViews +{ + [self.navBarController expand]; + + UIEdgeInsets scrollInsets = self.scrollView.contentInset; + scrollInsets.top = CGRectGetHeight(self.extensionViewsContainer.bounds) + self.viewController.topLayoutGuide.length; + + self.scrollView.contentInset = scrollInsets; + self.scrollView.scrollIndicatorInsets = scrollInsets; +} + +- (void)addExtensionView:(UIView *)view +{ + // TODO: expand the container instead of just adding it on top + self.extensionViewsContainer.frame = view.bounds; + [self.extensionViewsContainer addSubview:view]; +} + - (void)scrollViewDidEndScrolling { [self _handleScrollingEnded]; } +#pragma mark - KVO methods + - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:NSStringFromSelector(@selector(contentOffset))]) diff --git a/TLYShyNavBar/TLYShyViewController.h b/TLYShyNavBar/TLYShyViewController.h index 97e0428..271551c 100644 --- a/TLYShyNavBar/TLYShyViewController.h +++ b/TLYShyNavBar/TLYShyViewController.h @@ -13,15 +13,28 @@ extern const CGFloat contractionVelocity; typedef CGPoint(^TLYShyViewControllerExpandedCenterBlock)(UIView *view); typedef CGFloat(^TLYShyViewControllerContractionAmountBlock)(UIView *view); +/* CLASS DESCRIPTION: + * ================== + * A shy view is a view that contracts when a scrolling event is + * triggered. We use this class to control the operations we perform on + * the shy view. + * + * ShyViewControllers can have a child, which gets the same offset + * updates as its parent. The use for this is to implement a drawer like + * functionality. When a parent is contracted/expanded, we want the child + * which is beneath the view to move the same amount so it remains hidden. + */ + @interface TLYShyViewController : NSObject +@property (nonatomic, strong) TLYShyViewController *child; + @property (nonatomic, weak) UIView *view; @property (nonatomic, copy) TLYShyViewControllerExpandedCenterBlock expandedCenter; @property (nonatomic, copy) TLYShyViewControllerContractionAmountBlock contractionAmount; @property (nonatomic) BOOL hidesSubviews; -@property (nonatomic) BOOL hidesAfterContraction; - (CGFloat)updateYOffset:(CGFloat)deltaY; diff --git a/TLYShyNavBar/TLYShyViewController.m b/TLYShyNavBar/TLYShyViewController.m index 21bc7a3..07cdbfe 100644 --- a/TLYShyNavBar/TLYShyViewController.m +++ b/TLYShyNavBar/TLYShyViewController.m @@ -53,30 +53,42 @@ const CGFloat contractionVelocity = 140.f; } } -- (CGFloat)updateYOffset:(CGFloat)deltaY +- (CGFloat)_updateYOffset:(CGFloat)deltaY overrideLimit:(BOOL)override { + if (self.child && deltaY < 0) + { + deltaY = [self.child updateYOffset:deltaY]; + } + CGFloat newYOffset = self.view.center.y + deltaY; - CGFloat newYCenter = MAX(MIN(self.expandedCenterValue.y, newYOffset), self.contractedCenterValue.y); + CGFloat newYCenter = override ? newYOffset : MAX(MIN(self.expandedCenterValue.y, newYOffset), self.contractedCenterValue.y); self.view.center = CGPointMake(self.expandedCenterValue.x, newYCenter); - CGFloat newAlpha = 1.f - (self.expandedCenterValue.y - self.view.center.y) / self.contractionAmountValue; - newAlpha = MIN(MAX(FLT_EPSILON, newAlpha), 1.f); - if (self.hidesSubviews) { + CGFloat newAlpha = 1.f - (self.expandedCenterValue.y - self.view.center.y) / self.contractionAmountValue; + newAlpha = MIN(MAX(FLT_EPSILON, newAlpha), 1.f); + [self _updateSubviewsToAlpha:newAlpha]; } - if (self.hidesAfterContraction) + CGFloat residual = newYOffset - newYCenter; + self.child.view.center = CGPointMake(self.child.view.center.x, self.child.view.center.y + deltaY - residual); + + if (self.child && deltaY > 0) { - self.view.alpha = fabs(newYCenter - self.contractedCenterValue.y) < FLT_EPSILON ? 0.f : 1.f; + residual = [self.child updateYOffset:residual]; } - CGFloat residual = newYOffset - newYCenter; return residual; } +- (CGFloat)updateYOffset:(CGFloat)deltaY +{ + return [self _updateYOffset:deltaY overrideLimit:NO]; +} + - (CGFloat)snap:(BOOL)contract afterDelay:(NSTimeInterval)delay { CGFloat newYCenter = (contract @@ -84,8 +96,21 @@ const CGFloat contractionVelocity = 140.f; : self.expandedCenterValue.y); CGFloat deltaY = newYCenter - self.view.center.y; + CGFloat duration = fabs(deltaY/contractionVelocity); - [UIView animateWithDuration:fabs(deltaY/contractionVelocity) + if (contract) + { + CGFloat childDelta = [self.child snap:contract afterDelay:delay]; + delay = fabs(childDelta/contractionVelocity); + + deltaY += childDelta; + } + else + { + deltaY += [self.child snap:contract afterDelay:delay+duration]; + } + + [UIView animateWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{