Compare commits

...

5 Commits

Author SHA1 Message Date
Mazyad Alabduljaleel 30ed689dc9 [FIX]: make sure we track status bar frame changes 2014-11-04 17:03:32 +04:00
Mazyad Alabduljaleel e658e03c0a One more shitty hack to get some bug out of the way
When the app recovers from a fullscreen window overlay, the frame of the navigation bar isn't updated properly, and causes some weird issues with the navbar, so we hard code the navbar height for now, and see what happens
2014-11-04 17:01:31 +04:00
Mazyad Alabduljaleel 116ff631cc better fix 2014-10-30 16:16:20 +04:00
Mazyad Alabduljaleel f6fe4fd020 [FIX]: support translucent and opaque bars, and fix issue on iOS 7 2014-10-30 16:02:50 +04:00
Mazyad Alabduljaleel 613e74ceb6 [NEW]: revamped the library to support opaque bars, but now there are issues with orientation and translucency >_< 2014-10-28 22:47:21 +04:00
13 changed files with 630 additions and 293 deletions

View File

@ -0,0 +1,22 @@
//
// TLYBoundsShyController.h
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYShyController.h"
/** We need the scroll view to undo the content offset */
typedef void(^TLYShyViewControllerCancelScrollBlock)(CGFloat deltaY);
typedef UINavigationBar *(^TLYShyViewControllerNavbarBlock)();
/** Reacts to the scroll deltas by manipulating the bounds
*/
@interface TLYBoundsShyController : TLYShyController
@property (nonatomic, copy) TLYShyViewControllerCancelScrollBlock cancelScrollBlock;
@property (nonatomic, copy) TLYShyViewControllerNavbarBlock navbarBlock;
@end

View File

@ -0,0 +1,101 @@
//
// TLYBoundsShyController.m
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYBoundsShyController.h"
#import "TLYShyNavBarManager.h"
@interface TLYBoundsShyController ()
@property (nonatomic, readonly) CGFloat initialViewHeight;
@end
@implementation TLYBoundsShyController
#pragma mark - Properties
- (BOOL)isExpanded
{
return fabs(CGRectGetHeight(self.view.bounds) - self.initialViewHeight) < FLT_EPSILON;
}
- (BOOL)isContracted
{
return fabs(CGRectGetHeight(self.view.bounds) - self.initialViewHeight - self.navbarHeight) < FLT_EPSILON;
}
- (CGFloat)initialViewHeight
{
// Very bad assumption, but I know no other way :(
return (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)
? CGRectGetWidth([UIScreen mainScreen].bounds)
: CGRectGetHeight([UIScreen mainScreen].bounds));
}
- (CGFloat)phase
{
CGFloat phase = 1.f - (CGRectGetHeight(self.view.bounds) - self.initialViewHeight) / self.navbarHeight;
return MIN(MAX(0.f, phase), 1.f);
}
- (CGFloat)totalHeight
{
return self.navbarHeight + self.child.totalHeight;
}
- (CGFloat)navbarHeight
{
return CGRectGetHeight(self.navbarBlock().bounds);
}
#pragma mark - Public methods
- (void)updateSubviewsToAlpha:(CGFloat)alpha
{
UINavigationBar *navbar = self.navbarBlock();
[navbar updateSubviewsToAlpha:alpha];
}
- (CGFloat)performUpdateForDelta:(CGFloat)deltaY
{
CGFloat heightDelta = CGRectGetHeight(self.view.bounds) - self.initialViewHeight;
CGFloat newDelta = MIN(heightDelta, MAX(heightDelta - self.navbarHeight, deltaY));
CGRect navFrame = UIEdgeInsetsInsetRect(self.view.frame, UIEdgeInsetsMake(newDelta, 0, 0, 0));
self.cancelScrollBlock(newDelta);
self.view.frame = navFrame;
CGFloat residual = deltaY - newDelta;
return residual;
}
- (CGFloat)expand
{
[super expand];
/* To "expand" the nav bar, we shrink the nav view */
CGFloat amountToShrink = CGRectGetHeight(self.view.bounds) - self.initialViewHeight;
self.view.frame = UIEdgeInsetsInsetRect(self.view.frame, UIEdgeInsetsMake(amountToShrink, 0, 0, 0));
return amountToShrink;
}
- (CGFloat)contract
{
[super contract];
/* to "contract" the nav bar, we expand the nav view */
CGFloat amountToExpand = CGRectGetHeight(self.view.bounds) - self.initialViewHeight - self.navbarHeight;
self.view.frame = UIEdgeInsetsInsetRect(self.view.frame, UIEdgeInsetsMake(amountToExpand, 0, 0, 0));
return amountToExpand;
}
@end

