Skip to content

Commit

Permalink
Updated README and finished project
Browse files Browse the repository at this point in the history
  • Loading branch information
shaps-test committed Apr 5, 2014
1 parent f2b3732 commit 22f318b
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 50 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Purpose
--------------

SPXMasking is category on CALayer that allows you to specify different a corner radius for each corner of a CALayer.
I recenty needed to build the iMessage style pull-to-reveal timestamps feature for a personal project and decided to open source the category. :)


Supported OS & SDK Versions
-----------------------------

* Supported build target - iOS 6.0
* Earliest supported deployment target - iOS 6.0


ARC Compatibility
------------------

The category will work correctly ONLY with ARC enabled.


Installation
--------------

To install, either copy the category into your project or add it to your podfile.

pod 'SPXRevealableView'


Usage
-------

Using this component is almost completely drop in, just follow a few simple steps:

1. #import "UITableView+SPXRevealAdditions.h"
2. [self.tableView enableRevealableViewForDirection:SPXRevealableViewGestureDirectionLeft]; (e.g. in viewDidLoad)
3. cell.revealableView = timestampView;
To gain the benefits of reusable cells, I recommended setting the revealableView in your `-awakeFromNib` cell method, but you could declare it directly in your `-cellForRowAtIndexPath` method as is shown in the included demo.

That's it! It will automatically handle inserting the view into the cells, you have a nice property on each cell to make it simple to update and all gesture handling is done automatically for you just by including the class.


