diff --git a/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/AppIcon.appiconset/Contents.json index a396706..33ec0bc 100644 --- a/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -14,6 +14,11 @@ "idiom" : "iphone", "size" : "60x60", "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" } ], "info" : { diff --git a/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/sample.imageset/Contents.json b/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/sample.imageset/Contents.json index bf73ba7..5c246c5 100644 --- a/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/sample.imageset/Contents.json +++ b/TLYShyNavBarDemo/TLYShyNavBarDemo/Images.xcassets/sample.imageset/Contents.json @@ -8,6 +8,10 @@ "idiom" : "universal", "scale" : "2x", "filename" : "sample@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" } ], "info" : { diff --git a/TLYShyNavBarSwiftDemo/Bridging-Header.h b/TLYShyNavBarSwiftDemo/Bridging-Header.h new file mode 100644 index 0000000..d398c43 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/Bridging-Header.h @@ -0,0 +1,16 @@ +// +// Bridging-Header.h +// TLYShyNavBarSwiftDemo +// +// Created by Tony Nuzzi on 2/22/15. +// Copyright (c) 2015 Acktie, LLC. All rights reserved. +// + +#ifndef TLYShyNavBarSwiftDemo_Bridging_Header_h +#define TLYShyNavBarSwiftDemo_Bridging_Header_h +#import "TLYShyNavBarManager.h" +#import "TLYShyViewController.h" +#import "TLYDelegateProxy.h" +#import "NSObject+TLYSwizzlingHelpers.h" +#import "UIViewController+BetterLayoutGuides.h" +#endif diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.h b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.h new file mode 100644 index 0000000..2394e82 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.h @@ -0,0 +1,16 @@ +// +// NSObject+TLYSwizzlingHelpers.h +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/23/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import + +@interface NSObject (TLYSwizzlingHelpers) + ++ (void)tly_swizzleClassMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector; ++ (void)tly_swizzleInstanceMethod:(SEL)originalSelector withReplacement:(SEL)replacementSelector; + +@end diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.m b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.m new file mode 100644 index 0000000..d72f537 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/NSObject+TLYSwizzlingHelpers.m @@ -0,0 +1,28 @@ +// +// 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 + +@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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.h b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.h new file mode 100644 index 0000000..afd162d --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.h @@ -0,0 +1,25 @@ +// +// UIViewController+BetterLayoutGuides.h +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/21/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import + +/* 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 tly_topLayoutGuide; +@property (nonatomic, readonly) id tly_bottomLayoutGuide; + +@end diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.m b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.m new file mode 100644 index 0000000..8ea1159 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/Categories/UIViewController+BetterLayoutGuides.m @@ -0,0 +1,37 @@ +// +// 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)tly_topLayoutGuide +{ + if (self.parentViewController && + ![self.parentViewController isKindOfClass:UINavigationController.class]) + { + return self.parentViewController.tly_topLayoutGuide; + } + else { + return self.topLayoutGuide; + } +} + +- (id)tly_bottomLayoutGuide +{ + if (self.parentViewController && + ![self.parentViewController isKindOfClass:UINavigationController.class]) + { + return self.parentViewController.tly_bottomLayoutGuide; + } + else { + return self.bottomLayoutGuide; + } +} + +@end diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.h b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.h new file mode 100644 index 0000000..fa831eb --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.h @@ -0,0 +1,25 @@ +// +// TLYDelegateProxy.h +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/27/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import + +/* 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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.m b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.m new file mode 100644 index 0000000..5324fdd --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYDelegateProxy.m @@ -0,0 +1,74 @@ +// +// TLYDelegateProxy.m +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/27/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import "TLYDelegateProxy.h" +#import + +@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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.h b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.h new file mode 100644 index 0000000..b4484ab --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.h @@ -0,0 +1,77 @@ +// +// TLYShyNavBarManager.h +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/13/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import +#import + +/* 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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.m b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.m new file mode 100644 index 0000000..7e6d390 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyNavBarManager.m @@ -0,0 +1,452 @@ +// +// 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 + +#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 () + +@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, ­NavBarManagerKey, shyNavBarManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (TLYShyNavBarManager *)shyNavBarManager +{ + id shyNavBarManager = objc_getAssociatedObject(self, ­NavBarManagerKey); + 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, ­NavBarManagerKey); +} + +@end + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.h b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.h new file mode 100644 index 0000000..021d981 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.h @@ -0,0 +1,50 @@ +// +// TLYShyViewController.h +// TLYShyNavBarDemo +// +// Created by Mazyad Alabduljaleel on 6/14/14. +// Copyright (c) 2014 Telly, Inc. All rights reserved. +// + +#import +#import + +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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.m b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.m new file mode 100644 index 0000000..1fb0369 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBar/TLYShyViewController.m @@ -0,0 +1,181 @@ +// +// 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 diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.pbxproj b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..38e2ce7 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.pbxproj @@ -0,0 +1,470 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E7ADE6101A99A83B00E8F95C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7ADE60F1A99A83B00E8F95C /* AppDelegate.swift */; }; + E7ADE6151A99A83B00E8F95C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7ADE6131A99A83B00E8F95C /* Main.storyboard */; }; + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E7ADE6201A99A83B00E8F95C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E7ADE6021A99A83B00E8F95C /* Project object */; + proxyType = 1; + remoteGlobalIDString = E7ADE6091A99A83B00E8F95C; + remoteInfo = TLYShyNavBarSwiftDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + E7ADE60A1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TLYShyNavBarSwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E7ADE60E1A99A83B00E8F95C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E7ADE60F1A99A83B00E8F95C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + E7ADE6141A99A83B00E8F95C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E7ADE6161A99A83B00E8F95C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + E7ADE6191A99A83B00E8F95C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 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 = ""; }; + E7ADE6251A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TLYShyNavBarSwiftDemoTests.swift; sourceTree = ""; }; + E7ADE6311A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+TLYSwizzlingHelpers.h"; sourceTree = ""; }; + E7ADE6321A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+TLYSwizzlingHelpers.m"; sourceTree = ""; }; + E7ADE6331A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+BetterLayoutGuides.h"; sourceTree = ""; }; + E7ADE6341A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+BetterLayoutGuides.m"; sourceTree = ""; }; + E7ADE6351A99A84C00E8F95C /* TLYDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYDelegateProxy.h; sourceTree = ""; }; + E7ADE6361A99A84C00E8F95C /* TLYDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYDelegateProxy.m; sourceTree = ""; }; + E7ADE6371A99A84C00E8F95C /* TLYShyNavBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyNavBarManager.h; sourceTree = ""; }; + E7ADE6381A99A84C00E8F95C /* TLYShyNavBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyNavBarManager.m; sourceTree = ""; }; + E7ADE6391A99A84C00E8F95C /* TLYShyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLYShyViewController.h; sourceTree = ""; }; + E7ADE63A1A99A84C00E8F95C /* TLYShyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLYShyViewController.m; sourceTree = ""; }; + E7ADE6401A99A86600E8F95C /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + E7ADE6411A99AB5800E8F95C /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E7ADE6071A99A83B00E8F95C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E7ADE61C1A99A83B00E8F95C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E7ADE6011A99A83B00E8F95C = { + isa = PBXGroup; + children = ( + E7ADE6401A99A86600E8F95C /* Bridging-Header.h */, + E7ADE62F1A99A84C00E8F95C /* TLYShyNavBar */, + E7ADE60C1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */, + E7ADE6221A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests */, + E7ADE60B1A99A83B00E8F95C /* Products */, + ); + sourceTree = ""; + }; + E7ADE60B1A99A83B00E8F95C /* Products */ = { + isa = PBXGroup; + children = ( + E7ADE60A1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo.app */, + E7ADE61F1A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + E7ADE60C1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */ = { + isa = PBXGroup; + children = ( + E7ADE60F1A99A83B00E8F95C /* AppDelegate.swift */, + E7ADE6131A99A83B00E8F95C /* Main.storyboard */, + E7ADE6411A99AB5800E8F95C /* TableViewController.swift */, + E7ADE6161A99A83B00E8F95C /* Images.xcassets */, + E7ADE6181A99A83B00E8F95C /* LaunchScreen.xib */, + E7ADE60D1A99A83B00E8F95C /* Supporting Files */, + ); + path = TLYShyNavBarSwiftDemo; + sourceTree = ""; + }; + E7ADE60D1A99A83B00E8F95C /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E7ADE60E1A99A83B00E8F95C /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + E7ADE6221A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests */ = { + isa = PBXGroup; + children = ( + E7ADE6251A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift */, + E7ADE6231A99A83B00E8F95C /* Supporting Files */, + ); + path = TLYShyNavBarSwiftDemoTests; + sourceTree = ""; + }; + E7ADE6231A99A83B00E8F95C /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E7ADE6241A99A83B00E8F95C /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + E7ADE62F1A99A84C00E8F95C /* TLYShyNavBar */ = { + isa = PBXGroup; + children = ( + E7ADE6301A99A84C00E8F95C /* Categories */, + E7ADE6351A99A84C00E8F95C /* TLYDelegateProxy.h */, + E7ADE6361A99A84C00E8F95C /* TLYDelegateProxy.m */, + E7ADE6371A99A84C00E8F95C /* TLYShyNavBarManager.h */, + E7ADE6381A99A84C00E8F95C /* TLYShyNavBarManager.m */, + E7ADE6391A99A84C00E8F95C /* TLYShyViewController.h */, + E7ADE63A1A99A84C00E8F95C /* TLYShyViewController.m */, + ); + path = TLYShyNavBar; + sourceTree = ""; + }; + E7ADE6301A99A84C00E8F95C /* Categories */ = { + isa = PBXGroup; + children = ( + E7ADE6311A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.h */, + E7ADE6321A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m */, + E7ADE6331A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.h */, + E7ADE6341A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m */, + ); + path = Categories; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E7ADE6091A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7ADE6291A99A83B00E8F95C /* Build configuration list for PBXNativeTarget "TLYShyNavBarSwiftDemo" */; + buildPhases = ( + E7ADE6061A99A83B00E8F95C /* Sources */, + E7ADE6071A99A83B00E8F95C /* Frameworks */, + E7ADE6081A99A83B00E8F95C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TLYShyNavBarSwiftDemo; + productName = TLYShyNavBarSwiftDemo; + productReference = E7ADE60A1A99A83B00E8F95C /* TLYShyNavBarSwiftDemo.app */; + productType = "com.apple.product-type.application"; + }; + E7ADE61E1A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7ADE62C1A99A83B00E8F95C /* Build configuration list for PBXNativeTarget "TLYShyNavBarSwiftDemoTests" */; + buildPhases = ( + E7ADE61B1A99A83B00E8F95C /* Sources */, + E7ADE61C1A99A83B00E8F95C /* Frameworks */, + E7ADE61D1A99A83B00E8F95C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E7ADE6211A99A83B00E8F95C /* PBXTargetDependency */, + ); + name = TLYShyNavBarSwiftDemoTests; + productName = TLYShyNavBarSwiftDemoTests; + productReference = E7ADE61F1A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E7ADE6021A99A83B00E8F95C /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "Acktie, LLC"; + TargetAttributes = { + E7ADE6091A99A83B00E8F95C = { + CreatedOnToolsVersion = 6.1.1; + }; + E7ADE61E1A99A83B00E8F95C = { + CreatedOnToolsVersion = 6.1.1; + TestTargetID = E7ADE6091A99A83B00E8F95C; + }; + }; + }; + buildConfigurationList = E7ADE6051A99A83B00E8F95C /* Build configuration list for PBXProject "TLYShyNavBarSwiftDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E7ADE6011A99A83B00E8F95C; + productRefGroup = E7ADE60B1A99A83B00E8F95C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E7ADE6091A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */, + E7ADE61E1A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E7ADE6081A99A83B00E8F95C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7ADE6151A99A83B00E8F95C /* Main.storyboard in Resources */, + E7ADE61A1A99A83B00E8F95C /* LaunchScreen.xib in Resources */, + E7ADE6171A99A83B00E8F95C /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E7ADE61D1A99A83B00E8F95C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E7ADE6061A99A83B00E8F95C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7ADE63F1A99A84C00E8F95C /* TLYShyViewController.m in Sources */, + E7ADE63C1A99A84C00E8F95C /* UIViewController+BetterLayoutGuides.m in Sources */, + E7ADE6421A99AB5800E8F95C /* TableViewController.swift in Sources */, + E7ADE6101A99A83B00E8F95C /* AppDelegate.swift in Sources */, + E7ADE63D1A99A84C00E8F95C /* TLYDelegateProxy.m in Sources */, + E7ADE63E1A99A84C00E8F95C /* TLYShyNavBarManager.m in Sources */, + E7ADE63B1A99A84C00E8F95C /* NSObject+TLYSwizzlingHelpers.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E7ADE61B1A99A83B00E8F95C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7ADE6261A99A83B00E8F95C /* TLYShyNavBarSwiftDemoTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E7ADE6211A99A83B00E8F95C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E7ADE6091A99A83B00E8F95C /* TLYShyNavBarSwiftDemo */; + targetProxy = E7ADE6201A99A83B00E8F95C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + E7ADE6131A99A83B00E8F95C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E7ADE6141A99A83B00E8F95C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + E7ADE6181A99A83B00E8F95C /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + E7ADE6191A99A83B00E8F95C /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E7ADE6271A99A83B00E8F95C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E7ADE6281A99A83B00E8F95C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E7ADE62A1A99A83B00E8F95C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TLYShyNavBarSwiftDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + }; + name = Debug; + }; + E7ADE62B1A99A83B00E8F95C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TLYShyNavBarSwiftDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + }; + name = Release; + }; + E7ADE62D1A99A83B00E8F95C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = TLYShyNavBarSwiftDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TLYShyNavBarSwiftDemo.app/TLYShyNavBarSwiftDemo"; + }; + name = Debug; + }; + E7ADE62E1A99A83B00E8F95C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = TLYShyNavBarSwiftDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TLYShyNavBarSwiftDemo.app/TLYShyNavBarSwiftDemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E7ADE6051A99A83B00E8F95C /* Build configuration list for PBXProject "TLYShyNavBarSwiftDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7ADE6271A99A83B00E8F95C /* Debug */, + E7ADE6281A99A83B00E8F95C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7ADE6291A99A83B00E8F95C /* Build configuration list for PBXNativeTarget "TLYShyNavBarSwiftDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7ADE62A1A99A83B00E8F95C /* Debug */, + E7ADE62B1A99A83B00E8F95C /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + E7ADE62C1A99A83B00E8F95C /* Build configuration list for PBXNativeTarget "TLYShyNavBarSwiftDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7ADE62D1A99A83B00E8F95C /* Debug */, + E7ADE62E1A99A83B00E8F95C /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = E7ADE6021A99A83B00E8F95C /* Project object */; +} diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..db0c8d2 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/TLYShyNavBarSwiftDemo.xccheckout b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/TLYShyNavBarSwiftDemo.xccheckout new file mode 100644 index 0000000..5871bc4 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/TLYShyNavBarSwiftDemo.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 72C64448-CAFE-4F5D-8307-0D51A162F41F + IDESourceControlProjectName + TLYShyNavBarSwiftDemo + IDESourceControlProjectOriginsDictionary + + 58D55ECABAD5AAEB583BB3898420091CC2A418B2 + github.com:TNuzzi/TLYShyNavBar.git + + IDESourceControlProjectPath + TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 58D55ECABAD5AAEB583BB3898420091CC2A418B2 + ../../.. + + IDESourceControlProjectURL + github.com:TNuzzi/TLYShyNavBar.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 58D55ECABAD5AAEB583BB3898420091CC2A418B2 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 58D55ECABAD5AAEB583BB3898420091CC2A418B2 + IDESourceControlWCCName + TLYShyNavBar + + + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/AppDelegate.swift b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/AppDelegate.swift new file mode 100644 index 0000000..74bdc5b --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/AppDelegate.swift @@ -0,0 +1,23 @@ +// +// AppDelegate.swift +// TLYShyNavBarSwiftDemo +// +// Created by Tony Nuzzi on 2/22/15. +// Copyright (c) 2015 Acktie, LLC. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } + +} + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/LaunchScreen.xib b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000..b2fc1c2 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/Main.storyboard b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..3603bb9 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Base.lproj/Main.storyboard @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Info.plist b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Info.plist new file mode 100644 index 0000000..5334512 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.acktie.test.swift.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/TableViewController.swift b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/TableViewController.swift new file mode 100644 index 0000000..561ee41 --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemo/TableViewController.swift @@ -0,0 +1,51 @@ +// +// TableViewController.swift +// TLYShyNavBarSwiftDemo +// +// Created by Tony Nuzzi on 2/22/15. +// Copyright (c) 2015 Acktie, LLC. All rights reserved. +// + +import UIKit + +class TableViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + /* In your UIViewController viewDidLoad or after creating the scroll view. */ + self.shyNavBarManager.scrollView = self.tableView; + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem() + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + // MARK: - Table view data source + + override func numberOfSectionsInTableView(tableView: UITableView) -> Int { + // #warning Potentially incomplete method implementation. + // Return the number of sections. + return 1 + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete method implementation. + // Return the number of rows in the section. + return 50 + } + + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell + + return cell + } +} diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/Info.plist b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/Info.plist new file mode 100644 index 0000000..00100cf --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.acktie.test.swift.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/TLYShyNavBarSwiftDemoTests.swift b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/TLYShyNavBarSwiftDemoTests.swift new file mode 100644 index 0000000..4416c1e --- /dev/null +++ b/TLYShyNavBarSwiftDemo/TLYShyNavBarSwiftDemoTests/TLYShyNavBarSwiftDemoTests.swift @@ -0,0 +1,36 @@ +// +// TLYShyNavBarSwiftDemoTests.swift +// TLYShyNavBarSwiftDemoTests +// +// Created by Tony Nuzzi on 2/22/15. +// Copyright (c) 2015 Acktie, LLC. All rights reserved. +// + +import UIKit +import XCTest + +class TLYShyNavBarSwiftDemoTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + XCTAssert(true, "Pass") + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock() { + // Put the code you want to measure the time of here. + } + } + +}