View File

@ -0,0 +1,21 @@
//
// TLYOffsetShyController.h
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYShyController.h"
typedef CGPoint(^TLYShyViewControllerExpandedCenterBlock)(UIView *view);
typedef CGFloat(^TLYShyViewControllerContractionAmountBlock)(UIView *view);
/** Reacts to the scroll deltas by manipulating the offset
*/
@interface TLYOffsetShyController : TLYShyController
@property (nonatomic, copy) TLYShyViewControllerExpandedCenterBlock expandedCenter;
@property (nonatomic, copy) TLYShyViewControllerContractionAmountBlock contractionAmount;
@end

View File

@ -0,0 +1,122 @@
//
// TLYOffsetShyController.m
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYOffsetShyController.h"
@interface TLYOffsetShyController ()
@property (nonatomic) CGPoint expandedCenterValue;
@property (nonatomic) CGFloat contractionAmountValue;
@property (nonatomic) CGPoint contractedCenterValue;
@end
@implementation TLYOffsetShyController
#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)phase
{
CGFloat phase = 1.f - (self.expandedCenterValue.y - self.view.center.y) / self.contractionAmountValue;
return MIN(MAX(0.f, phase), 1.f);
}
- (CGFloat)totalHeight
{
return self.child.totalHeight + (self.expandedCenterValue.y - self.contractedCenterValue.y);
}
#pragma mark - Public methods
- (void)updateSubviewsToAlpha:(CGFloat)alpha
{
if ([self.view respondsToSelector:@selector(updateSubviewsToAlpha:)])
{
[(id)self.view updateSubviewsToAlpha:alpha];
}
else
{
self.view.alpha = alpha;
}
}
- (CGFloat)performUpdateForDelta:(CGFloat)deltaY
{
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);
CGFloat residual = newYOffset - newYCenter;
return residual;
}
- (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];
#warning HACK - one more hack :(
if ([self.view isKindOfClass:[UINavigationBar class]]) {
CGRect newFrame = self.view.frame;
newFrame.size.height = MAX(44.f, newFrame.size.height);
self.view.frame = newFrame;
}
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

@ -0,0 +1,49 @@
//
// TLYShyController.h
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const CGFloat contractionVelocity;
/** A shy controller manages a shy object. Shy objects are any
* kind of object that will receive scroll deltas and respond
* to them in some way.
*
* Currently, there are two ShyControllers:
* 1- TranslationShyController: uses the delta to perform translations
* 2- BoundsShyController: uses the delta to update the bounds
*/
@interface TLYShyController : NSObject
@property (nonatomic, weak) TLYShyController *child;
@property (nonatomic, weak) UIView *view;
@property (nonatomic) BOOL hidesSubviews;
@property (nonatomic) BOOL hidesAfterContraction;
@property (nonatomic) BOOL alphaFadeEnabled;
@property (nonatomic, getter = isContracted) BOOL contracted;
@property (nonatomic, getter = isExpanded) BOOL expanded;
@property (nonatomic, readonly) CGFloat phase;
@property (nonatomic, readonly) CGFloat totalHeight;
- (CGFloat)updateYOffset:(CGFloat)deltaY;
/** PROTECTED */
- (CGFloat)performUpdateForDelta:(CGFloat)deltaY;
- (void)updateSubviewsToAlpha:(CGFloat)alpha;
/** PROTECTED */
- (CGFloat)snap:(BOOL)contract;
- (CGFloat)expand;
- (CGFloat)contract;
@end

View File

