Removing locally copyied TLYShyNavBar and replacing with reference copy

This commit is contained in:
Tony Nuzzi 2015-02-22 14:18:17 -06:00
parent ccd5229c78
commit 634226702b
11 changed files with 38 additions and 1000 deletions

View File

@ -1,16 +0,0 @@
//
// NSObject+TLYSwizzlingHelpers.h
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/23/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (TLYSwizzlingHelpers)
+ (void)tly_swizzleClassMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector;
+ (void)tly_swizzleInstanceMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector;
@end

View File

@ -1,28 +0,0 @@
//
// NSObject+TLYSwizzlingHelpers.m
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/23/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "NSObject+TLYSwizzlingHelpers.h"
#import <objc/runtime.h>
@implementation NSObject (TLYSwizzlingHelpers)
+ (void)tly_swizzleClassMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector
{
Method originalMethod = class_getClassMethod([self class], originalSelector);
Method replacementMethod = class_getClassMethod([self class], replacementSelector);
method_exchangeImplementations(replacementMethod, originalMethod);
}
+ (void)tly_swizzleInstanceMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector
{
Method originalMethod = class_getInstanceMethod([self class], originalSelector);
Method replacementMethod = class_getInstanceMethod([self class], replacementSelector);
method_exchangeImplementations(replacementMethod, originalMethod);
}
@end

View File

@ -1,25 +0,0 @@
//
// UIViewController+BetterLayoutGuides.h
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/21/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
/* CATEGORY DESCRIPTION:
* =====================
* Apparently, Apple messed up when they implemented autolayout
* somehow, so when we have child view controllers, they get wrong
* layout guides. This helps accomodate that problem.
*
* Courtesy of http://stackoverflow.com/questions/19140530/toplayoutguide-in-child-view-controller
*/
@interface UIViewController (BetterLayoutGuides)
@property (nonatomic, readonly) id<UILayoutSupport> tly_topLayoutGuide;
@property (nonatomic, readonly) id<UILayoutSupport> tly_bottomLayoutGuide;
@end

View File

@ -1,37 +0,0 @@
//
// UIViewController+BetterLayoutGuides.m
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/21/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "UIViewController+BetterLayoutGuides.h"
@implementation UIViewController (BetterLayoutGuides)
- (id<UILayoutSupport>)tly_topLayoutGuide
{
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class])
{
return self.parentViewController.tly_topLayoutGuide;
}
else {
return self.topLayoutGuide;
}
}
- (id<UILayoutSupport>)tly_bottomLayoutGuide
{
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class])
{
return self.parentViewController.tly_bottomLayoutGuide;
}
else {
return self.bottomLayoutGuide;
}
}
@end

View File

@ -1,25 +0,0 @@
//
// TLYDelegateProxy.h
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/27/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
/* CLASS DESCRIPTION:
* ==================
* Delegate proxy is meant to be used as a proxy between and
* object and its delegate. The DelegateProxy is initialized with a
* target and middle man, where the target is the original delegate
* and the middle-man is just an object we send identical messages to.
*/
@interface TLYDelegateProxy : NSProxy
@property (nonatomic, weak) id originalDelegate;
- (instancetype)initWithMiddleMan:(id)middleMan;
@end

View File