Feel free to use in any way you see fit. Please try and reference me somewhere in your app if you use this in a production app and maybe even tell me about it via Twitter @shaps ;)
23 changes: 16 additions & 7 deletions iMessageStyleReveal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
54D217EE18EF5A6100A19583 /* iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54D217ED18EF5A6100A19583 /* iPhone.storyboard */; };
54D217F118EF5AB400A19583 /* SPXRevealTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D217F018EF5AB400A19583 /* SPXRevealTableViewController.m */; };
54D217F318EF5EC700A19583 /* TimestampView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54D217F218EF5EC700A19583 /* TimestampView.xib */; };
54D217F918EF681300A19583 /* UITableView+SPXRevealAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D217F818EF681300A19583 /* UITableView+SPXRevealAdditions.m */; };
54D2180118EF8DCD00A19583 /* UITableView+SPXRevealAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D217F818EF681300A19583 /* UITableView+SPXRevealAdditions.m */; };
F0B70066D2334B38B0736746 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8022ABD4D229429E94B14AE1 /* libPods.a */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -91,6 +91,7 @@
54D217B218EF5A4500A19583 = {
isa = PBXGroup;
children = (
54D2180018EF8CF100A19583 /* SPXRevealableView */,
54D217C418EF5A4500A19583 /* iMessageStyleReveal */,
54D217DD18EF5A4500A19583 /* iMessageStyleRevealTests */,
54D217BD18EF5A4500A19583 /* Frameworks */,
Expand Down Expand Up @@ -123,15 +124,10 @@
54D217C418EF5A4500A19583 /* iMessageStyleReveal */ = {
isa = PBXGroup;
children = (
54D217F718EF681300A19583 /* UITableView+SPXRevealAdditions.h */,
54D217F818EF681300A19583 /* UITableView+SPXRevealAdditions.m */,
54D217CD18EF5A4500A19583 /* SPXAppDelegate.h */,
54D217CE18EF5A4500A19583 /* SPXAppDelegate.m */,
54D217EF18EF5AB400A19583 /* SPXRevealTableViewController.h */,
54D217F018EF5AB400A19583 /* SPXRevealTableViewController.m */,
54D217F218EF5EC700A19583 /* TimestampView.xib */,
54D217ED18EF5A6100A19583 /* iPhone.storyboard */,
54D217D018EF5A4500A19583 /* Images.xcassets */,
54D217C518EF5A4500A19583 /* Supporting Files */,
);
path = iMessageStyleReveal;
Expand All @@ -140,10 +136,13 @@
54D217C518EF5A4500A19583 /* Supporting Files */ = {
isa = PBXGroup;
children = (
54D217CD18EF5A4500A19583 /* SPXAppDelegate.h */,
54D217CE18EF5A4500A19583 /* SPXAppDelegate.m */,
54D217C618EF5A4500A19583 /* iMessageStyleReveal-Info.plist */,
54D217C718EF5A4500A19583 /* InfoPlist.strings */,
54D217CA18EF5A4500A19583 /* main.m */,
54D217CC18EF5A4500A19583 /* iMessageStyleReveal-Prefix.pch */,
54D217D018EF5A4500A19583 /* Images.xcassets */,
);
name = "Supporting Files";
sourceTree = "<group>";
Expand All @@ -166,6 +165,16 @@
name = "Supporting Files";
sourceTree = "<group>";
};
54D2180018EF8CF100A19583 /* SPXRevealableView */ = {
isa = PBXGroup;
children = (
54D217F718EF681300A19583 /* UITableView+SPXRevealAdditions.h */,
54D217F818EF681300A19583 /* UITableView+SPXRevealAdditions.m */,
);
name = SPXRevealableView;
path = iMessageStyleReveal;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -299,7 +308,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
54D217F918EF681300A19583 /* UITableView+SPXRevealAdditions.m in Sources */,
54D2180118EF8DCD00A19583 /* UITableView+SPXRevealAdditions.m in Sources */,
54D217CF18EF5A4500A19583 /* SPXAppDelegate.m in Sources */,
54D217CB18EF5A4500A19583 /* main.m in Sources */,
54D217F118EF5AB400A19583 /* SPXRevealTableViewController.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
<true/>
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
<false/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "0"
version = "2.0">
</Bucket>
12 changes: 6 additions & 6 deletions iMessageStyleReveal/SPXRevealTableViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ - (void)viewDidLoad
[self.tableView enableRevealableViewForDirection:SPXRevealableViewGestureDirectionLeft];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"%zd : %zd", indexPath.section, indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"Section %zd : Item %zd", indexPath.section, indexPath.row];
cell.revealableView = [[UINib nibWithNibName:@"TimestampView" bundle:nil] instantiateWithOwner:nil options:nil].firstObject;
return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 40;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ typedef NS_OPTIONS(NSInteger, SPXRevealableViewGestureDirection)


/**
* Gets/sets the revealable view
* Gets/sets the revealable view. The view will be added to the cell's contentView
*/
@property (nonatomic, strong) UIView *revealableView;

Expand Down
103 changes: 71 additions & 32 deletions iMessageStyleReveal/SPXRevealableView/UITableView+SPXRevealAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,40 @@

static void * SPXRevealableView = &SPXRevealableView;
static void * SPXPanGestureRecognizer = &SPXPanGestureRecognizer;
static void * SPXPanGestureDirection = &SPXPanGestureDirection;
static void * SPXContext = &SPXContext;

@interface UITableViewCell (SPXRevealAdditionsPrivate)
- (void)updateRevealableViewFrameForDirection:(SPXRevealableViewGestureDirection)direction;
@end

@interface UITableView () <UIGestureRecognizerDelegate>
@property (nonatomic, strong) UIPanGestureRecognizer *spx_panGestureRecognizer;
@property (nonatomic, assign) SPXRevealableViewGestureDirection spx_gestureDirection;
@end

@implementation UITableView (SPXRevealAdditions)

#pragma mark - Gestures

- (SPXRevealableViewGestureDirection)spx_gestureDirection
{
return [objc_getAssociatedObject(self, SPXPanGestureDirection) intValue];
}

- (void)setSpx_gestureDirection:(SPXRevealableViewGestureDirection)spx_gestureDirection
{
objc_setAssociatedObject(self, SPXPanGestureDirection, @(spx_gestureDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIPanGestureRecognizer *)spx_panGestureRecognizer
{
UIPanGestureRecognizer *gesture = objc_getAssociatedObject(self, SPXPanGestureRecognizer);

if (!gesture) {
gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
gesture.delegate = self;
self.panGestureRecognizer.enabled = NO;
objc_setAssociatedObject(self, SPXPanGestureRecognizer, gesture, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

Expand All @@ -52,57 +70,66 @@ - (UIPanGestureRecognizer *)spx_panGestureRecognizer

- (void)enableRevealableViewForDirection:(SPXRevealableViewGestureDirection)direction
{
[self removeGestureRecognizer:self.spx_panGestureRecognizer];
[self setSpx_gestureDirection:direction];
[self addGestureRecognizer:self.spx_panGestureRecognizer];
[self addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// This ensure we can scroll the tableView while our revealable view are visisble
// This ensure we can scroll the tableView while our revealable views are visisble
return YES;
}

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gesture
{
// if its a gestureRecognizer other than ours, ignore it
if (gesture != self.spx_panGestureRecognizer) {
[super gestureRecognizerShouldBegin:gesture];

if (self.spx_panGestureRecognizer != gesture) {
return YES;
}

CGPoint velocity = [gesture velocityInView:gesture.view];

// if the gesture is a vertical one it will interfere with existing UITableView, so ignore it
if (velocity.y != 0) {
return NO;
}
CGPoint translation = [gesture translationInView:gesture.view];
BOOL horizontalScrollingWithSPXGesture = (fabsf(translation.x) > fabsf(translation.y)) && (gesture == self.spx_panGestureRecognizer);

return YES;
return horizontalScrollingWithSPXGesture;
}

#pragma mark - Cell Frame Updates
#pragma mark - Layout

static CGFloat translationX;
static CGFloat currentOffset;

- (void)updateCells
- (void)updateFramesForCells
{
for (UITableViewCell *cell in self.visibleCells) {
CGRect rect = cell.frame;
CGFloat x = currentOffset;

x = MAX(x, -CGRectGetWidth(cell.revealableView.bounds));
x = MIN(x, 0);
if (self.spx_gestureDirection == SPXRevealableViewGestureDirectionLeft) {
x = MAX(x, -CGRectGetWidth(cell.revealableView.bounds));
x = MIN(x, 0);
} else {
x = MAX(x, 0);
x = MIN(x, CGRectGetWidth(cell.revealableView.bounds));
}

rect.origin.x = x;
cell.frame = rect;

[cell updateRevealableViewFrameForDirection:self.spx_gestureDirection];
}
}

#pragma mark - Observers and gesture handling

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self && [keyPath isEqualToString:@"contentOffset"]) {
[self updateCells];
return;
if (context == SPXContext) {
if ([keyPath isEqualToString:@"contentOffset"]) {
[self updateFramesForCells];
return;
}
}

[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
Expand All @@ -112,13 +139,15 @@ - (void)pan:(UIPanGestureRecognizer *)gesture
{
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
[self addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:SPXContext];
break;
case UIGestureRecognizerStateChanged:
{
translationX = [gesture translationInView:gesture.view].x;
currentOffset += translationX;

[gesture setTranslation:CGPointZero inView:gesture.view];
[self updateCells];
[self updateFramesForCells];
}
break;
default:
Expand All @@ -134,6 +163,8 @@ - (void)pan:(UIPanGestureRecognizer *)gesture
} completion:^(BOOL finished) {
translationX = 0;
}];

[self removeObserver:self forKeyPath:@"contentOffset"];
}
break;
}
Expand All @@ -143,11 +174,28 @@ - (void)pan:(UIPanGestureRecognizer *)gesture


/**
* The following implementation just ensures we have a revealable view
* The following implementation just ensures we have a revealable view and that its layed out correctly
*/

@implementation UITableViewCell (SPXRevealAdditions)

- (void)updateRevealableViewFrameForDirection:(SPXRevealableViewGestureDirection)direction
{
CGRect rect = self.revealableView.bounds;

if (direction == SPXRevealableViewGestureDirectionLeft) {
rect.origin.x = CGRectGetMaxX(self.bounds);
self.revealableView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin;
} else {
rect.origin.x = CGRectGetMinX(self.bounds) - CGRectGetWidth(self.revealableView.bounds);
self.revealableView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin;
}

rect.origin.y = CGRectGetMinY(rect);
rect.size.height = CGRectGetHeight(self.bounds);
self.revealableView.frame = rect;
}

- (UIView *)revealableView
{
return objc_getAssociatedObject(self, SPXRevealableView);
Expand All @@ -156,23 +204,14 @@ - (UIView *)revealableView
- (void)setRevealableView:(UIView *)revealableView
{
UIView *_revealableView = [self revealableView];

if (_revealableView == revealableView) {
return;
}

objc_setAssociatedObject(self, SPXRevealableView, revealableView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

CGRect rect = revealableView.bounds;

rect.origin.x = CGRectGetMaxX(self.bounds);
rect.origin.y = CGRectGetMinY(rect);
rect.size.height = CGRectGetHeight(self.bounds);

revealableView.frame = rect;
revealableView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin;

[self addSubview:revealableView];
[self.contentView addSubview:revealableView];
[self updateRevealableViewFrameForDirection:SPXRevealableViewGestureDirectionLeft];
}

@end
Expand Down
Loading

0 comments on commit 22f318b

Please sign in to comment.