@ -0,0 +1,133 @@
//
// TLYShyController.m
// Telly
//
// Created by Mazyad Alabduljaleel on 10/28/14.
// Copyright (c) 2014 Telly, Inc. All rights reserved.
//
#import "TLYShyController.h"
const CGFloat contractionVelocity = 300.f;
@implementation TLYShyController
#pragma mark - Properties
- (BOOL)isContracted
{
[self _throwAbstractMethodException];
return NO;
}
- (BOOL)isExpanded
{
[self _throwAbstractMethodException];
return NO;
}
- (CGFloat)totalHeight
{
[self _throwAbstractMethodException];
return 0xBAD;
}
#pragma mark - Private methods
- (void)updateSubviewsToAlpha:(CGFloat)alpha
{
[self _throwAbstractMethodException];
}
- (void)_throwAbstractMethodException
{
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"Abstract method not implemented"
userInfo:nil];
}
#pragma mark - Public methods
- (void)setAlphaFadeEnabled:(BOOL)alphaFadeEnabled
{
_alphaFadeEnabled = alphaFadeEnabled;
if (!alphaFadeEnabled)
{
[self updateSubviewsToAlpha:1.f];
}
}
- (CGFloat)performUpdateForDelta:(CGFloat)deltaY
{
[self _throwAbstractMethodException];
return 0.f;
}
- (CGFloat)updateYOffset:(CGFloat)deltaY
{
if (self.child && deltaY < 0)
{
deltaY = [self.child updateYOffset:deltaY];
self.child.view.hidden = deltaY < 0.f;
}
CGFloat residual = [self performUpdateForDelta:deltaY];
if (self.hidesSubviews)
{
if (self.alphaFadeEnabled)
{
[self updateSubviewsToAlpha:self.phase];
}
}
if (self.child && deltaY > 0 && residual > 0)
{
residual = [self.child updateYOffset:residual];
self.child.view.hidden = residual < 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
{
return [self.child expand];
}
- (CGFloat)contract
{
return [self.child contract];
}
@end

View File

@ -8,14 +8,11 @@
#import <Foundation/Foundation.h>
/* CLASS DESCRIPTION:
* ==================
* Delegate proxy is meant to be used as a proxy between and
/** 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;

View File

@ -8,9 +8,9 @@
#import <Foundation/Foundation.h>
/* CLASS DESCRIPTION:
* ==================
* Manages the relationship between a scrollView and a view
extern CGFloat tly_AACStatusBarHeight(void);
/** 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
@ -19,7 +19,6 @@
* viewController.shyNavManager = ...;
*
*/
@interface TLYShyNavBarManager : NSObject
/* The view controller that is part of the navigation stack
@ -55,9 +54,7 @@
@end
/* CATEGORY DESCRIPTION:
* =====================
* The category described in the TLYShyNavBarManager usage, and it
/** The category described in the TLYShyNavBarManager usage, and it
* simply uses associated objects to attatch a TLYShyNavBar to the
* designated view controller.
*
@ -65,10 +62,20 @@
* 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
/** Simple category on UINavigationBar to update the subview's alpha
*/
@interface UINavigationBar (ShyNavBar)
// This method is courtesy of GTScrollNavigationBar
// https://github.com/luugiathuy/GTScrollNavigationBar
- (void)updateSubviewsToAlpha:(CGFloat)alpha;
@end

View File

@ -7,7 +7,8 @@
//
#import "TLYShyNavBarManager.h"
#import "TLYShyViewController.h"
#import "TLYOffsetShyController.h"
#import "TLYBoundsShyController.h"
#import "TLYDelegateProxy.h"
#import "UIViewController+BetterLayoutGuides.h"
@ -20,7 +21,7 @@
// Thanks to SO user, MattDiPasquale
// http://stackoverflow.com/questions/12991935/how-to-programmatically-get-ios-status-bar-height/16598350#16598350
static inline CGFloat AACStatusBarHeight()
CGFloat tly_AACStatusBarHeight(void)
{
if ([UIApplication sharedApplication].statusBarHidden)
{
@ -35,8 +36,11 @@ static inline CGFloat AACStatusBarHeight()
@interface TLYShyNavBarManager () <UIScrollViewDelegate>
@property (nonatomic, strong) TLYShyViewController *navBarController;
@property (nonatomic, strong) TLYShyViewController *extensionController;
@property (nonatomic, unsafe_unretained) TLYShyController *navBarController;
@property (nonatomic, strong) TLYShyController *translucentNavBarController;
@property (nonatomic, strong) TLYShyController *opaqueNavBarController;
@property (nonatomic, strong) TLYShyController *extensionController;
@property (nonatomic, strong) TLYDelegateProxy *delegateProxy;
@ -75,41 +79,75 @@ static inline CGFloat AACStatusBarHeight()
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.opaqueNavBarController = ({
TLYBoundsShyController *shyController = [[TLYBoundsShyController alloc] init];
shyController.hidesSubviews = YES;
shyController.cancelScrollBlock = ^(CGFloat deltaY)
{
UIScrollView *scrollView = weakSelf.scrollView;
id delegate = scrollView.delegate;
scrollView.delegate = nil;
scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y + deltaY);
scrollView.delegate = (id)delegate;
};
shyController.navbarBlock = ^
{
return weakSelf.viewController.navigationController.navigationBar;
};
shyController;
});
self.translucentNavBarController = ({
TLYOffsetShyController *shyController = [[TLYOffsetShyController alloc] init];
shyController.hidesSubviews = YES;
shyController.contractionAmount = ^(UIView *view)
{
return CGRectGetHeight(view.bounds);
};
shyController.expandedCenter = ^(UIView *view)
{
return CGPointMake(CGRectGetMidX(view.bounds),
CGRectGetMidY(view.bounds) + tly_AACStatusBarHeight());
};
shyController;
});
self.extensionViewContainer = ({
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.f, 0.f)];
view.backgroundColor = [UIColor clearColor];
view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleBottomMargin;
view;
});
self.extensionController = ({
TLYOffsetShyController *shyController = [[TLYOffsetShyController alloc] init];
shyController.view = self.extensionViewContainer;
shyController.hidesAfterContraction = YES;
shyController.contractionAmount = ^(UIView *view)
{
return CGRectGetHeight(view.bounds);
};
shyController.expandedCenter = ^(UIView *view)
{
UIView *navbar = weakSelf.viewController.navigationController.navigationBar;
return CGPointMake(CGRectGetMidX(view.bounds),
CGRectGetMidY(view.bounds) + CGRectGetHeight(navbar.bounds) + tly_AACStatusBarHeight());
};
shyController;
});
self.navBarController.child = self.extensionController;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeStatusBarFrame) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
return self;
}
@ -131,13 +169,26 @@ static inline CGFloat AACStatusBarHeight()
{
_viewController = viewController;
UIView *navbar = viewController.navigationController.navigationBar;
NSAssert(navbar != nil, @"You are using the component wrong... Please see the README file.");
viewController.automaticallyAdjustsScrollViewInsets = NO;
UINavigationController *navController = viewController.navigationController;
NSAssert(navController != nil, @"The view controller must already be in a navigation controller hierarchy");
[self.extensionViewContainer removeFromSuperview];
[self.viewController.view addSubview:self.extensionViewContainer];
self.navBarController.view = navbar;
if (navController.navigationBar.isTranslucent)
{
[navController.navigationBar.subviews.firstObject setBackgroundColor:[UIColor whiteColor]];
self.navBarController = self.translucentNavBarController;
self.navBarController.view = navController.navigationBar;
}
else
{
self.navBarController = self.opaqueNavBarController;
self.navBarController.view = navController.view;
}
[self layoutViews];
}
@ -156,9 +207,9 @@ static inline CGFloat AACStatusBarHeight()
self.delegateProxy.originalDelegate = _scrollView.delegate;
_scrollView.delegate = (id)self.delegateProxy;
}
[self cleanup];
[self layoutViews];
}
- (CGRect)extensionViewBounds
@ -229,7 +280,7 @@ static inline CGFloat AACStatusBarHeight()
deltaY = MIN(0, availableResistance + deltaY);
}
else if (self.scrollView.contentOffset.y > -AACStatusBarHeight())
else if (self.scrollView.contentOffset.y > 0)
{
CGFloat availableResistance = self.expansionResistance - self.resistanceConsumed;
self.resistanceConsumed = MIN(self.expansionResistance, self.resistanceConsumed + deltaY);
@ -303,9 +354,13 @@ static inline CGFloat AACStatusBarHeight()
self.previousScrollInsets = scrollInsets;
[self.navBarController expand];
if (!self.scrollView.isTracking && !self.scrollView.isDragging && !self.scrollView.isDecelerating)
{
[self.navBarController expand];
}
[self.extensionViewContainer.superview bringSubviewToFront:self.extensionViewContainer];
self.scrollView.contentInset = scrollInsets;
self.scrollView.scrollIndicatorInsets = scrollInsets;
}
@ -340,6 +395,11 @@ static inline CGFloat AACStatusBarHeight()
#pragma mark - NSNotificationCenter methods
- (void)applicationDidChangeStatusBarFrame
{
[self prepareForDisplay];
}
- (void)applicationDidBecomeActive
{
[self.navBarController expand];
@ -395,8 +455,9 @@ static char shyNavBarManagerKey;
- (TLYShyNavBarManager *)shyNavBarManager
{
#warning HACK - Disabled for iPad
id shyNavBarManager = objc_getAssociatedObject(self, &shyNavBarManagerKey);
if (!shyNavBarManager)
if (!shyNavBarManager && ![UIDevice isPad])
{
shyNavBarManager = [[TLYShyNavBarManager alloc] init];
self.shyNavBarManager = shyNavBarManager;
@ -415,3 +476,25 @@ static char shyNavBarManagerKey;
@end
#pragma mark - UINavigationBar Category Implementation
@implementation UINavigationBar (TLYShyNavBar)
// This method is courtesy of GTScrollNavigationBar
// https://github.com/luugiathuy/GTScrollNavigationBar
- (void)updateSubviewsToAlpha:(CGFloat)alpha
{
for (UIView* view in self.subviews)
{
bool isBackgroundView = view == [self.subviews firstObject];
bool isViewHidden = view.hidden || view.alpha < FLT_EPSILON;
if (!isBackgroundView && !isViewHidden)
{
view.alpha = MAX(alpha, FLT_EPSILON);
}
}
}
@end

View File

@ -1,49 +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>
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

@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
8268FA02194C926F004EC0E4 /* TLYShyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8268FA01194C926F004EC0E4 /* TLYShyViewController.m */; };
8268FA13194DBA58004EC0E4 /* TLYShyNavBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */; };
828F57201949C37B009EB8DD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 828F571F1949C37B009EB8DD /* Foundation.framework */; };
828F57221949C37B009EB8DD /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 828F57211949C37B009EB8DD /* CoreGraphics.framework */; };
@ -25,6 +24,9 @@
828F574B1949C37B009EB8DD /* TLYShyNavBarDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 828F574A1949C37B009EB8DD /* TLYShyNavBarDemoTests.m */; };
829FEE001957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 829FEDFF1957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m */; };
82B01ED3195D449F00C3C10C /* TLYDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 82B01ED2195D449F00C3C10C /* TLYDelegateProxy.m */; };
82BED11B19FFF7CD00334F30 /* TLYBoundsShyController.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BED11619FFF7CD00334F30 /* TLYBoundsShyController.m */; };
82BED11C19FFF7CD00334F30 /* TLYOffsetShyController.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BED11819FFF7CD00334F30 /* TLYOffsetShyController.m */; };
82BED11D19FFF7CD00334F30 /* TLYShyController.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BED11A19FFF7CD00334F30 /* TLYShyController.m */; };
82C882091955FDA60046C49D /* UIViewController+BetterLayoutGuides.m in Sources */ = {isa = PBXBuildFile; fileRef = 82C882081955FDA60046C49D /* UIViewController+BetterLayoutGuides.m */; };
/* End PBXBuildFile section */
@ -39,8 +41,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
8268FA00194C926F004EC0E4 /* TLYShyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyViewController.h; sourceTree = "<group>"; };
8268FA01194C926F004EC0E4 /* TLYShyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyViewController.m; sourceTree = "<group>"; };
8268FA11194DBA58004EC0E4 /* TLYShyNavBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyNavBarManager.h; sourceTree = "<group>"; };
8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyNavBarManager.m; sourceTree = "<group>"; };
828F571C1949C37B009EB8DD /* TLYShyNavBarDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TLYShyNavBarDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -66,6 +66,12 @@
829FEDFF1957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+TLYSwizzlingHelpers.m"; sourceTree = "<group>"; };
82B01ED1195D449F00C3C10C /* TLYDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYDelegateProxy.h; sourceTree = "<group>"; };
82B01ED2195D449F00C3C10C /* TLYDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYDelegateProxy.m; sourceTree = "<group>"; };
82BED11519FFF7CD00334F30 /* TLYBoundsShyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYBoundsShyController.h; sourceTree = "<group>"; };
82BED11619FFF7CD00334F30 /* TLYBoundsShyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYBoundsShyController.m; sourceTree = "<group>"; };
82BED11719FFF7CD00334F30 /* TLYOffsetShyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYOffsetShyController.h; sourceTree = "<group>"; };
82BED11819FFF7CD00334F30 /* TLYOffsetShyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYOffsetShyController.m; sourceTree = "<group>"; };
82BED11919FFF7CD00334F30 /* TLYShyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyController.h; sourceTree = "<group>"; };
82BED11A19FFF7CD00334F30 /* TLYShyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyController.m; sourceTree = "<group>"; };
82C882071955FDA60046C49D /* UIViewController+BetterLayoutGuides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+BetterLayoutGuides.h"; sourceTree = "<group>"; };
82C882081955FDA60046C49D /* UIViewController+BetterLayoutGuides.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+BetterLayoutGuides.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -171,11 +177,10 @@
828F57541949C381009EB8DD /* TLYShyNavBar */ = {
isa = PBXGroup;
children = (
82BED11419FFF7CD00334F30 /* ShyControllers */,
82B01EDB195D580000C3C10C /* Categories */,
8268FA11194DBA58004EC0E4 /* TLYShyNavBarManager.h */,
8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */,
8268FA00194C926F004EC0E4 /* TLYShyViewController.h */,
8268FA01194C926F004EC0E4 /* TLYShyViewController.m */,
82B01ED1195D449F00C3C10C /* TLYDelegateProxy.h */,
82B01ED2195D449F00C3C10C /* TLYDelegateProxy.m */,
);
@ -194,6 +199,19 @@
path = Categories;
sourceTree = "<group>";
};
82BED11419FFF7CD00334F30 /* ShyControllers */ = {
isa = PBXGroup;
children = (
82BED11519FFF7CD00334F30 /* TLYBoundsShyController.h */,
82BED11619FFF7CD00334F30 /* TLYBoundsShyController.m */,
82BED11719FFF7CD00334F30 /* TLYOffsetShyController.h */,
82BED11819FFF7CD00334F30 /* TLYOffsetShyController.m */,
82BED11919FFF7CD00334F30 /* TLYShyController.h */,
82BED11A19FFF7CD00334F30 /* TLYShyController.m */,
);
path = ShyControllers;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -292,14 +310,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
82BED11D19FFF7CD00334F30 /* TLYShyController.m in Sources */,
8268FA13194DBA58004EC0E4 /* TLYShyNavBarManager.m in Sources */,
82C882091955FDA60046C49D /* UIViewController+BetterLayoutGuides.m in Sources */,
828F57301949C37B009EB8DD /* TLYAppDelegate.m in Sources */,
82B01ED3195D449F00C3C10C /* TLYDelegateProxy.m in Sources */,
8268FA02194C926F004EC0E4 /* TLYShyViewController.m in Sources */,
829FEE001957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m in Sources */,
828F57361949C37B009EB8DD /* TLYViewController.m in Sources */,
828F572C1949C37B009EB8DD /* main.m in Sources */,
82BED11C19FFF7CD00334F30 /* TLYOffsetShyController.m in Sources */,
82BED11B19FFF7CD00334F30 /* TLYBoundsShyController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -11,23 +11,35 @@
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>58D55ECABAD5AAEB583BB3898420091CC2A418B2</key>
<string>ssh://github.com/telly/TLYShyNavBar.git</string>
<string>github.com:telly/TLYShyNavBar.git</string>
<key>8C0C471265CB14B6A879DEAFBD0682FF2966FD95</key>
<string>github.com:mazyod/Telly-iOS.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>TLYShyNavBarDemo/TLYShyNavBarDemo.xcodeproj/project.xcworkspace</string>
<string>TLYShyNavBarDemo/TLYShyNavBarDemo.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>58D55ECABAD5AAEB583BB3898420091CC2A418B2</key>
<string>../../..</string>
<key>8C0C471265CB14B6A879DEAFBD0682FF2966FD95</key>
<string>../../../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>ssh://github.com/telly/TLYShyNavBar.git</string>
<string>github.com:telly/TLYShyNavBar.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>58D55ECABAD5AAEB583BB3898420091CC2A418B2</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>8C0C471265CB14B6A879DEAFBD0682FF2966FD95</string>
<key>IDESourceControlWCCName</key>
<string>..</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>