diff --git a/Pod/Classes/MXSegmentedPager+ParallaxHeader.h b/Pod/Classes/MXSegmentedPager+ParallaxHeader.h index 65ad264aa..a947119b0 100644 --- a/Pod/Classes/MXSegmentedPager+ParallaxHeader.h +++ b/Pod/Classes/MXSegmentedPager+ParallaxHeader.h @@ -62,3 +62,37 @@ typedef void (^MXProgressBlock) (CGFloat progress); height:(CGFloat)height; @end + +/** + While using MXSegmentedPager with Parallax header, your pages can adopt the MXPageDelegate protocol to have a nice effect while scrolling. + This is useful when you have a page with a scrolling subview (e.g. UIWebView). + */ +@protocol MXPageDelegate + +@optional + +/** + Tells if the view is at its scrolling top. + + @return Whether or nor the view is at its scroll top. + */ +- (BOOL) isAtTop; + +/** + Scrolls to top with no animation. + */ +- (void) scrollToTop; + +@end + +/** + UIScrollView category that adopt the MXPageDelegate protocol. + */ +@interface UIScrollView (MXSegmentedPager) +@end + +/** + UIWebView category that adopt the MXPageDelegate protocol. + */ +@interface UIWebView (MXSegmentedPager) +@end diff --git a/Pod/Classes/MXSegmentedPager+ParallaxHeader.m b/Pod/Classes/MXSegmentedPager+ParallaxHeader.m index 0bd213b75..ae980d73b 100644 --- a/Pod/Classes/MXSegmentedPager+ParallaxHeader.m +++ b/Pod/Classes/MXSegmentedPager+ParallaxHeader.m @@ -36,10 +36,15 @@ @interface MXScrollView : UIScrollView -self.minimumHeigth) { - self.contentOffset = CGPointMake(self.contentOffset.x, -self.minimumHeigth); - } } #pragma mark @@ -89,7 +93,7 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ return YES; } -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } @@ -110,6 +114,52 @@ - (MXPanGestureDirection) getDirectionOfPanGestureRecognizer:(UIPanGestureRecogn return MXPanGestureDirectionNone; } +- (void) didScrollWithDelta:(CGFloat)delta { + + self.moveView = NO; +// [self removeObserver:self forKeyPath:kContentOffsetKeyPath context:kMXScrollViewKVOContext]; + + UIView* page = (id) self.segmentedPager.selectedPage; + + if (page) { + + BOOL isAtTop = ([page respondsToSelector:@selector(isAtTop)])? [page isAtTop] : YES; + + if (self.contentOffset.y > -self.minimumHeigth) { + self.contentOffset = CGPointMake(self.contentOffset.x, -self.minimumHeigth); + } + else if (self.contentOffset.y + delta >= -self.minimumHeigth && !isAtTop) { + self.contentOffset = CGPointMake(self.contentOffset.x, -self.minimumHeigth); + } + + if (self.contentOffset.y < -self.minimumHeigth && [page respondsToSelector:@selector(scrollToTop)]) { + [page scrollToTop]; + } + } + + self.moveView = YES; +// [self addObserver:self forKeyPath:kContentOffsetKeyPath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:kMXScrollViewKVOContext]; +} + +#pragma mark KVO + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + + if (context == kMXScrollViewKVOContext && [keyPath isEqualToString:kContentOffsetKeyPath]) { + + if (self.moveView) { + CGPoint newOffset = [[change objectForKey:NSKeyValueChangeNewKey] CGPointValue]; + CGPoint oldOffset = [[change objectForKey:NSKeyValueChangeOldKey] CGPointValue]; + CGFloat delta = oldOffset.y - newOffset.y; + + [self didScrollWithDelta:delta]; + } + } + else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + @end @interface MXSegmentedPager () @@ -118,7 +168,8 @@ @interface MXSegmentedPager () @implementation MXSegmentedPager (ParallaxHeader) -static NSString* const kContaineFrameKeyPath = @"container.frame"; +static void * const kMXSegmentedPagerKVOContext = (void*)&kMXSegmentedPagerKVOContext; +static NSString* const kFrameKeyPath = @"frame"; - (void)setParallaxHeaderView:(UIView *)view mode:(VGParallaxHeaderMode)mode height:(CGFloat)height { @@ -132,8 +183,7 @@ - (void)setParallaxHeaderView:(UIView *)view mode:(VGParallaxHeaderMode)mode hei [self.scrollView setParallaxHeaderView:view mode:mode height:height]; [self addSubview:self.scrollView]; - //I'm not a big fan of KVO but its the only way I found to subtract minimum height to container frame. - [self addObserver:self forKeyPath:kContaineFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; + [self.container addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:kMXSegmentedPagerKVOContext]; } #pragma mark Properties @@ -169,8 +219,8 @@ - (void)setProgressBlock:(MXProgressBlock)progressBlock { #pragma mark KVO - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (object == self && [keyPath isEqualToString:kContaineFrameKeyPath]) { - [self removeObserver:self forKeyPath:kContaineFrameKeyPath]; + if (context == kMXSegmentedPagerKVOContext && [keyPath isEqualToString:kFrameKeyPath]) { + [self.container removeObserver:self forKeyPath:kFrameKeyPath context:kMXSegmentedPagerKVOContext]; self.container.frame = (CGRect){ .origin = self.container.frame.origin, @@ -178,11 +228,38 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N .size.height = self.container.frame.size.height - self.scrollView.minimumHeigth }; - [self addObserver:self forKeyPath:kContaineFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; + [self.container addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:kMXSegmentedPagerKVOContext]; } else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:nil]; + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } @end + +@implementation UIScrollView (MXSegmentedPager) + +#pragma mark + +- (BOOL) isAtTop { + return (self.contentOffset.y <= -self.contentInset.top); +} + +- (void) scrollToTop { + self.contentOffset = CGPointMake(self.contentOffset.x, -self.contentInset.top); +} + +@end + +@implementation UIWebView (MXSegmentedPager) + +#pragma mark + +- (BOOL) isAtTop { + return (self.scrollView.contentOffset.y <= self.scrollView.contentInset.top); +} + +- (void) scrollToTop { + self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top); +} +@end diff --git a/Pod/Classes/MXSegmentedPager.h b/Pod/Classes/MXSegmentedPager.h index 6e3356dd6..bd2fc8763 100644 --- a/Pod/Classes/MXSegmentedPager.h +++ b/Pod/Classes/MXSegmentedPager.h @@ -148,6 +148,11 @@ */ @property (nonatomic, assign) CGSize containerSize; +/** + The current selected page view. + */ +@property (nonatomic, readonly) UIView* selectedPage; + /** Reloads everything from scratch. redisplays pages in container. */ diff --git a/Pod/Classes/MXSegmentedPager.m b/Pod/Classes/MXSegmentedPager.m index 84bc7e012..42bee0e7a 100644 --- a/Pod/Classes/MXSegmentedPager.m +++ b/Pod/Classes/MXSegmentedPager.m @@ -133,6 +133,14 @@ - (void)setContainerSize:(CGSize)containerSize { }; } +- (UIView*) selectedPage { + NSInteger index = self.segmentedControl.selectedSegmentIndex; + if (self.pages.count > index) { + return [self.pages objectAtIndex:index]; + } + return nil; +} + #pragma mark HMSegmentedControl target - (void)pageControlValueChanged:(HMSegmentedControl*)segmentedControl {