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

Merged branches, added minHeigth as parameter #35

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
93 changes: 90 additions & 3 deletions APParallaxHeader/UIScrollView+APParallaxHeader.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,83 @@

@interface UIScrollView (APParallaxHeader)

- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andShadow:(BOOL)shadow;
/**
* Adds a parallax with an image and an initial height.
*
* @param image An UIImage to parallax when scrolling.
* @param height The max height for the parallax header.
*/
- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height;
- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height;

/**
* Adds a parallax with an image and an initial height.
*
* @param image An UIImage to parallax when scrolling.
* @param height The max height for the parallax header.
* @param minHeight The min height for the parallax header.
*/
- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight;

/**
* Adds a parallax with an image, an initial height and a bottom inner shadow.
*
* @param image An UIImage to parallax when scrolling.
* @param height The max height for the parallax header
* @param shadow BOOL to show or hide the bottom inner shadow.
*/
- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andShadow:(BOOL)shadow;

/**
* Adds a parallax with an image and an initial height.
*
* @param image An UIImage to parallax when scrolling.
* @param height The max height for the parallax header.
* @param minHeight The min height for the parallax header.
* @param shadow BOOL to show or hide the bottom inner shadow.
*/
- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight andShadow:(BOOL)shadow;

/**
* Adds a parallax with a custom view with a given initial height.
*
* @param view A UIView to display in the parallax header.
* @param height The max height for the parallax header.
*/
- (void)addParallaxWithView:(UIView *)view andHeight:(CGFloat)height;

/**
* Adds a parallax with a custom view with a given initial height and minimum height.
*
* @param view A UIView to display in the parallax header.
* @param height The max height for the parallax header.
* @param minHeight The min height for the parallax header.
*/
- (void)addParallaxWithView:(UIView *)view andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight;

/**
* Adds a parallax with a custom view with a given initial height and a bottom inner shadow.
*
* @param view A UIView to display in the parallax header.
* @param height The max height for the parallax header.
* @param shadow BOOL to show or hide the bottom inner shadow.
*/
- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height andShadow:(BOOL)shadow;

/**
* Adds a parallax with a custom view with a given initial height and a bottom inner shadow.
*
* @param view A UIView to display in the parallax header.
* @param height The max height for the parallax header.
* @param minHeight The min height for the parallax header.
* @param shadow BOOL to show or hide the bottom inner shadow.
*/
- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight andShadow:(BOOL)shadow;

/**
* Getter for the parallaxView
*/
@property (nonatomic, strong, readonly) APParallaxView *parallaxView;

@property (nonatomic, assign) BOOL showsParallax;

@end
Expand All @@ -39,17 +110,33 @@ typedef NS_ENUM(NSUInteger, APParallaxTrackingState) {

@property (nonatomic, readonly) APParallaxTrackingState state;
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIView *currentSubView;
@property (nonatomic, strong) APParallaxShadowView *shadowView;
@property (nonatomic, strong) UIView *customView;

- (id)initWithFrame:(CGRect)frame andShadow:(BOOL)shadow;

@end

/**
* The APParallaxView delegate will allow you to get information for when the
* parallax header view will change its frame, so that you can do your repositioning etc.
*/
@protocol APParallaxViewDelegate <NSObject>
@optional
/**
* Delegate call for when the APParallaxView will change its frame.
*
* @param view The APParallaxView that will change its frame.
* @param frame The target frame.
*/
- (void)parallaxView:(APParallaxView *)view willChangeFrame:(CGRect)frame;

/**
* Delegate call for when the APParallaxView did change its frame.
*
* @param view The APParallaxView that changed its frame.
* @param frame The target frame.
*/
- (void)parallaxView:(APParallaxView *)view didChangeFrame:(CGRect)frame;
@end

Expand Down
116 changes: 70 additions & 46 deletions APParallaxHeader/UIScrollView+APParallaxHeader.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@

#import <QuartzCore/QuartzCore.h>

static char contentOffsetContext;

@interface APParallaxView ()

@property (nonatomic, readwrite) APParallaxTrackingState state;

@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, readwrite) CGFloat originalTopInset;
@property (nonatomic) CGFloat parallaxHeight;
@property (nonatomic) CGFloat parallaxMinHeight;

