Compare commits
7 Commits
master
...
telly-spec
| Author | SHA1 | Date |
|---|---|---|
|
|
a924866611 | |
|
|
ce0afb6192 | |
|
|
30ed689dc9 | |
|
|
e658e03c0a | |
|
|
116ff631cc | |
|
|
f6fe4fd020 | |
|
|
613e74ceb6 |
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -8,14 +8,11 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#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
|
* object and its delegate. The DelegateProxy is initialized with a
|
||||||
* target and middle man, where the target is the original delegate
|
* target and middle man, where the target is the original delegate
|
||||||
* and the middle-man is just an object we send identical messages to.
|
* and the middle-man is just an object we send identical messages to.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@interface TLYDelegateProxy : NSProxy
|
@interface TLYDelegateProxy : NSProxy
|
||||||
|
|
||||||
@property (nonatomic, weak) id originalDelegate;
|
@property (nonatomic, weak) id originalDelegate;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
/* CLASS DESCRIPTION:
|
extern CGFloat tly_AACStatusBarHeight(void);
|
||||||
* ==================
|
|
||||||
* Manages the relationship between a scrollView and a view
|
/** Manages the relationship between a scrollView and a view
|
||||||
* controller. Must be instantiated and assigned the scrollView
|
* controller. Must be instantiated and assigned the scrollView
|
||||||
* that drives the contraction/expansion, then assigned to the
|
* that drives the contraction/expansion, then assigned to the
|
||||||
* viewController that needs the functionality. Must be assigned
|
* viewController that needs the functionality. Must be assigned
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
* viewController.shyNavManager = ...;
|
* viewController.shyNavManager = ...;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@interface TLYShyNavBarManager : NSObject
|
@interface TLYShyNavBarManager : NSObject
|
||||||
|
|
||||||
/* The view controller that is part of the navigation stack
|
/* The view controller that is part of the navigation stack
|
||||||
|
|
@ -55,9 +54,7 @@
|
||||||
@end
|
@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
|
* simply uses associated objects to attatch a TLYShyNavBar to the
|
||||||
* designated view controller.
|
* designated view controller.
|
||||||
*
|
*
|
||||||
|
|
@ -65,10 +62,20 @@
|
||||||
* TLYShyNavBar. Things like, viewDidLayoutSubviews, viewWillAppear and
|
* TLYShyNavBar. Things like, viewDidLayoutSubviews, viewWillAppear and
|
||||||
* Disappear, ... etc.
|
* Disappear, ... etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@interface UIViewController (ShyNavBar)
|
@interface UIViewController (ShyNavBar)
|
||||||
|
|
||||||
/* Initially, this is nil, but created for you when you access it */
|
/* Initially, this is nil, but created for you when you access it */
|
||||||
@property (nonatomic, strong) TLYShyNavBarManager *shyNavBarManager;
|
@property (nonatomic, strong) TLYShyNavBarManager *shyNavBarManager;
|
||||||
|
|
||||||
@end
|
@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
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "TLYShyNavBarManager.h"
|
#import "TLYShyNavBarManager.h"
|
||||||
#import "TLYShyViewController.h"
|
#import "TLYOffsetShyController.h"
|
||||||
|
#import "TLYBoundsShyController.h"
|
||||||
#import "TLYDelegateProxy.h"
|
#import "TLYDelegateProxy.h"
|
||||||
|
|
||||||
#import "UIViewController+BetterLayoutGuides.h"
|
#import "UIViewController+BetterLayoutGuides.h"
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
// Thanks to SO user, MattDiPasquale
|
// Thanks to SO user, MattDiPasquale
|
||||||
// http://stackoverflow.com/questions/12991935/how-to-programmatically-get-ios-status-bar-height/16598350#16598350
|
// 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)
|
if ([UIApplication sharedApplication].statusBarHidden)
|
||||||
{
|
{
|
||||||
|
|
@ -35,14 +36,16 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
|
|
||||||
@interface TLYShyNavBarManager () <UIScrollViewDelegate>
|
@interface TLYShyNavBarManager () <UIScrollViewDelegate>
|
||||||
|
|
||||||
@property (nonatomic, strong) TLYShyViewController *navBarController;
|
@property (nonatomic, unsafe_unretained) TLYShyController *navBarController;
|
||||||
@property (nonatomic, strong) TLYShyViewController *extensionController;
|
|
||||||
|
@property (nonatomic, strong) TLYShyController *translucentNavBarController;
|
||||||
|
@property (nonatomic, strong) TLYShyController *opaqueNavBarController;
|
||||||
|
@property (nonatomic, strong) TLYShyController *extensionController;
|
||||||
|
|
||||||
@property (nonatomic, strong) TLYDelegateProxy *delegateProxy;
|
@property (nonatomic, strong) TLYDelegateProxy *delegateProxy;
|
||||||
|
|
||||||
@property (nonatomic, strong) UIView *extensionViewContainer;
|
@property (nonatomic, strong) UIView *extensionViewContainer;
|
||||||
|
|
||||||
@property (nonatomic) UIEdgeInsets previousScrollInsets;
|
|
||||||
@property (nonatomic) CGFloat previousYOffset;
|
@property (nonatomic) CGFloat previousYOffset;
|
||||||
@property (nonatomic) CGFloat resistanceConsumed;
|
@property (nonatomic) CGFloat resistanceConsumed;
|
||||||
|
|
||||||
|
|
@ -72,44 +75,77 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
|
|
||||||
self.alphaFadeEnabled = YES;
|
self.alphaFadeEnabled = YES;
|
||||||
|
|
||||||
self.previousScrollInsets = UIEdgeInsetsZero;
|
|
||||||
self.previousYOffset = NAN;
|
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;
|
__weak __typeof(self) weakSelf = self;
|
||||||
self.extensionController.expandedCenter = ^(UIView *view)
|
|
||||||
{
|
self.opaqueNavBarController = ({
|
||||||
return CGPointMake(CGRectGetMidX(view.bounds),
|
TLYBoundsShyController *shyController = [[TLYBoundsShyController alloc] init];
|
||||||
CGRectGetMidY(view.bounds) + weakSelf.viewController.tly_topLayoutGuide.length);
|
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;
|
self.navBarController.child = self.extensionController;
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeStatusBarFrame) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
@ -131,13 +167,25 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
{
|
{
|
||||||
_viewController = viewController;
|
_viewController = viewController;
|
||||||
|
|
||||||
UIView *navbar = viewController.navigationController.navigationBar;
|
UINavigationController *navController = viewController.navigationController;
|
||||||
NSAssert(navbar != nil, @"You are using the component wrong... Please see the README file.");
|
NSAssert(navController != nil, @"The view controller must already be in a navigation controller hierarchy");
|
||||||
|
|
||||||
|
navController.automaticallyAdjustsScrollViewInsets = NO;
|
||||||
|
viewController.automaticallyAdjustsScrollViewInsets = NO;
|
||||||
|
|
||||||
[self.extensionViewContainer removeFromSuperview];
|
[self.extensionViewContainer removeFromSuperview];
|
||||||
[self.viewController.view addSubview:self.extensionViewContainer];
|
[self.viewController.view addSubview:self.extensionViewContainer];
|
||||||
|
|
||||||
self.navBarController.view = navbar;
|
if (navController.navigationBar.isTranslucent)
|
||||||
|
{
|
||||||
|
self.navBarController = self.translucentNavBarController;
|
||||||
|
self.navBarController.view = navController.navigationBar;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.navBarController = self.opaqueNavBarController;
|
||||||
|
self.navBarController.view = navController.view;
|
||||||
|
}
|
||||||
|
|
||||||
[self layoutViews];
|
[self layoutViews];
|
||||||
}
|
}
|
||||||
|
|
@ -156,9 +204,9 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
self.delegateProxy.originalDelegate = _scrollView.delegate;
|
self.delegateProxy.originalDelegate = _scrollView.delegate;
|
||||||
_scrollView.delegate = (id)self.delegateProxy;
|
_scrollView.delegate = (id)self.delegateProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self cleanup];
|
[self cleanup];
|
||||||
[self layoutViews];
|
[self layoutViews];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)extensionViewBounds
|
- (CGRect)extensionViewBounds
|
||||||
|
|
@ -191,6 +239,8 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
|
|
||||||
if (!isnan(self.previousYOffset))
|
if (!isnan(self.previousYOffset))
|
||||||
{
|
{
|
||||||
|
[self layoutViews];
|
||||||
|
|
||||||
// 1 - Calculate the delta
|
// 1 - Calculate the delta
|
||||||
CGFloat deltaY = (self.previousYOffset - self.scrollView.contentOffset.y);
|
CGFloat deltaY = (self.previousYOffset - self.scrollView.contentOffset.y);
|
||||||
|
|
||||||
|
|
@ -229,7 +279,7 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
|
|
||||||
deltaY = MIN(0, availableResistance + deltaY);
|
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;
|
CGFloat availableResistance = self.expansionResistance - self.resistanceConsumed;
|
||||||
self.resistanceConsumed = MIN(self.expansionResistance, self.resistanceConsumed + deltaY);
|
self.resistanceConsumed = MIN(self.expansionResistance, self.resistanceConsumed + deltaY);
|
||||||
|
|
@ -294,18 +344,20 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
- (void)layoutViews
|
- (void)layoutViews
|
||||||
{
|
{
|
||||||
UIEdgeInsets scrollInsets = self.scrollView.contentInset;
|
UIEdgeInsets scrollInsets = self.scrollView.contentInset;
|
||||||
scrollInsets.top = CGRectGetHeight(self.extensionViewContainer.bounds) + self.viewController.tly_topLayoutGuide.length;
|
scrollInsets.top = CGRectGetHeight(self.extensionViewContainer.bounds) + 64.f;
|
||||||
|
|
||||||
if (UIEdgeInsetsEqualToEdgeInsets(scrollInsets, self.previousScrollInsets))
|
if (UIEdgeInsetsEqualToEdgeInsets(scrollInsets, self.scrollView.contentInset))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previousScrollInsets = scrollInsets;
|
if (!self.scrollView.isTracking && !self.scrollView.isDragging && !self.scrollView.isDecelerating)
|
||||||
|
{
|
||||||
|
[self.navBarController expand];
|
||||||
|
}
|
||||||
|
|
||||||
[self.navBarController expand];
|
|
||||||
[self.extensionViewContainer.superview bringSubviewToFront:self.extensionViewContainer];
|
[self.extensionViewContainer.superview bringSubviewToFront:self.extensionViewContainer];
|
||||||
|
|
||||||
self.scrollView.contentInset = scrollInsets;
|
self.scrollView.contentInset = scrollInsets;
|
||||||
self.scrollView.scrollIndicatorInsets = scrollInsets;
|
self.scrollView.scrollIndicatorInsets = scrollInsets;
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +367,6 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
[self.navBarController expand];
|
[self.navBarController expand];
|
||||||
|
|
||||||
self.previousYOffset = NAN;
|
self.previousYOffset = NAN;
|
||||||
self.previousScrollInsets = UIEdgeInsetsZero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UIScrollViewDelegate methods
|
#pragma mark - UIScrollViewDelegate methods
|
||||||
|
|
@ -340,6 +391,11 @@ static inline CGFloat AACStatusBarHeight()
|
||||||
|
|
||||||
#pragma mark - NSNotificationCenter methods
|
#pragma mark - NSNotificationCenter methods
|
||||||
|
|
||||||
|
- (void)applicationDidChangeStatusBarFrame
|
||||||
|
{
|
||||||
|
[self prepareForDisplay];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)applicationDidBecomeActive
|
- (void)applicationDidBecomeActive
|
||||||
{
|
{
|
||||||
[self.navBarController expand];
|
[self.navBarController expand];
|
||||||
|
|
@ -395,8 +451,9 @@ static char shyNavBarManagerKey;
|
||||||
|
|
||||||
- (TLYShyNavBarManager *)shyNavBarManager
|
- (TLYShyNavBarManager *)shyNavBarManager
|
||||||
{
|
{
|
||||||
|
#warning HACK - Disabled for iPad
|
||||||
id shyNavBarManager = objc_getAssociatedObject(self, ­NavBarManagerKey);
|
id shyNavBarManager = objc_getAssociatedObject(self, ­NavBarManagerKey);
|
||||||
if (!shyNavBarManager)
|
if (!shyNavBarManager && ![UIDevice isPad])
|
||||||
{
|
{
|
||||||
shyNavBarManager = [[TLYShyNavBarManager alloc] init];
|
shyNavBarManager = [[TLYShyNavBarManager alloc] init];
|
||||||
self.shyNavBarManager = shyNavBarManager;
|
self.shyNavBarManager = shyNavBarManager;
|
||||||
|
|
@ -415,3 +472,25 @@ static char shyNavBarManagerKey;
|
||||||
|
|
||||||
@end
|
@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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
8268FA13194DBA58004EC0E4 /* TLYShyNavBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */; };
|
||||||
828F57201949C37B009EB8DD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 828F571F1949C37B009EB8DD /* Foundation.framework */; };
|
828F57201949C37B009EB8DD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 828F571F1949C37B009EB8DD /* Foundation.framework */; };
|
||||||
828F57221949C37B009EB8DD /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 828F57211949C37B009EB8DD /* CoreGraphics.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 */; };
|
828F574B1949C37B009EB8DD /* TLYShyNavBarDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 828F574A1949C37B009EB8DD /* TLYShyNavBarDemoTests.m */; };
|
||||||
829FEE001957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 829FEDFF1957DF620017E186 /* NSObject+TLYSwizzlingHelpers.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 */; };
|
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 */; };
|
82C882091955FDA60046C49D /* UIViewController+BetterLayoutGuides.m in Sources */ = {isa = PBXBuildFile; fileRef = 82C882081955FDA60046C49D /* UIViewController+BetterLayoutGuides.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
|
@ -39,8 +41,6 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference 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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
82C882081955FDA60046C49D /* UIViewController+BetterLayoutGuides.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+BetterLayoutGuides.m"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
@ -171,11 +177,10 @@
|
||||||
828F57541949C381009EB8DD /* TLYShyNavBar */ = {
|
828F57541949C381009EB8DD /* TLYShyNavBar */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
82BED11419FFF7CD00334F30 /* ShyControllers */,
|
||||||
82B01EDB195D580000C3C10C /* Categories */,
|
82B01EDB195D580000C3C10C /* Categories */,
|
||||||
8268FA11194DBA58004EC0E4 /* TLYShyNavBarManager.h */,
|
8268FA11194DBA58004EC0E4 /* TLYShyNavBarManager.h */,
|
||||||
8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */,
|
8268FA12194DBA58004EC0E4 /* TLYShyNavBarManager.m */,
|
||||||
8268FA00194C926F004EC0E4 /* TLYShyViewController.h */,
|
|
||||||
8268FA01194C926F004EC0E4 /* TLYShyViewController.m */,
|
|
||||||
82B01ED1195D449F00C3C10C /* TLYDelegateProxy.h */,
|
82B01ED1195D449F00C3C10C /* TLYDelegateProxy.h */,
|
||||||
82B01ED2195D449F00C3C10C /* TLYDelegateProxy.m */,
|
82B01ED2195D449F00C3C10C /* TLYDelegateProxy.m */,
|
||||||
);
|
);
|
||||||
|
|
@ -194,6 +199,19 @@
|
||||||
path = Categories;
|
path = Categories;
|
||||||
sourceTree = "<group>";
|
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 */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
|
@ -292,14 +310,16 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
82BED11D19FFF7CD00334F30 /* TLYShyController.m in Sources */,
|
||||||
8268FA13194DBA58004EC0E4 /* TLYShyNavBarManager.m in Sources */,
|
8268FA13194DBA58004EC0E4 /* TLYShyNavBarManager.m in Sources */,
|
||||||
82C882091955FDA60046C49D /* UIViewController+BetterLayoutGuides.m in Sources */,
|
82C882091955FDA60046C49D /* UIViewController+BetterLayoutGuides.m in Sources */,
|
||||||
828F57301949C37B009EB8DD /* TLYAppDelegate.m in Sources */,
|
828F57301949C37B009EB8DD /* TLYAppDelegate.m in Sources */,
|
||||||
82B01ED3195D449F00C3C10C /* TLYDelegateProxy.m in Sources */,
|
82B01ED3195D449F00C3C10C /* TLYDelegateProxy.m in Sources */,
|
||||||
8268FA02194C926F004EC0E4 /* TLYShyViewController.m in Sources */,
|
|
||||||
829FEE001957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m in Sources */,
|
829FEE001957DF620017E186 /* NSObject+TLYSwizzlingHelpers.m in Sources */,
|
||||||
828F57361949C37B009EB8DD /* TLYViewController.m in Sources */,
|
828F57361949C37B009EB8DD /* TLYViewController.m in Sources */,
|
||||||
828F572C1949C37B009EB8DD /* main.m in Sources */,
|
828F572C1949C37B009EB8DD /* main.m in Sources */,
|
||||||
|
82BED11C19FFF7CD00334F30 /* TLYOffsetShyController.m in Sources */,
|
||||||
|
82BED11B19FFF7CD00334F30 /* TLYBoundsShyController.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,35 @@
|
||||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>58D55ECABAD5AAEB583BB3898420091CC2A418B2</key>
|
<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>
|
</dict>
|
||||||
<key>IDESourceControlProjectPath</key>
|
<key>IDESourceControlProjectPath</key>
|
||||||
<string>TLYShyNavBarDemo/TLYShyNavBarDemo.xcodeproj/project.xcworkspace</string>
|
<string>TLYShyNavBarDemo/TLYShyNavBarDemo.xcodeproj</string>
|
||||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>58D55ECABAD5AAEB583BB3898420091CC2A418B2</key>
|
<key>58D55ECABAD5AAEB583BB3898420091CC2A418B2</key>
|
||||||
<string>../../..</string>
|
<string>../../..</string>
|
||||||
|
<key>8C0C471265CB14B6A879DEAFBD0682FF2966FD95</key>
|
||||||
|
<string>../../../../..</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>IDESourceControlProjectURL</key>
|
<key>IDESourceControlProjectURL</key>
|
||||||
<string>ssh://github.com/telly/TLYShyNavBar.git</string>
|
<string>github.com:telly/TLYShyNavBar.git</string>
|
||||||
<key>IDESourceControlProjectVersion</key>
|
<key>IDESourceControlProjectVersion</key>
|
||||||
<integer>111</integer>
|
<integer>111</integer>
|
||||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||||
<string>58D55ECABAD5AAEB583BB3898420091CC2A418B2</string>
|
<string>58D55ECABAD5AAEB583BB3898420091CC2A418B2</string>
|
||||||
<key>IDESourceControlProjectWCConfigurations</key>
|
<key>IDESourceControlProjectWCConfigurations</key>
|
||||||
<array>
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
|
<string>public.vcs.git</string>
|
||||||
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
|
<string>8C0C471265CB14B6A879DEAFBD0682FF2966FD95</string>
|
||||||
|
<key>IDESourceControlWCCName</key>
|
||||||
|
<string>..</string>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue