From da699974d1c9333e12c554be1e28a68c1ea1ac46 Mon Sep 17 00:00:00 2001 From: Marco Meschini Date: Thu, 13 Dec 2012 12:53:55 +0000 Subject: [PATCH] Refactor + README --- .../FWTOverlayView.xcodeproj/project.pbxproj | 2 + .../FWTOverlayBlockDefinitions.h | 10 ++ .../FWTOverlayScrollViewHelper.h | 4 + .../FWTOverlayScrollViewHelper.m | 156 ++++++++++-------- .../UIScrollView+FWTOverlayView.h | 24 +-- .../UIScrollView+FWTOverlayView.m | 88 ++++++---- .../UITableView+FWTOverlayView.m | 10 +- .../project.pbxproj | 4 +- .../FWTOverlayView_Test/OverlayView.m | 32 +++- .../SamplePickerViewController.m | 5 +- .../ScrollViewController.h | 2 +- .../ScrollViewController.m | 31 +++- .../FWTOverlayView_Test/TableViewController.m | 49 +++++- README.md | 62 +++++++ 14 files changed, 336 insertions(+), 143 deletions(-) create mode 100644 FWTOverlayView/FWTOverlayView/FWTOverlayBlockDefinitions.h diff --git a/FWTOverlayView/FWTOverlayView.xcodeproj/project.pbxproj b/FWTOverlayView/FWTOverlayView.xcodeproj/project.pbxproj index e42cddf..a60ab38 100644 --- a/FWTOverlayView/FWTOverlayView.xcodeproj/project.pbxproj +++ b/FWTOverlayView/FWTOverlayView.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A923ED621678A39300709A25 /* FWTOverlayBlockDefinitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FWTOverlayBlockDefinitions.h; sourceTree = ""; }; A997599D1611C35A00BFB780 /* libFWTOverlayView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFWTOverlayView.a; sourceTree = BUILT_PRODUCTS_DIR; }; A99759A01611C35A00BFB780 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; A99759A41611C35A00BFB780 /* FWTOverlayView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FWTOverlayView-Prefix.pch"; sourceTree = ""; }; @@ -118,6 +119,7 @@ A99759B81611C3B300BFB780 /* Private */ = { isa = PBXGroup; children = ( + A923ED621678A39300709A25 /* FWTOverlayBlockDefinitions.h */, A99759AE1611C39800BFB780 /* FWTOverlayScrollViewHelper.h */, A99759AF1611C39800BFB780 /* FWTOverlayScrollViewHelper.m */, ); diff --git a/FWTOverlayView/FWTOverlayView/FWTOverlayBlockDefinitions.h b/FWTOverlayView/FWTOverlayView/FWTOverlayBlockDefinitions.h new file mode 100644 index 0000000..af53621 --- /dev/null +++ b/FWTOverlayView/FWTOverlayView/FWTOverlayBlockDefinitions.h @@ -0,0 +1,10 @@ +// +// FWTOverlayBlockDefinitions.h +// FWTOverlayView +// +// Created by Marco Meschini on 12/12/2012. +// Copyright (c) 2012 Marco Meschini. All rights reserved. +// + +typedef void (^FWTOverlayLayoutBlock)(BOOL); +typedef void (^FWTOverlayDismissBlock)(); \ No newline at end of file diff --git a/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.h b/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.h index d611fb9..d8beeaf 100644 --- a/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.h +++ b/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.h @@ -8,6 +8,7 @@ #import #import +#import "FWTOverlayBlockDefinitions.h" // This is a private class. You can access its properties through the UIScrollView (FWTOverlay) category. // @@ -17,6 +18,9 @@ @property (nonatomic, retain) UIView *overlayView; @property (nonatomic, assign) UIEdgeInsets edgeInsets; @property (nonatomic, assign) CGFloat hideAfterDelay; +@property (nonatomic, assign) UIViewAutoresizing flexibleMargin; +@property (nonatomic, copy) FWTOverlayLayoutBlock layoutBlock; +@property (nonatomic, copy) FWTOverlayDismissBlock dismissBlock; - (id)initWithScrollView:(UIScrollView *)scrollView; diff --git a/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.m b/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.m index 9c5dde2..0430ead 100644 --- a/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.m +++ b/FWTOverlayView/FWTOverlayView/FWTOverlayScrollViewHelper.m @@ -11,7 +11,7 @@ #import #import "UIScrollView+FWTOverlayView.h" -#define DEBUG_ENABLED YES +#define DEBUG_ENABLED NO static BOOL isMethodPartOfProtocol(SEL aSelector, Protocol *aProtocol) { @@ -44,9 +44,9 @@ @interface FWTOverlayScrollViewHelper () @property (nonatomic, assign) UIScrollView *scrollView; @property (nonatomic, assign) id realDelegate; @property (nonatomic, retain) UIView *debugView; -@property (nonatomic, assign) CGFloat presentAnimationDuration, dismissAnimationDuration; -@property (nonatomic, assign) CGFloat slideOffset; @property (nonatomic, assign, getter = isOverlayViewVisible) BOOL overlayViewVisible; +@property (nonatomic, assign, getter = isDismissAnimationRunning) BOOL dismissAnimationRunning; +@property (nonatomic, assign) BOOL overlayViewWillDisappear; @end @@ -55,6 +55,8 @@ @implementation FWTOverlayScrollViewHelper - (void)dealloc { + self.dismissBlock = NULL; + self.layoutBlock = NULL; self.debugView = nil; self.overlayView = nil; self.realDelegate = nil; @@ -68,9 +70,6 @@ - (id)initWithScrollView:(UIScrollView *)scrollView { self.scrollView = scrollView; self.hideAfterDelay = 2.0f; - self.presentAnimationDuration = .2f; - self.dismissAnimationDuration = .2f; - self.slideOffset = 20.0f; } return self; @@ -96,6 +95,7 @@ - (void)setScrollView:(UIScrollView *)scrollView // [self->_scrollView addObserver:self forKeyPath:keyPathDelegate options:NSKeyValueObservingOptionNew context:NULL]; [self->_scrollView addObserver:self forKeyPath:keyPathFrame options:NSKeyValueObservingOptionNew context:NULL]; + //TODO: add observer for contentSize } } } @@ -168,6 +168,49 @@ - (UIView *)debugView return self->_debugView; } +- (FWTOverlayLayoutBlock)layoutBlock +{ + if (self->_layoutBlock == NULL) + { + __block typeof(self) myself = self; + self->_layoutBlock = [^(BOOL animated){ + if (animated) + { + CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"opacity"]; + anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; + anim.fromValue = @.0f; + anim.toValue = @1.0f; + myself.overlayView.layer.opacity = 1.0f; + [myself.overlayView.layer addAnimation:anim forKey:@"animation"]; + } + else + myself.overlayView.layer.opacity = 1.0f; + + } copy]; + } + + return self->_layoutBlock; +} + +- (FWTOverlayDismissBlock)dismissBlock +{ + if (self->_dismissBlock == NULL) + { + __block typeof(self) myself = self; + self->_dismissBlock = [^(){ + CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"opacity"]; + anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; + anim.fromValue = @1.0f; + anim.toValue = @.0f; + myself.overlayView.layer.opacity = .0f; + [myself.overlayView.layer addAnimation:anim forKey:@"animation"]; + + } copy]; + } + + return self->_dismissBlock; +} + #pragma mark - Private - (void)_swapDelegateForScrollView:(UIScrollView *)scrollView { @@ -204,94 +247,67 @@ - (void)_layoutOverlayView { if (!self.overlayView.superview) { + self.overlayView.frame = [self _overlayFrame]; [self.scrollView addSubview:self.overlayView]; - self.overlayView.alpha = .0f; - [UIView animateWithDuration:self.presentAnimationDuration - animations:^{ - self.overlayView.alpha = 1.0f; - self.overlayView.transform = CGAffineTransformMakeTranslation(-self.slideOffset, 0.0f); - }]; + self.layoutBlock(YES); } else { [self.scrollView bringSubviewToFront:self.overlayView]; self.overlayView.frame = [self _overlayFrame]; - self.overlayView.alpha = 1.0f; + self.layoutBlock(NO); } } - (CGRect)_overlayFrame { - // Work out positions - CGFloat currentTablePositionPercentage = [self.scrollView fwt_contentOffsetPercentageClampEnabled:YES]; + CGPoint contentOffsetRelative = [self.scrollView fwt_relativeContentOffsetNormalized:YES]; CGSize overlaySize = self.overlayView.frame.size; - CGRect overlayFrame = [self _overlayBounds]; + CGRect overlayAvailableFrame = [self _overlayBounds]; + CGRect toReturn = overlayAvailableFrame; + toReturn.origin.x += ((CGRectGetWidth(overlayAvailableFrame)-overlaySize.width)*contentOffsetRelative.x); + toReturn.origin.y += ((CGRectGetHeight(overlayAvailableFrame)-overlaySize.height)*contentOffsetRelative.y); + toReturn.size = overlaySize; - FWTScrollViewDirection direction = [self.scrollView fwt_scrollDirection]; - if (direction == FWTScrollViewDirectionVertical) + if (self.flexibleMargin == UIViewAutoresizingFlexibleLeftMargin) { - overlayFrame.origin.y += ((CGRectGetHeight(overlayFrame)-overlaySize.height)*currentTablePositionPercentage); // adjust y - overlayFrame.size.height = overlaySize.height; + toReturn.origin.x += CGRectGetWidth(overlayAvailableFrame)-overlaySize.width; } - else if (direction == FWTScrollViewDirectionHorizontal) + else if (self.flexibleMargin == UIViewAutoresizingFlexibleTopMargin) { - overlayFrame.origin.x += ((CGRectGetWidth(overlayFrame)-overlaySize.width)*currentTablePositionPercentage); // adjust y - overlayFrame.size.width = overlaySize.width; + toReturn.origin.y += CGRectGetHeight(overlayAvailableFrame)-overlaySize.height; } - - return overlayFrame; + + return toReturn; } - (CGRect)_overlayBounds { - CGSize overlaySize = self.overlayView.frame.size; - CGRect toReturn = UIEdgeInsetsInsetRect(self.scrollView.bounds, self.edgeInsets); - - FWTScrollViewDirection direction = [self.scrollView fwt_scrollDirection]; - if (direction == FWTScrollViewDirectionVertical) - { - if (self.edgeInsets.left == .0f) - toReturn.origin.x += CGRectGetWidth(toReturn)-overlaySize.width; - - toReturn.size.width = overlaySize.width; - } - else if (direction == FWTScrollViewDirectionHorizontal) - { - if (self.edgeInsets.top == .0f) - toReturn.origin.y += CGRectGetHeight(toReturn)-overlaySize.height; - - toReturn.size.height = overlaySize.height; - } - - return toReturn; + return UIEdgeInsetsInsetRect(self.scrollView.bounds, self.edgeInsets); } - (void)_dismissOverlayView { - if (!_dismissOverlayViewAnimating) + if (![self isDismissAnimationRunning]) { - _dismissOverlayViewAnimating = YES; - - [UIView animateWithDuration:self.dismissAnimationDuration - animations:^{ - self.overlayView.alpha = .0f; - self.overlayView.frame = CGRectOffset(self.overlayView.frame, self.slideOffset, .0f); - } - completion:^(BOOL finished) { - - _dismissOverlayViewAnimating = NO; - - // check if we did have a scroll during the animation run - if (self.overlayView.alpha == .0f) - { - [self.debugView removeFromSuperview]; - [self.overlayView removeFromSuperview]; - self.overlayView.transform = CGAffineTransformIdentity; - - self.overlayViewVisible = NO; - } - } - ]; + self.dismissAnimationRunning = YES; + self.overlayViewWillDisappear = YES; + + __block typeof(self) myself = self; + [CATransaction begin]; + [CATransaction setCompletionBlock:^{ + if (myself.overlayViewWillDisappear) + { + [myself.debugView removeFromSuperview]; + [myself.overlayView removeFromSuperview]; + myself.overlayViewVisible = NO; + } + + myself.dismissAnimationRunning = NO; + }]; + + self.dismissBlock(); + [CATransaction commit]; } } @@ -299,6 +315,8 @@ - (void)_dismissOverlayView - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { self.overlayViewVisible = YES; + self.overlayViewWillDisappear = NO; + [self.overlayView.layer removeAllAnimations]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_dismissOverlayView) object:nil]; @@ -326,10 +344,8 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - // [self _layoutSubviews]; - // if (_realDelegateHas.scrollViewDidScroll) [self.realDelegate scrollViewDidScroll:scrollView]; } diff --git a/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.h b/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.h index 4796585..a1b8a58 100644 --- a/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.h +++ b/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.h @@ -7,24 +7,24 @@ // #import +#import "FWTOverlayBlockDefinitions.h" + +@interface UIScrollView (FWTRelativeContentOffset) + +@property (nonatomic) CGPoint fwt_relativeContentOffset; +- (CGPoint)fwt_relativeContentOffsetNormalized:(BOOL)normalized; + +@end -typedef enum _FWTScrollViewDirection -{ - FWTScrollViewDirectionNone, - FWTScrollViewDirectionVertical, - FWTScrollViewDirectionHorizontal, -} FWTScrollViewDirection; @interface UIScrollView (FWTOverlayView) +// configure @property (nonatomic, retain) UIView *fwt_overlayView; @property (nonatomic, assign) UIEdgeInsets fwt_overlayViewEdgeInsets; +@property (nonatomic, assign) UIViewAutoresizing fwt_overlayViewFlexibleMargin; @property (nonatomic, assign) CGFloat fwt_overlayViewHideAfterDelay; -@property (nonatomic, readonly, assign) CGPoint fwt_overlayViewCenter; - -@property (nonatomic, readonly, assign) CGFloat fwt_contentOffsetPercentage; -- (CGFloat)fwt_contentOffsetPercentageClampEnabled:(BOOL)clampEnabled; - -@property (nonatomic, readonly, assign) FWTScrollViewDirection fwt_scrollDirection; +@property (nonatomic, copy) FWTOverlayLayoutBlock fwt_layoutBlock; +@property (nonatomic, copy) FWTOverlayDismissBlock fwt_dismissBlock; @end diff --git a/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.m b/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.m index df68b52..42ad00e 100644 --- a/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.m +++ b/FWTOverlayView/FWTOverlayView/UIScrollView+FWTOverlayView.m @@ -10,9 +10,43 @@ #import "FWTOverlayScrollViewHelper.h" #import +@implementation UIScrollView (FWTRelativeContentOffset) + +- (CGPoint)fwt_relativeContentOffset +{ + return [self fwt_relativeContentOffsetNormalized:NO]; +} + +- (CGPoint)fwt_relativeContentOffsetNormalized:(BOOL)normalized +{ + CGPoint toReturn = CGPointZero; + CGSize contentSize = self.contentSize; + CGSize frameSize = self.frame.size; + CGPoint contentOffset = self.contentOffset; + CGSize workingSize = (CGSize){contentSize.width-frameSize.width, contentSize.height-frameSize.height}; + toReturn.x = isnan(contentOffset.x/workingSize.width) ? .0f : contentOffset.x/workingSize.width; + toReturn.y = isnan(contentOffset.y/workingSize.height) ? .0f : contentOffset.y/workingSize.height; + if (normalized) + { + toReturn.x = MAX(toReturn.x, .0f); + toReturn.x = MIN(toReturn.x, 1.0f); + toReturn.y = MAX(toReturn.y, .0f); + toReturn.y = MIN(toReturn.y, 1.0f); + } + + return toReturn; +} + +- (void)setFwt_relativeContentOffset:(CGPoint)fwt_relativeContentOffset +{ + +} + +@end + static char overlayHelperKey; -@interface UIScrollView () +@interface UIScrollView (FWTOverlayView_Private) @property (nonatomic, retain) FWTOverlayScrollViewHelper *fwt_overlayHelper; @end @@ -63,6 +97,18 @@ - (UIEdgeInsets)fwt_overlayViewEdgeInsets return helper.edgeInsets; } +- (void)setFwt_overlayViewFlexibleMargin:(UIViewAutoresizing)fwt_overlayViewFlexibleMargin +{ + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:YES]; + helper.flexibleMargin = fwt_overlayViewFlexibleMargin; +} + +- (UIViewAutoresizing)fwt_overlayViewFlexibleMargin +{ + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:NO]; + return helper.flexibleMargin; +} + - (void)setFwt_overlayViewHideAfterDelay:(CGFloat)fwt_hideAfterDelay { FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:YES]; @@ -75,44 +121,28 @@ - (CGFloat)fwt_overlayViewHideAfterDelay return helper.hideAfterDelay; } -- (CGPoint)fwt_overlayViewCenter +- (FWTOverlayLayoutBlock)fwt_layoutBlock { - return self.fwt_overlayView.center; + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:NO]; + return helper.layoutBlock; } -- (CGFloat)fwt_contentOffsetPercentage +- (void)setFwt_layoutBlock:(FWTOverlayLayoutBlock)fwt_layoutBlock { - return [self fwt_contentOffsetPercentageClampEnabled:NO]; + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:YES]; + helper.layoutBlock = fwt_layoutBlock; } -- (CGFloat)fwt_contentOffsetPercentageClampEnabled:(BOOL)clampEnabled +- (FWTOverlayDismissBlock)fwt_dismissBlock { - BOOL isHorizontal = [self fwt_scrollDirection] == FWTScrollViewDirectionHorizontal ? YES : NO; - CGFloat contentSize = isHorizontal ? self.contentSize.width : self.contentSize.height; - CGFloat frameSize = isHorizontal ? self.frame.size.width : self.frame.size.height; - CGFloat currentTablePosition = isHorizontal ? self.contentOffset.x : self.contentOffset.y; - - CGFloat workingTableHeight = contentSize - frameSize; - CGFloat currentTablePositionPercentage = currentTablePosition / workingTableHeight; - - if (clampEnabled) - { - currentTablePositionPercentage = MAX(currentTablePositionPercentage, .0f); - currentTablePositionPercentage = MIN(currentTablePositionPercentage, 1.0f); - } - - return currentTablePositionPercentage; + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:NO]; + return helper.dismissBlock; } -- (FWTScrollViewDirection)fwt_scrollDirection +- (void)setFwt_dismissBlock:(FWTOverlayDismissBlock)fwt_dismissBlock { - FWTScrollViewDirection toReturn = FWTScrollViewDirectionNone; - if (self.contentSize.width > CGRectGetWidth(self.frame)) - toReturn = FWTScrollViewDirectionHorizontal; - else if (self.contentSize.height > CGRectGetHeight(self.frame)) - toReturn = FWTScrollViewDirectionVertical; - - return toReturn; + FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:YES]; + helper.dismissBlock = fwt_dismissBlock; } @end diff --git a/FWTOverlayView/FWTOverlayView/UITableView+FWTOverlayView.m b/FWTOverlayView/FWTOverlayView/UITableView+FWTOverlayView.m index e0dc6af..20cc88e 100644 --- a/FWTOverlayView/FWTOverlayView/UITableView+FWTOverlayView.m +++ b/FWTOverlayView/FWTOverlayView/UITableView+FWTOverlayView.m @@ -9,16 +9,12 @@ #import "UITableView+FWTOverlayView.h" #import "FWTOverlayScrollViewHelper.h" -@interface UIScrollView () -- (FWTOverlayScrollViewHelper *)_getAssociatedScrollViewHelperAndInitIfNeeded:(BOOL)value; -@end - @implementation UITableView (FWTOverlayView) - (NSIndexPath *)fwt_overlayViewIndexPath { - FWTOverlayScrollViewHelper *helper = [self _getAssociatedScrollViewHelperAndInitIfNeeded:NO]; - if (helper) + UIView *overlayView = self.fwt_overlayView; + if (overlayView) { if (self.contentOffset.y < 0) { @@ -34,7 +30,7 @@ - (NSIndexPath *)fwt_overlayViewIndexPath } else { - return [self indexPathForRowAtPoint:helper.overlayView.center]; + return [self indexPathForRowAtPoint:overlayView.center]; } } return nil; diff --git a/FWTOverlayView_Test/FWTOverlayView_Test.xcodeproj/project.pbxproj b/FWTOverlayView_Test/FWTOverlayView_Test.xcodeproj/project.pbxproj index d66d620..5b570fb 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test.xcodeproj/project.pbxproj +++ b/FWTOverlayView_Test/FWTOverlayView_Test.xcodeproj/project.pbxproj @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../FWTOverlayView/FWTOverlayView/\""; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.1; OTHER_LDFLAGS = "-ObjC"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -267,7 +267,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../FWTOverlayView/FWTOverlayView/\""; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.1; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; OTHER_LDFLAGS = "-ObjC"; SDKROOT = iphoneos; diff --git a/FWTOverlayView_Test/FWTOverlayView_Test/OverlayView.m b/FWTOverlayView_Test/FWTOverlayView_Test/OverlayView.m index 4f3a6f6..2856fd7 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test/OverlayView.m +++ b/FWTOverlayView_Test/FWTOverlayView_Test/OverlayView.m @@ -32,14 +32,19 @@ - (id)initWithFrame:(CGRect)frame self = [super initWithFrame:frame]; if (self) { - self.layer.shadowOpacity = .2f; - self.layer.shadowRadius = 6.0f; - self.layer.shadowOffset = CGSizeMake(.0f, 2.0f); - self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; +// self.layer.shadowOpacity = .2f; +// self.layer.shadowRadius = 6.0f; +// self.layer.shadowOffset = CGSizeMake(.0f, 2.0f); +// self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; } return self; } +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; +} + - (void)layoutSubviews { [super layoutSubviews]; @@ -109,15 +114,26 @@ - (UIImageView *)imageView - (UIImage *)_backgroundImage { - CGSize ctxSize = CGSizeMake(10.0f, 10.0f); + CGSize ctxSize = CGSizeMake(20.0f, 20.0f); CGRect ctxRect = CGRectMake(.0f, .0f, ctxSize.width, ctxSize.height); UIGraphicsBeginImageContextWithOptions(ctxSize, NO, .0f); - ctxRect = CGRectInset(ctxRect, 1, 1); + ctxRect = CGRectInset(ctxRect, 5, 5); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + UIBezierPath *bp = [UIBezierPath bezierPathWithRoundedRect:ctxRect cornerRadius:3]; + + CGContextSaveGState(ctx); + CGContextSetShadowWithColor(ctx, CGSizeMake(.0f, 1.0f), 4.0f, [UIColor blackColor].CGColor); + [[UIColor blackColor] set]; + [bp fill]; + [bp addClip]; + CGContextClearRect(ctx, ctxRect); + CGContextRestoreGState(ctx); + [[[UIColor whiteColor] colorWithAlphaComponent:.7f] set]; [bp stroke]; - bp = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(ctxRect, 2, 2) cornerRadius:3]; + bp = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(ctxRect, 2, 2) cornerRadius:2]; [[[UIColor whiteColor] colorWithAlphaComponent:.5f] set]; [bp fill]; [bp stroke]; @@ -125,7 +141,7 @@ - (UIImage *)_backgroundImage // UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - return [image resizableImageWithCapInsets:UIEdgeInsetsMake(5.0f, 5.0f, 4.0f, 4.0f)]; + return [image resizableImageWithCapInsets:UIEdgeInsetsMake(10.0f, 10.0f, 9.0f, 9.0f)]; } @end diff --git a/FWTOverlayView_Test/FWTOverlayView_Test/SamplePickerViewController.m b/FWTOverlayView_Test/FWTOverlayView_Test/SamplePickerViewController.m index 9935364..274a3e4 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test/SamplePickerViewController.m +++ b/FWTOverlayView_Test/FWTOverlayView_Test/SamplePickerViewController.m @@ -45,8 +45,9 @@ - (NSArray *)items { if (!self->_items) self->_items = [@[ - [ScrollViewController scrollViewControllerWithContentSize:CGSizeMake(CGRectGetWidth(self.view.frame), 3000.0f)], - [ScrollViewController scrollViewControllerWithContentSize:CGSizeMake(3000.0f, CGRectGetHeight(self.view.frame))], + [ScrollViewController scrollViewControllerWithContentSize:CGSizeMake(CGRectGetWidth(self.view.frame), 3000.0f) title:@"vertical"], + [ScrollViewController scrollViewControllerWithContentSize:CGSizeMake(3000.0f, CGRectGetHeight(self.view.frame)) title:@"horizontal"], + [ScrollViewController scrollViewControllerWithContentSize:CGSizeMake(3000.0f, 3000.0f) title:@"vertical+horizontal"], [[[TableViewController alloc] init] autorelease], ] retain]; diff --git a/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.h b/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.h index ebb1c68..4fd4307 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.h +++ b/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.h @@ -10,6 +10,6 @@ @interface ScrollViewController : UIViewController -+ (id)scrollViewControllerWithContentSize:(CGSize)contentSize; ++ (id)scrollViewControllerWithContentSize:(CGSize)contentSize title:(NSString *)title; @end diff --git a/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.m b/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.m index bf8cafd..6f0074e 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.m +++ b/FWTOverlayView_Test/FWTOverlayView_Test/ScrollViewController.m @@ -33,14 +33,26 @@ - (void)loadView self.scrollView.delegate = self; [self.view addSubview:self.scrollView]; - OverlayView *overlayView = [[[OverlayView alloc] initWithFrame:CGRectMake(.0f, .0f, 70.0f, 30.0f)] autorelease]; + OverlayView *overlayView = [[[OverlayView alloc] initWithFrame:CGRectMake(.0f, .0f, 80.0f, 34.0f)] autorelease]; overlayView.textLabel.numberOfLines = 0; self.scrollView.fwt_overlayView = overlayView; - if (self.contentSize.width > self.contentSize.height) - self.scrollView.fwt_overlayViewEdgeInsets = UIEdgeInsetsMake(2.0f, 2.0f, .0f, 2.0f); - else - self.scrollView.fwt_overlayViewEdgeInsets = UIEdgeInsetsMake(2.0f, .0f, 2.0f, 10.0f); + CGSize viewSize = self.view.frame.size; + if (self.contentSize.width > viewSize.width && self.contentSize.height > viewSize.height) + { +// self.scrollView.fwt_overlayViewEdgeInsets = UIEdgeInsetsMake(10.0f, 10.0f, 200.0f, 200.0f); + } + else if (self.contentSize.width > self.view.frame.size.width) + { + self.scrollView.fwt_overlayViewEdgeInsets = UIEdgeInsetsMake(2.0f, 2.0f, 10.0f, 2.0f); +// self.scrollView.fwt_overlayViewFlexibleMargin = UIViewAutoresizingFlexibleTopMargin; + } + else if (self.contentSize.height > viewSize.height) + { + self.scrollView.fwt_overlayViewEdgeInsets = (UIEdgeInsets){2.0f, 2.0f, 2.0f, 10.0f}; + self.scrollView.fwt_overlayViewFlexibleMargin = UIViewAutoresizingFlexibleLeftMargin; + + } } - (void)viewDidAppear:(BOOL)animated @@ -51,16 +63,17 @@ - (void)viewDidAppear:(BOOL)animated - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - CGFloat percentage = [scrollView fwt_contentOffsetPercentageClampEnabled:NO]; + CGPoint relativeContentOffset = [scrollView fwt_relativeContentOffsetNormalized:NO]; OverlayView *overlayView = (OverlayView *)scrollView.fwt_overlayView; - overlayView.textLabel.text = [NSString stringWithFormat:@"%f", percentage]; + overlayView.textLabel.text = [NSString stringWithFormat:@"%@", NSStringFromCGPoint(relativeContentOffset)]; } -+ (id)scrollViewControllerWithContentSize:(CGSize)contentSize ++ (id)scrollViewControllerWithContentSize:(CGSize)contentSize title:(NSString *)title { ScrollViewController *toReturn = [[[ScrollViewController alloc] init] autorelease]; toReturn.contentSize = contentSize; - toReturn.title = contentSize.width > contentSize.height ? @"horizontal" : @"vertical"; + toReturn.title = title; +// toReturn.title = contentSize.width > contentSize.height ? @"horizontal" : @"vertical"; return toReturn; } diff --git a/FWTOverlayView_Test/FWTOverlayView_Test/TableViewController.m b/FWTOverlayView_Test/FWTOverlayView_Test/TableViewController.m index a1a6e78..2542777 100644 --- a/FWTOverlayView_Test/FWTOverlayView_Test/TableViewController.m +++ b/FWTOverlayView_Test/FWTOverlayView_Test/TableViewController.m @@ -9,6 +9,7 @@ #import "TableViewController.h" #import "OverlayView.h" #import "UITableView+FWTOverlayView.h" +#import @interface Item : NSObject @property (nonatomic, retain) NSDate *date; @@ -56,8 +57,50 @@ - (void)loadView self.tableView.backgroundColor = [UIColor colorWithWhite:.8f alpha:1.0f]; - self.tableView.fwt_overlayView = [[[OverlayView alloc] initWithFrame:CGRectMake(.0f, .0f, 70.0f, 30.0f)] autorelease]; - self.tableView.fwt_overlayViewEdgeInsets = UIEdgeInsetsMake(2.0f, .0f, 2.0f, 10.0f); + UIView *overlayView = [[[OverlayView alloc] initWithFrame:CGRectMake(.0f, .0f, 80.0f, 34.0f)] autorelease]; + self.tableView.fwt_overlayView = overlayView; + self.tableView.fwt_overlayViewEdgeInsets = (UIEdgeInsets){2.0f, 2.0f, 2.0f, 10.0f}; + self.tableView.fwt_overlayViewFlexibleMargin = UIViewAutoresizingFlexibleLeftMargin; + + __block typeof(overlayView) weakOverlayView = overlayView; + self.tableView.fwt_layoutBlock = ^(BOOL animated){ + if (animated) + { + // + CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"]; + opacity.fromValue = @.0f; + opacity.toValue = @1.0f; + + CABasicAnimation *translation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; + translation.fromValue = @40; + + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.animations = @[opacity, translation]; + group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; + weakOverlayView.layer.opacity = 1.0f; + [weakOverlayView.layer addAnimation:group forKey:@"groupAnimation"]; + } + else + { + weakOverlayView.layer.opacity = 1.0f; + } + }; + + self.tableView.fwt_dismissBlock = ^(){ + // + CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"]; + opacity.fromValue = @1.0f; + opacity.toValue = @.0f; + + CABasicAnimation *translation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; + translation.toValue = @40; + + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.animations = @[opacity, translation]; + group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; + weakOverlayView.layer.opacity = .0f; + [weakOverlayView.layer addAnimation:group forKey:@"groupAnimation"]; + }; } #pragma mark - Getters @@ -129,8 +172,8 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - OverlayView *overlayView = (OverlayView *)scrollView.fwt_overlayView; Item *item = [self.data objectAtIndex:self.tableView.fwt_overlayViewIndexPath.row]; + OverlayView *overlayView = (OverlayView *)scrollView.fwt_overlayView; overlayView.textLabel.text = item.timeString; overlayView.detailTextLabel.text = item.dateString; } diff --git a/README.md b/README.md index e69de29..25079f3 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,62 @@ +#FWTOverlayView + +![FWTOverlayView screenshot](http://grab.by/ige8) + +FWTOverlayView extends scrollview and make it easy to add a floating overlay view. The overlay view hovers through your scrollview frame and helps tracking the current offset. +This component is inspired by Path app and tries to expose a generic and flexible interface. +It uses associative reference to add a custom property to a UIScrollView category. + + +##Requirements +* XCode 4.4.1 or higher +* iOS 5.0 + +##Features +FWTOverlayView is a small collection of objective-c categories. + +* **UIScrollView (FWTRelativeContentOffset)**, adds the relative content offset property +* **UIScrollView (FWTOverlayView)** hides a private helper class and exposes a small set of properties to make the overlay view customization easy +* **UITableView (FWTOverlayView)** adds a handy property to get the indexPath from the current overlay view position. + +##How to use it: configure + +####UIScrollView (FWTRelativeContentOffset) + +* **fwt_relativeContentOffset** the point, in the unit coordinate space, at which the origin of the content view is offset from the origin of the scroll view +* **fwt_relativeContentOffsetNormalized:(BOOL)normalized** returns the relative content offset and if enabled it clamps to [0, 1] values + +####UIScrollView (FWTOverlayView) + +* **fwt_overlayView** the custom view to display on top of the scrollview +* **fwt_overlayViewEdgeInsets** the inset or outset margins for the edges of the available overlay view area +* **fwt_overlayViewFlexibleMargin** +* **fwt_overlayViewHideAfterDelay** +* **fwt_layoutBlock** +* **fwt_dismissBlock** + +####UITableView (FWTOverlayView) + +* **fwt_overlayViewIndexPath** returns the index path identifying the row and section below the overlay view + +##For your interest +say about **FWTOverlayScrollViewHelper** + +##Demo +The sample project shows how to use the categories and how to create a custom overlay view. + +``` objective-c + + CGRect frame = CGRectMake(.0f, .0f, 80.0f, 34.0f); + UIView *overlayView = [[[OverlayView alloc] initWithFrame:frame] autorelease]; + self.tableView.fwt_overlayView = overlayView; + self.tableView.fwt_overlayViewEdgeInsets = (UIEdgeInsets){2.0f, 2.0f, 2.0f, 10.0f}; + self.tableView.fwt_overlayViewFlexibleMargin = UIViewAutoresizingFlexibleLeftMargin; + +``` + +##Licensing +Apache License Version 2.0 + +##Support, bugs and feature requests +If you want to submit a feature request, please do so via the issue tracker on github. +If you want to submit a bug report, please also do so via the issue tracker, including a diagnosis of the problem and a suggested fix (in code).