@property(nonatomic, assign) BOOL isObserving;

@end



#pragma mark - UIScrollView (APParallaxHeader)
#import <objc/runtime.h>

Expand All @@ -34,21 +35,30 @@ - (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height {
[self addParallaxWithImage:image andHeight:height andShadow:YES];
}

- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight {
[self addParallaxWithImage:image andHeight:height andShadow:YES];
}

- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andShadow:(BOOL)shadow {
[self addParallaxWithImage:image andHeight:height andMinHeight:0 andShadow:shadow];
}

- (void)addParallaxWithImage:(UIImage *)image andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight andShadow:(BOOL)shadow {
if(self.parallaxView) {
if(self.parallaxView.currentSubView) {
[self.parallaxView.currentSubView removeFromSuperview];
if(self.parallaxView.customView) {
[self.parallaxView.customView removeFromSuperview];
}
[self.parallaxView.imageView setImage:image];
}
else
{
APParallaxView *parallaxView = [[APParallaxView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width*2, height) andShadow:shadow];
else {
APParallaxView *parallaxView = [[APParallaxView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, height) andShadow:shadow];
[parallaxView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[parallaxView setClipsToBounds:YES];
[parallaxView.imageView setImage:image];

parallaxView.scrollView = self;
parallaxView.parallaxHeight = height;
parallaxView.parallaxMinHeight = minHeight;
[self addSubview:parallaxView];

parallaxView.originalTopInset = self.contentInset.top;
Expand All @@ -66,23 +76,33 @@ - (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height {
[self addParallaxWithView:view andHeight:height andShadow:YES];
}

- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight {
[self addParallaxWithView:view andHeight:height andMinHeight:minHeight andShadow:YES];
}

- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height andShadow:(BOOL)shadow {
[self addParallaxWithView:view andHeight:height andMinHeight:0 andShadow:shadow];
}

- (void)addParallaxWithView:(UIView*)view andHeight:(CGFloat)height andMinHeight:(CGFloat)minHeight andShadow:(BOOL)shadow {
if(self.parallaxView) {
[self.parallaxView.currentSubView removeFromSuperview];
[self.parallaxView.customView removeFromSuperview];
[view setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[self.parallaxView setCustomView:view];
}
else
{
APParallaxView *parallaxView = [[APParallaxView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, height) andShadow:shadow];
[parallaxView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[parallaxView setClipsToBounds:YES];

[parallaxView setCustomView:view];

parallaxView.scrollView = self;
parallaxView.parallaxHeight = height;
parallaxView.parallaxMinHeight = minHeight;

[self addSubview:parallaxView];

parallaxView.originalTopInset = self.contentInset.top;

UIEdgeInsets newInset = self.contentInset;
Expand Down Expand Up @@ -110,14 +130,12 @@ - (void)setShowsParallax:(BOOL)showsParallax {
if(!showsParallax) {
if (self.parallaxView.isObserving) {
[self removeObserver:self.parallaxView forKeyPath:@"contentOffset"];
[self removeObserver:self.parallaxView forKeyPath:@"frame"];
self.parallaxView.isObserving = NO;
}
}
else {
if (!self.parallaxView.isObserving) {
[self addObserver:self.parallaxView forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self.parallaxView forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self.parallaxView forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:&contentOffsetContext];
self.parallaxView.isObserving = YES;
}
}
Expand All @@ -127,6 +145,10 @@ - (BOOL)showsParallax {
return !self.parallaxView.hidden;
}

- (void)didMoveToSuperview {
[super didMoveToSuperview];
}

@end

#pragma mark - ShadowLayer
Expand All @@ -151,7 +173,7 @@ - (void)drawRect:(CGRect)rect {

//// Gradient Declarations
NSArray* gradient3Colors = [NSArray arrayWithObjects:
(id)[UIColor colorWithWhite:0 alpha:0.3].CGColor,
(id)[UIColor colorWithWhite:0 alpha:0.2].CGColor,
(id)[UIColor clearColor].CGColor, nil];
CGFloat gradient3Locations[] = {0, 1};
CGGradientRef gradient3 = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradient3Colors, gradient3Locations);
Expand All @@ -167,7 +189,6 @@ - (void)drawRect:(CGRect)rect {
//// Cleanup
CGGradientRelease(gradient3);
CGColorSpaceRelease(colorSpace);

}

@end
Expand Down Expand Up @@ -221,18 +242,12 @@ - (void)willMoveToSuperview:(UIView *)newSuperview {
if (self.isObserving) {
//If enter this branch, it is the moment just before "APParallaxView's dealloc", so remove observer here
[scrollView removeObserver:self forKeyPath:@"contentOffset"];
[scrollView removeObserver:self forKeyPath:@"frame"];
self.isObserving = NO;
}
}
}
}

- (void)addSubview:(UIView *)view {
[super addSubview:view];
self.currentSubView = view;
}

- (void)setCustomView:(UIView *)customView
{
if (_customView) {
Expand All @@ -241,50 +256,59 @@ - (void)setCustomView:(UIView *)customView

_customView = customView;

[self addSubview:customView];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:nil views:@{@"customView" : customView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:nil views:@{@"customView" : customView}]];
}

- (void)layoutSubviews
{
[super layoutSubviews];

if (self.shadowView) {
[self bringSubviewToFront:self.shadowView];
[self insertSubview:customView belowSubview:self.shadowView];
}
else {
[self addSubview:customView];
}

[self.customView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:nil views:@{@"customView" : self.customView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:nil views:@{@"customView" : self.customView}]];
}

#pragma mark - Observing

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if([keyPath isEqualToString:@"contentOffset"]) {
if(context == &contentOffsetContext) {
[self scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]];
}
else if([keyPath isEqualToString:@"frame"]) {
[self layoutSubviews];
}

- (void)setFrame:(CGRect)frame {
if ([self.delegate respondsToSelector:@selector(parallaxView:willChangeFrame:)]) {
[self.delegate parallaxView:self willChangeFrame:self.frame];
}

[super setFrame:frame];

if ([self.delegate respondsToSelector:@selector(parallaxView:didChangeFrame:)]) {
[self.delegate parallaxView:self didChangeFrame:self.frame];
}
}

- (void)scrollViewDidScroll:(CGPoint)contentOffset {
// We do not want to track when the parallax view is hidden
if (contentOffset.y > 0) {
// We do not want to track when the parallax view is hidden
if (contentOffset.y > 0 && self.parallaxMinHeight == 0) {
[self setState:APParallaxTrackingInactive];
} else {
[self setState:APParallaxTrackingActive];
}

if(self.state == APParallaxTrackingActive) {

if(self.state == APParallaxTrackingActive) {
// Resize/reposition the parallaxView based on the content offset
CGFloat yOffset = contentOffset.y*-1;
if ([self.delegate respondsToSelector:@selector(parallaxView:willChangeFrame:)]) {
[self.delegate parallaxView:self willChangeFrame:self.frame];
CGFloat height = MAX(self.parallaxMinHeight, yOffset);
[self setFrame:CGRectMake(0, contentOffset.y, CGRectGetWidth(self.frame), height)];

// Correct the scroll indicator position
// Without this the scroll indicator will be displayed on top of the parallax view
if (self.scrollView.contentOffset.y < -self.parallaxHeight) {
[self.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(self.scrollView.contentInset.top+(abs(self.scrollView.contentOffset.y)-self.parallaxHeight), 0, 0, 0)];
}

[self setFrame:CGRectMake(0, contentOffset.y, CGRectGetWidth(self.frame), yOffset)];

if ([self.delegate respondsToSelector:@selector(parallaxView:didChangeFrame:)]) {
[self.delegate parallaxView:self didChangeFrame:self.frame];
else {
[self.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(self.scrollView.contentInset.top, 0, 0, 0)];
}
}
}
Expand Down
Loading