Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safari-style bar behavior definer + delegate for creating a delegate chain #56

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions BLKFlexibleHeightBar/BLKFlexibleHeightBarBehaviorDefiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@class BLKFlexibleHeightBar;

/**
Expand All @@ -34,6 +36,11 @@
*/
@property (nonatomic, readonly, weak) BLKFlexibleHeightBar *flexibleHeightBar;

/**
All unhandled messages will be redirected to this delegate.
*/
@property (nonatomic, weak) id<NSObject> delegate;

/**
Determines whether snapping is enabled or not. Default value is YES.
*/
Expand Down Expand Up @@ -96,3 +103,5 @@
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;

@end

NS_ASSUME_NONNULL_END
65 changes: 63 additions & 2 deletions BLKFlexibleHeightBar/BLKFlexibleHeightBarBehaviorDefiner.m
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ - (void)snapWithScrollView:(UIScrollView *)scrollView
{
self.currentlySnapping = YES;

__block CGFloat snapPosition = MAXFLOAT;
__block CGFloat snapPosition = CGFLOAT_MAX;
[self.snappingPositionsForProgressRanges enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

NSValue *existingRangeValue = key;
Expand All @@ -124,7 +124,7 @@ - (void)snapWithScrollView:(UIScrollView *)scrollView

}];

if(snapPosition != MAXFLOAT)
if(snapPosition != CGFLOAT_MAX)
{
[UIView animateWithDuration:0.15 animations:^{

Expand All @@ -149,6 +149,9 @@ - (void)snapWithScrollView:(UIScrollView *)scrollView
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self snapWithScrollView:scrollView];

NSInvocation* invocation = [self invocationForSelector:@selector(scrollViewDidEndDecelerating:) scrollView:scrollView];
[invocation invoke];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
Expand All @@ -157,11 +160,69 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
{
[self snapWithScrollView:scrollView];
}

NSInvocation* invocation = [self invocationForSelector:@selector(scrollViewDidEndDragging:willDecelerate:) scrollView:scrollView];
[invocation setArgument:&decelerate atIndex:3];
[invocation invoke];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(CGRectGetHeight(self.flexibleHeightBar.bounds), scrollView.scrollIndicatorInsets.left, scrollView.scrollIndicatorInsets.bottom, scrollView.scrollIndicatorInsets.right);

NSInvocation* invocation = [self invocationForSelector:@selector(scrollViewDidScroll:) scrollView:scrollView];
[invocation invoke];
}

# pragma mark - Message forwarding helper

- (NSInvocation*)invocationForSelector:(SEL)selector scrollView:(UIScrollView*)scrollView
{
if ([self.delegate respondsToSelector:selector])
{
NSMethodSignature* signature = [self.delegate.class instanceMethodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self.delegate];
[invocation setSelector:selector];
[invocation setArgument:&scrollView atIndex:2];

return invocation;
}

return nil;
}

# pragma mark - Message forwarding

- (void)forwardInvocation:(NSInvocation*)anInvocation
{
if ([self.delegate respondsToSelector:anInvocation.selector])
{
[anInvocation invokeWithTarget:self.delegate];
}
}

- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *first = [super methodSignatureForSelector:aSelector];
NSMethodSignature *second = [(NSObject*)self.delegate methodSignatureForSelector:aSelector];

if (first)
{
return first;
}
else if (second)
{
return second;
}

return nil;
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL result = [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector];
return result;
}

@end
34 changes: 34 additions & 0 deletions BLKFlexibleHeightBar/SafariStyleBehaviorDefiner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Copyright (c) 2015, Bryan Keller. All rights reserved.
Licensed under the MIT license <http://opensource.org/licenses/MIT>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

/*
Safari style behavior definer by VragonerDev
*/

#import "BLKFlexibleHeightBarBehaviorDefiner.h"

@interface SafariStyleBehaviorDefiner : BLKFlexibleHeightBarBehaviorDefiner

/**
The non-negative value used for implementing Safari-style bar behavior when scrolling down with velocity.
Bigger value means that a bigger velocity is needed to expand the bar to the maximum height. The default value is 0.4.
*/
@property (nonatomic) CGFloat velocityThreshold;

@end
109 changes: 109 additions & 0 deletions BLKFlexibleHeightBar/SafariStyleBehaviorDefiner.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright (c) 2015, Bryan Keller. All rights reserved.
Licensed under the MIT license <http://opensource.org/licenses/MIT>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

#import "SafariStyleBehaviorDefiner.h"
#import "BLKFlexibleHeightBar.h"

#define kDefaultVelocityThreshold 0.4

@interface SafariStyleBehaviorDefiner ()
{
CGFloat previousScrollPosition;
}
@end

@implementation SafariStyleBehaviorDefiner

#pragma mark -
#pragma mark Init

- (instancetype)init
{
self = [super init];
if (self)
{
self.velocityThreshold = kDefaultVelocityThreshold;
}
return self;
}

#pragma mark -
#pragma mark Scroll view delegate methods

- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView
{
[super scrollViewWillBeginDragging:scrollView];

previousScrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;
}

- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
[super scrollViewDidScroll:scrollView];

CGFloat scrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;

if (!self.isCurrentlySnapping)
{
CGFloat newProgress = self.flexibleHeightBar.progress;

if (scrollPosition < 0.0)
{
newProgress = 0.0;
}
else if ((scrollView.contentOffset.y - scrollView.contentInset.bottom) >= scrollView.contentSize.height)
{
newProgress = 1.0;
}
else
{
CGFloat barHeight = self.flexibleHeightBar.maximumBarHeight - self.flexibleHeightBar.minimumBarHeight;
CGFloat progressDelta = (scrollPosition - previousScrollPosition) / barHeight;

if ((progressDelta > 0.0) || (scrollPosition < barHeight))
{
newProgress += progressDelta;
}
}

if (self.flexibleHeightBar.progress != newProgress)
{
self.flexibleHeightBar.progress = newProgress;
[self.flexibleHeightBar setNeedsLayout];
}
}

previousScrollPosition = scrollPosition;
}

- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint*)targetContentOffset
{
[super scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];

if (velocity.y < -self.velocityThreshold)
{
self.flexibleHeightBar.progress = 0.0;

[self snapWithScrollView:scrollView];
}
}

@end

#undef kDefaultVelocityThreshold