@ -1,74 +0,0 @@
//
// TLYDelegateProxy.m
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/27/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYDelegateProxy.h"
#import <objc/runtime.h>
@interface TLYDelegateProxy ()
@property (nonatomic, weak) id middleMan;
@end
@implementation TLYDelegateProxy
- (instancetype)initWithMiddleMan:(id)middleMan
{
if (self)
{
self.middleMan = middleMan;
}
return self;
}
- (NSInvocation *)_copyInvocation:(NSInvocation *)invocation
{
NSInvocation *copy = [NSInvocation invocationWithMethodSignature:[invocation methodSignature]];
NSUInteger argCount = [[invocation methodSignature] numberOfArguments];
for (int i = 0; i < argCount; i++)
{
char buffer[sizeof(intmax_t)];
[invocation getArgument:(void *)&buffer atIndex:i];
[copy setArgument:(void *)&buffer atIndex:i];
}
return copy;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
if ([self.middleMan respondsToSelector:invocation.selector])
{
NSInvocation *invocationCopy = [self _copyInvocation:invocation];
[invocationCopy invokeWithTarget:self.middleMan];
}
if ([self.originalDelegate respondsToSelector:invocation.selector])
{
[invocation invokeWithTarget:self.originalDelegate];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
id result = [self.originalDelegate methodSignatureForSelector:sel];
if (!result) {
result = [self.middleMan methodSignatureForSelector:sel];
}
return result;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return ([self.originalDelegate respondsToSelector:aSelector]
|| [self.middleMan respondsToSelector:aSelector]);
}
@end

View File

@ -1,77 +0,0 @@
//
// TLYShyNavBarManager.h
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/13/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/* CLASS DESCRIPTION:
* ==================
* Manages the relationship between a scrollView and a view
* controller. Must be instantiated and assigned the scrollView
* that drives the contraction/expansion, then assigned to the
* viewController that needs the functionality. Must be assigned
* throught the UIViewController category:
*
* viewController.shyNavManager = ...;
*
*/
@interface TLYShyNavBarManager : NSObject
/* The view controller that is part of the navigation stack
* IMPORTANT: Must have access to navigationController
*/
@property (nonatomic, readonly, weak) UIViewController *viewController;
/* The scrollView subclass that will drive the contraction/expansion
* IMPORTANT: set this property AFTER assigning its delegate, if needed!
*/
@property (nonatomic, weak) UIScrollView *scrollView;
/* The extension view to be shown beneath the navbar
*/
@property (nonatomic, strong) UIView *extensionView;
/* The container contains the extension view, if any. Exposed to
* allow the developer to adjust content offset as necessary.
*/
@property (nonatomic, readonly) CGRect extensionViewBounds;
/* Control the resistance when scrolling up/down before the navbar
* expands/contracts again.
*/
@property (nonatomic) CGFloat expansionResistance; // default 200
@property (nonatomic) CGFloat contractionResistance; // default 0
/* Turn on or off the alpha fade as the navbar contracts/expands.
* Defaults to YES
*/
@property (nonatomic, getter = isAlphaFadeEnabled) BOOL alphaFadeEnabled;
@property (nonatomic) BOOL disable;
@end
/* CATEGORY DESCRIPTION:
* =====================
* The category described in the TLYShyNavBarManager usage, and it
* simply uses associated objects to attatch a TLYShyNavBar to the
* designated view controller.
*
* We also perform some swizzling to pass notifications to the
* TLYShyNavBar. Things like, viewDidLayoutSubviews, viewWillAppear and
* Disappear, ... etc.
*/
@interface UIViewController (ShyNavBar)
/* Initially, this is nil, but created for you when you access it */
@property (nonatomic, strong) TLYShyNavBarManager *shyNavBarManager;
@end

View File

@ -1,452 +0,0 @@
//
// TLYShyNavBarManager.m
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/13/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYShyNavBarManager.h"
#import "TLYShyViewController.h"
#import "TLYDelegateProxy.h"
#import "UIViewController+BetterLayoutGuides.h"
#import "NSObject+TLYSwizzlingHelpers.h"
#import <objc/runtime.h>
#pragma mark - Helper functions
// Thanks to SO user, MattDiPasquale
// http://stackoverflow.com/questions/12991935/how-to-programmatically-get-ios-status-bar-height/16598350#16598350
static inline CGFloat AACStatusBarHeight()
{
if ([UIApplication sharedApplication].statusBarHidden)
{
return 0.f;
}
CGSize statusBarSize = [UIApplication sharedApplication].statusBarFrame.size;
return MIN(statusBarSize.width, statusBarSize.height);
}
@implementation UIScrollView(Helper)
// Modify contentInset and scrollIndicatorInsets while preserving visual content offset
- (void)tly_smartSetInsets:(UIEdgeInsets)contentAndScrollIndicatorInsets
{
if (contentAndScrollIndicatorInsets.top != self.contentInset.top)
{
CGPoint contentOffset = self.contentOffset;
contentOffset.y -= contentAndScrollIndicatorInsets.top - self.contentInset.top;
self.contentOffset = contentOffset;
}
self.contentInset = self.scrollIndicatorInsets = contentAndScrollIndicatorInsets;
}
@end
#pragma mark - TLYShyNavBarManager class
@interface TLYShyNavBarManager () <UIScrollViewDelegate>
@property (nonatomic, strong) TLYShyViewController *navBarController;
@property (nonatomic, strong) TLYShyViewController *extensionController;
@property (nonatomic, strong) TLYDelegateProxy *delegateProxy;
@property (nonatomic, strong) UIView *extensionViewContainer;
@property (nonatomic) UIEdgeInsets previousScrollInsets;
@property (nonatomic) CGFloat previousYOffset;
@property (nonatomic) CGFloat resistanceConsumed;
@property (nonatomic, getter = isContracting) BOOL contracting;
@property (nonatomic) BOOL previousContractionState;
@property (nonatomic, readonly) BOOL isViewControllerVisible;
@end
@implementation TLYShyNavBarManager
#pragma mark - Init & Dealloc
- (instancetype)init
{
self = [super init];
if (self)
{
self.delegateProxy = [[TLYDelegateProxy alloc] initWithMiddleMan:self];
self.contracting = NO;
self.previousContractionState = YES;
self.expansionResistance = 200.f;
self.contractionResistance = 0.f;
self.alphaFadeEnabled = YES;
self.previousScrollInsets = UIEdgeInsetsZero;
self.previousYOffset = NAN;
self.navBarController = [[TLYShyViewController alloc] init];
self.navBarController.hidesSubviews = YES;
self.navBarController.expandedCenter = ^(UIView *view)
{
return CGPointMake(CGRectGetMidX(view.bounds),
CGRectGetMidY(view.bounds) + AACStatusBarHeight());
};
self.navBarController.contractionAmount = ^(UIView *view)
{
return CGRectGetHeight(view.bounds);
};
self.extensionViewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.f, 0.f)];
self.extensionViewContainer.backgroundColor = [UIColor clearColor];
self.extensionViewContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleBottomMargin;
self.extensionController = [[TLYShyViewController alloc] init];
self.extensionController.view = self.extensionViewContainer;
self.extensionController.hidesAfterContraction = YES;
self.extensionController.contractionAmount = ^(UIView *view)
{
return CGRectGetHeight(view.bounds);
};
__weak __typeof(self) weakSelf = self;
self.extensionController.expandedCenter = ^(UIView *view)
{
return CGPointMake(CGRectGetMidX(view.bounds),
CGRectGetMidY(view.bounds) + weakSelf.viewController.tly_topLayoutGuide.length);
};
self.navBarController.child = self.extensionController;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
}
return self;
}
- (void)dealloc
{
// sanity check
if (_scrollView.delegate == _delegateProxy)
{
_scrollView.delegate = _delegateProxy.originalDelegate;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Properties
- (void)setViewController:(UIViewController *)viewController
{
_viewController = viewController;
UIView *navbar = viewController.navigationController.navigationBar;
NSAssert(navbar != nil, @"You are using the component wrong... Please see the README file.");
[self.extensionViewContainer removeFromSuperview];
[self.viewController.view addSubview:self.extensionViewContainer];
self.navBarController.view = navbar;
[self layoutViews];
}
- (void)setScrollView:(UIScrollView *)scrollView
{
if (_scrollView.delegate == self.delegateProxy)
{
_scrollView.delegate = self.delegateProxy.originalDelegate;
}
_scrollView = scrollView;
if (_scrollView.delegate != self.delegateProxy)
{
self.delegateProxy.originalDelegate = _scrollView.delegate;
_scrollView.delegate = (id)self.delegateProxy;
}
[self cleanup];
[self layoutViews];
}
- (CGRect)extensionViewBounds
{
return self.extensionViewContainer.bounds;
}
- (BOOL)isViewControllerVisible
{
return self.viewController.isViewLoaded && self.viewController.view.window;
}
- (void)setDisable:(BOOL)disable
{
if (disable == _disable)
{
return;
}
_disable = disable;
if (!disable) {
self.previousYOffset = self.scrollView.contentOffset.y;
}
}
#pragma mark - Private methods
- (BOOL)_shouldHandleScrolling
{
if (self.disable)
{
return NO;
}
CGRect scrollFrame = UIEdgeInsetsInsetRect(self.scrollView.bounds, self.scrollView.contentInset);
CGFloat scrollableAmount = self.scrollView.contentSize.height - CGRectGetHeight(scrollFrame);
BOOL scrollViewIsSuffecientlyLong = (scrollableAmount > self.navBarController.totalHeight);
return (self.isViewControllerVisible && scrollViewIsSuffecientlyLong);
}
- (void)_handleScrolling
{
if (![self _shouldHandleScrolling])
{
return;
}
if (!isnan(self.previousYOffset))
{
// 1 - Calculate the delta
CGFloat deltaY = (self.previousYOffset - self.scrollView.contentOffset.y);
// 2 - Ignore any scrollOffset beyond the bounds
CGFloat start = -self.scrollView.contentInset.top;
if (self.previousYOffset < start)
{
deltaY = MIN(0, deltaY - self.previousYOffset - start);
}
/* rounding to resolve a dumb issue with the contentOffset value */
CGFloat end = floorf(self.scrollView.contentSize.height - CGRectGetHeight(self.scrollView.bounds) + self.scrollView.contentInset.bottom - 0.5f);
if (self.previousYOffset > end && deltaY > 0)
{
deltaY = MAX(0, deltaY - self.previousYOffset + end);
}
// 3 - Update contracting variable
if (fabs(deltaY) > FLT_EPSILON)
{
self.contracting = deltaY < 0;
}
// 4 - Check if contracting state changed, and do stuff if so
if (self.isContracting != self.previousContractionState)
{
self.previousContractionState = self.isContracting;
self.resistanceConsumed = 0;
}
// 5 - Apply resistance
if (self.isContracting)
{
CGFloat availableResistance = self.contractionResistance - self.resistanceConsumed;
self.resistanceConsumed = MIN(self.contractionResistance, self.resistanceConsumed - deltaY);
deltaY = MIN(0, availableResistance + deltaY);
}
else if (self.scrollView.contentOffset.y > -AACStatusBarHeight())
{
CGFloat availableResistance = self.expansionResistance - self.resistanceConsumed;
self.resistanceConsumed = MIN(self.expansionResistance, self.resistanceConsumed + deltaY);
deltaY = MAX(0, deltaY - availableResistance);
}
// 6 - Update the shyViewController
self.navBarController.alphaFadeEnabled = self.alphaFadeEnabled;
[self.navBarController updateYOffset:deltaY];
}
self.previousYOffset = self.scrollView.contentOffset.y;
}
- (void)_handleScrollingEnded
{
if (!self.isViewControllerVisible)
{
return;
}
self.resistanceConsumed = 0;
CGFloat deltaY = [self.navBarController snap:self.isContracting];
CGPoint newContentOffset = self.scrollView.contentOffset;
newContentOffset.y -= deltaY;
[UIView animateWithDuration:0.2
animations:^{
self.scrollView.contentOffset = newContentOffset;
}];
}
#pragma mark - public methods
- (void)setExtensionView:(UIView *)view
{
if (view != _extensionView)
{
[_extensionView removeFromSuperview];
_extensionView = view;
CGRect bounds = view.frame;
bounds.origin = CGPointZero;
view.frame = bounds;
self.extensionViewContainer.frame = bounds;
[self.extensionViewContainer addSubview:view];
[self layoutViews];
}
}
- (void)prepareForDisplay
{
[self cleanup];
}
- (void)layoutViews
{
UIEdgeInsets scrollInsets = self.scrollView.contentInset;
scrollInsets.top = CGRectGetHeight(self.extensionViewContainer.bounds) + self.viewController.tly_topLayoutGuide.length;
if (UIEdgeInsetsEqualToEdgeInsets(scrollInsets, self.previousScrollInsets))
{
return;
}
self.previousScrollInsets = scrollInsets;
[self.navBarController expand];
[self.extensionViewContainer.superview bringSubviewToFront:self.extensionViewContainer];
[self.scrollView tly_smartSetInsets:scrollInsets];
}
- (void)cleanup
{
[self.navBarController expand];
self.previousYOffset = NAN;
self.previousScrollInsets = UIEdgeInsetsZero;
}
#pragma mark - UIScrollViewDelegate methods
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self _handleScrolling];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
[self _handleScrollingEnded];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self _handleScrollingEnded];
}
#pragma mark - NSNotificationCenter methods
- (void)applicationDidBecomeActive
{
[self.navBarController expand];
}
@end
#pragma mark - UIViewController+TLYShyNavBar category
static char shyNavBarManagerKey;
@implementation UIViewController (ShyNavBar)
#pragma mark - Static methods
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self tly_swizzleInstanceMethod:@selector(viewWillAppear:) withReplacement:@selector(tly_swizzledViewWillAppear:)];
[self tly_swizzleInstanceMethod:@selector(viewWillLayoutSubviews) withReplacement:@selector(tly_swizzledViewDidLayoutSubviews)];
[self tly_swizzleInstanceMethod:@selector(viewWillDisappear:) withReplacement:@selector(tly_swizzledViewWillDisappear:)];
});
}
#pragma mark - Swizzled View Life Cycle
- (void)tly_swizzledViewWillAppear:(BOOL)animated
{
[[self _internalShyNavBarManager] prepareForDisplay];
[self tly_swizzledViewWillAppear:animated];
}
- (void)tly_swizzledViewDidLayoutSubviews
{
[[self _internalShyNavBarManager] layoutViews];
[self tly_swizzledViewDidLayoutSubviews];
}
- (void)tly_swizzledViewWillDisappear:(BOOL)animated
{
[[self _internalShyNavBarManager] cleanup];
[self tly_swizzledViewWillDisappear:animated];
}
#pragma mark - Properties
- (void)setShyNavBarManager:(TLYShyNavBarManager *)shyNavBarManager
{
shyNavBarManager.viewController = self;
objc_setAssociatedObject(self, &shyNavBarManagerKey, shyNavBarManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (TLYShyNavBarManager *)shyNavBarManager
{
id shyNavBarManager = objc_getAssociatedObject(self, &shyNavBarManagerKey);
if (!shyNavBarManager)
{
shyNavBarManager = [[TLYShyNavBarManager alloc] init];
self.shyNavBarManager = shyNavBarManager;
}
return shyNavBarManager;
}
#pragma mark - Private methods
/* Internally, we need to access the variable without creating it */
- (TLYShyNavBarManager *)_internalShyNavBarManager
{
return objc_getAssociatedObject(self, &shyNavBarManagerKey);
}
@end

View File

@ -1,50 +0,0 @@
//
// TLYShyViewController.h
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/14/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
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.
*
* We are making some dangerous assumtions here!!! Most importantly,
* the TLYShyViewController can only be a maximum depth of 2. Adding a
* child to an already childified node is not supported.
*/
@interface TLYShyViewController : NSObject
@property (nonatomic, weak) 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;
@property (nonatomic) BOOL alphaFadeEnabled;
@property (nonatomic, readonly) CGFloat totalHeight;
- (CGFloat)updateYOffset:(CGFloat)deltaY;
- (CGFloat)snap:(BOOL)contract;
- (CGFloat)expand;
- (CGFloat)contract;
@end

View File

@ -1,181 +0,0 @@
//
// TLYShyViewController.m
// TLYShyNavBarDemo
//
// Created by Mazyad Alabduljaleel on 6/14/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYShyViewController.h"
const CGFloat contractionVelocity = 300.f;
@interface TLYShyViewController ()
@property (nonatomic) CGPoint expandedCenterValue;
@property (nonatomic) CGFloat contractionAmountValue;
@property (nonatomic) CGPoint contractedCenterValue;
@property (nonatomic, getter = isContracted) BOOL contracted;
@property (nonatomic, getter = isExpanded) BOOL expanded;
@end
@implementation TLYShyViewController
#pragma mark - Properties
// convenience
- (CGPoint)expandedCenterValue
{
return self.expandedCenter(self.view);
}
- (CGFloat)contractionAmountValue
{
return self.contractionAmount(self.view);
}
- (CGPoint)contractedCenterValue
{
return CGPointMake(self.expandedCenterValue.x, self.expandedCenterValue.y - self.contractionAmountValue);
}
- (BOOL)isContracted
{
return fabs(self.view.center.y - self.contractedCenterValue.y) < FLT_EPSILON;
}
- (BOOL)isExpanded
{
return fabs(self.view.center.y - self.expandedCenterValue.y) < FLT_EPSILON;
}
- (CGFloat)totalHeight
{
return self.child.totalHeight + (self.expandedCenterValue.y - self.contractedCenterValue.y);
}
#pragma mark - Private methods
// This method is courtesy of GTScrollNavigationBar
// https://github.com/luugiathuy/GTScrollNavigationBar
- (void)_updateSubviewsToAlpha:(CGFloat)alpha
{
for (UIView* view in self.view.subviews)
{
bool isBackgroundView = view == self.view.subviews[0];
bool isViewHidden = view.hidden || view.alpha < FLT_EPSILON;
if (!isBackgroundView && !isViewHidden)
{
view.alpha = alpha;
}
}
}
#pragma mark - Public methods
- (void)setAlphaFadeEnabled:(BOOL)alphaFadeEnabled
{
_alphaFadeEnabled = alphaFadeEnabled;
if (!alphaFadeEnabled)
{
[self _updateSubviewsToAlpha:1.f];
}
}
- (CGFloat)updateYOffset:(CGFloat)deltaY
{
if (self.child && deltaY < 0)
{
deltaY = [self.child updateYOffset:deltaY];
self.child.view.hidden = (deltaY) < 0;
}
CGFloat newYOffset = self.view.center.y + deltaY;
CGFloat newYCenter = MAX(MIN(self.expandedCenterValue.y, newYOffset), self.contractedCenterValue.y);
self.view.center = CGPointMake(self.expandedCenterValue.x, newYCenter);
if (self.hidesSubviews)
{
CGFloat newAlpha = 1.f - (self.expandedCenterValue.y - self.view.center.y) / self.contractionAmountValue;
newAlpha = MIN(MAX(FLT_EPSILON, newAlpha), 1.f);
if (self.alphaFadeEnabled)
{
[self _updateSubviewsToAlpha:newAlpha];
}
}
CGFloat residual = newYOffset - newYCenter;
if (self.child && deltaY > 0 && residual > 0)
{
residual = [self.child updateYOffset:residual];
self.child.view.hidden = residual - (newYOffset - newYCenter) > 0;
}
return residual;
}
- (CGFloat)snap:(BOOL)contract
{
/* "The Facebook" UX dictates that:
*
* 1 - When you contract:
* A - contract beyond the extension view -> contract the whole thing
* B - contract within the extension view -> expand the extension back
*
* 2 - When you expand:
* A - expand beyond the navbar -> expand the whole thing
* B - expand within the navbar -> contract the navbar back
*/
__block CGFloat deltaY;
[UIView animateWithDuration:0.2 animations:^
{
if ((contract && self.child.isContracted) || (!contract && !self.isExpanded))
{
deltaY = [self contract];
}
else
{
deltaY = [self.child expand];
}
}];
return deltaY;
}
- (CGFloat)expand
{
self.view.hidden = NO;
if (self.hidesSubviews && self.alphaFadeEnabled)
{
[self _updateSubviewsToAlpha:1.f];
}
CGFloat amountToMove = self.expandedCenterValue.y - self.view.center.y;
self.view.center = self.expandedCenterValue;
[self.child expand];
return amountToMove;
}
- (CGFloat)contract
{
CGFloat amountToMove = self.contractedCenterValue.y - self.view.center.y;
self.view.center = self.contractedCenterValue;
[self.child contract];
return amountToMove;
}
@end

View File

@ -12,12 +12,12 @@
E7ADE6171A99A83B00E8F95C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7ADE6161A99A83B00E8F95C /* Images.xcassets */; };
E7ADE61A1A99A83B00E8F95C /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7ADE6181A99A83B00E8F95C /* LaunchScreen.xib */; };
E7ADE6261A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6251A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift */; };
E7ADE63B1A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6321A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */; };
E7ADE63C1A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6341A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m */; };
E7ADE63D1A99A84C00E8F95C /* TLYDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6361A99A84C00E8F95C /* TLYDelegateProxy.m */; };
E7ADE63E1A99A84C00E8F95C /* TLYShyNavBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6381A99A84C00E8F95C /* TLYShyNavBarManager.m */; };
E7ADE63F1A99A84C00E8F95C /* TLYShyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE63A1A99A84C00E8F95C /* TLYShyViewController.m */; };
E7ADE6421A99AB5800E8F95C /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6411A99AB5800E8F95C /* TableViewController.swift */; };
E7ADE64F1A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6461A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */; };
E7ADE6501A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE6481A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.m */; };
E7ADE6511A9A6F7C00E8F95C /* TLYDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE64A1A9A6F7C00E8F95C /* TLYDelegateProxy.m */; };
E7ADE6521A9A6F7C00E8F95C /* TLYShyNavBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE64C1A9A6F7C00E8F95C /* TLYShyNavBarManager.m */; };
E7ADE6531A9A6F7C00E8F95C /* TLYShyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE64E1A9A6F7C00E8F95C /* TLYShyViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -40,18 +40,18 @@
E7ADE61F1A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TLYShyNavBarSwiftDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
E7ADE6241A99A83B00E8F95C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E7ADE6251A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TLYShyNavBarSwiftDemoTests.swift; sourceTree = "<group>"; };
E7ADE6311A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+TLYSwizzlingHelpers.h"; sourceTree = "<group>"; };
E7ADE6321A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+TLYSwizzlingHelpers.m"; sourceTree = "<group>"; };
E7ADE6331A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+BetterLayoutGuides.h"; sourceTree = "<group>"; };
E7ADE6341A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+BetterLayoutGuides.m"; sourceTree = "<group>"; };
E7ADE6351A99A84C00E8F95C /* TLYDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYDelegateProxy.h; sourceTree = "<group>"; };
E7ADE6361A99A84C00E8F95C /* TLYDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYDelegateProxy.m; sourceTree = "<group>"; };
E7ADE6371A99A84C00E8F95C /* TLYShyNavBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyNavBarManager.h; sourceTree = "<group>"; };
E7ADE6381A99A84C00E8F95C /* TLYShyNavBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyNavBarManager.m; sourceTree = "<group>"; };
E7ADE6391A99A84C00E8F95C /* TLYShyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyViewController.h; sourceTree = "<group>"; };
E7ADE63A1A99A84C00E8F95C /* TLYShyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyViewController.m; sourceTree = "<group>"; };
E7ADE6401A99A86600E8F95C /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
E7ADE6411A99AB5800E8F95C /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = "<group>"; };
E7ADE6451A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+TLYSwizzlingHelpers.h"; sourceTree = "<group>"; };
E7ADE6461A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+TLYSwizzlingHelpers.m"; sourceTree = "<group>"; };
E7ADE6471A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+BetterLayoutGuides.h"; sourceTree = "<group>"; };
E7ADE6481A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+BetterLayoutGuides.m"; sourceTree = "<group>"; };
E7ADE6491A9A6F7C00E8F95C /* TLYDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYDelegateProxy.h; sourceTree = "<group>"; };
E7ADE64A1A9A6F7C00E8F95C /* TLYDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYDelegateProxy.m; sourceTree = "<group>"; };
E7ADE64B1A9A6F7C00E8F95C /* TLYShyNavBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyNavBarManager.h; sourceTree = "<group>"; };
E7ADE64C1A9A6F7C00E8F95C /* TLYShyNavBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyNavBarManager.m; sourceTree = "<group>"; };
E7ADE64D1A9A6F7C00E8F95C /* TLYShyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyViewController.h; sourceTree = "<group>"; };
E7ADE64E1A9A6F7C00E8F95C /* TLYShyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -76,7 +76,7 @@
isa = PBXGroup;
children = (
E7ADE6401A99A86600E8F95C /* Bridging-Header.h */,
E7ADE62F1A99A84C00E8F95C /* TLYShyNavBar */,
E7ADE6431A9A6F7C00E8F95C /* TLYShyNavBar */,
E7ADE60C1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */,
E7ADE6221A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests */,
E7ADE60B1A99A83B00E8F95C /* Products */,
@ -130,27 +130,28 @@
name = "Supporting Files";
sourceTree = "<group>";
};
E7ADE62F1A99A84C00E8F95C /* TLYShyNavBar */ = {
E7ADE6431A9A6F7C00E8F95C /* TLYShyNavBar */ = {
isa = PBXGroup;
children = (
E7ADE6301A99A84C00E8F95C /* Categories */,
E7ADE6351A99A84C00E8F95C /* TLYDelegateProxy.h */,
E7ADE6361A99A84C00E8F95C /* TLYDelegateProxy.m */,
E7ADE6371A99A84C00E8F95C /* TLYShyNavBarManager.h */,
E7ADE6381A99A84C00E8F95C /* TLYShyNavBarManager.m */,
E7ADE6391A99A84C00E8F95C /* TLYShyViewController.h */,
E7ADE63A1A99A84C00E8F95C /* TLYShyViewController.m */,
E7ADE6441A9A6F7C00E8F95C /* Categories */,
E7ADE6491A9A6F7C00E8F95C /* TLYDelegateProxy.h */,
E7ADE64A1A9A6F7C00E8F95C /* TLYDelegateProxy.m */,
E7ADE64B1A9A6F7C00E8F95C /* TLYShyNavBarManager.h */,
E7ADE64C1A9A6F7C00E8F95C /* TLYShyNavBarManager.m */,
E7ADE64D1A9A6F7C00E8F95C /* TLYShyViewController.h */,
E7ADE64E1A9A6F7C00E8F95C /* TLYShyViewController.m */,
);
path = TLYShyNavBar;
name = TLYShyNavBar;
path = ../TLYShyNavBar;
sourceTree = "<group>";
};
E7ADE6301A99A84C00E8F95C /* Categories */ = {
E7ADE6441A9A6F7C00E8F95C /* Categories */ = {
isa = PBXGroup;
children = (
E7ADE6311A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */,
E7ADE6321A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */,
E7ADE6331A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.h */,
E7ADE6341A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m */,
E7ADE6451A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */,
E7ADE6461A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */,
E7ADE6471A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.h */,
E7ADE6481A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.m */,
);
path = Categories;
sourceTree = "<group>";
@ -255,13 +256,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E7ADE63F1A99A84C00E8F95C /* TLYShyViewController.m in Sources */,
E7ADE63C1A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m in Sources */,
E7ADE6531A9A6F7C00E8F95C /* TLYShyViewController.m in Sources */,
E7ADE6421A99AB5800E8F95C /* TableViewController.swift in Sources */,
E7ADE6501A9A6F7C00E8F95C /* UIViewController+BetterLayoutGuides.m in Sources */,
E7ADE6101A99A83B00E8F95C /* AppDelegate.swift in Sources */,
E7ADE63D1A99A84C00E8F95C /* TLYDelegateProxy.m in Sources */,
E7ADE63E1A99A84C00E8F95C /* TLYShyNavBarManager.m in Sources */,
E7ADE63B1A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m in Sources */,
E7ADE6511A9A6F7C00E8F95C /* TLYDelegateProxy.m in Sources */,
E7ADE6521A9A6F7C00E8F95C /* TLYShyNavBarManager.m in Sources */,
E7ADE64F1A9A6F7C00E8F95C /* NSObject+TLYSwizzlingHelpers.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -455,6 +456,7 @@
E7ADE62B1A99A83B00E8F95C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E7ADE62C1A99A83B00E8F95C /* Build configuration list for PBXNativeTarget "TLYShyNavBarSwiftDemoTests" */ = {
isa = XCConfigurationList;
@ -463,6 +465,7 @@
E7ADE62E1A99A83B00E8F95C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};