Skip to content

Commit

Permalink
Merge pull request #1 from andygeers/master
Browse files Browse the repository at this point in the history
Allow wordwrapping, option to change swipe direction, & created a podspec
  • Loading branch information
ddoria921 committed Feb 12, 2015
2 parents 9d93e07 + 07efbc7 commit f931d31
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 33 deletions.
66 changes: 40 additions & 26 deletions Coach Marks/DDBubble.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,38 @@ -(id)initWithFrame:(CGRect)frame title:(NSString*)title description:(NSString*)d
[self setBackgroundColor:[UIColor clearColor]];
}

UIFont* font = [self font];

// position bubble
[self setFrame:[self calculateFrame]];
[self setFrame:[self calculateFrameWithFont:font]];
[self fixFrameIfOutOfBounds];

// calculate and position text
float actualXPosition = [self offsets].width+PADDING*1.5;
float actualYPosition = [self offsets].height+PADDING*1.25;
float actualWidth = self.frame.size.width;
float actualHeight = TITLE_FONT_SIZE+3;
CGSize offsets = [self offsets];
float actualXPosition = offsets.width+PADDING*1.5;
float actualYPosition = offsets.height+PADDING*1.25;
float actualWidth = self.frame.size.width-offsets.width-PADDING*3;
float actualHeight = self.frame.size.height-offsets.height-PADDING*2.5;

UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(actualXPosition, actualYPosition, actualWidth, actualHeight)];
[titleLabel setTextColor:TEXT_COLOR];
[titleLabel setAlpha:0.9];
[titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:TITLE_FONT_SIZE]];
[titleLabel setFont:font];
[titleLabel setText:title];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
[titleLabel setNumberOfLines:0];
[self addSubview:titleLabel];


[self setNeedsDisplay];
return self;
}

- (UIFont*) font {
return [UIFont fontWithName:@"HelveticaNeue" size:TITLE_FONT_SIZE];
}


#pragma mark - Positioning and Size

Expand All @@ -84,7 +93,8 @@ - (void)fixFrameIfOutOfBounds
* position and size. If it is, return YES.
*/

const float xBounds = 320;
CGRect window = [[[UIApplication sharedApplication] keyWindow] frame];
const float xBounds = window.size.width; // 320;
// const float yBounds = 568;

float x = self.frame.origin.x;
Expand All @@ -97,46 +107,48 @@ - (void)fixFrameIfOutOfBounds
// check for right most bound
if (x + width > xBounds) {
_arrowOffset = (x + width) - xBounds;
x = x - _arrowOffset;
x = xBounds - width;
}
// check for left most bound
else if (x < 0) {
if (x < 0) {
_arrowOffset = x - padding;
x = x - _arrowOffset + padding;
x = 0;
}

[self setFrame:CGRectMake(x, y, width, height)];
}

-(CGRect)calculateFrame
-(CGRect)calculateFrameWithFont:(UIFont*)font
{
//Calculation of the bubble position
float x = self.attachedFrame.origin.x;
float y = self.attachedFrame.origin.y;

CGSize size = [self sizeWithFont:font];

if(self.arrowPosition==CRArrowPositionLeft||self.arrowPosition==CRArrowPositionRight)
{
y+=self.attachedFrame.size.height/2-[self size].height/2;
x+=(self.arrowPosition==CRArrowPositionLeft)? ARROW_SPACE+self.attachedFrame.size.width : -(ARROW_SPACE*2+[self size].width);
y+=self.attachedFrame.size.height/2-size.height/2;
x+=(self.arrowPosition==CRArrowPositionLeft)? ARROW_SPACE+self.attachedFrame.size.width : -(ARROW_SPACE*2+size.width);

}else if(self.arrowPosition==CRArrowPositionTop||self.arrowPosition==CRArrowPositionBottom)
{
x+=self.attachedFrame.size.width/2-[self size].width/2;
y+=(self.arrowPosition==CRArrowPositionTop)? ARROW_SPACE+self.attachedFrame.size.height : -(ARROW_SPACE*2+[self size].height);
x+=self.attachedFrame.size.width/2-size.width/2;
y+=(self.arrowPosition==CRArrowPositionTop)? ARROW_SPACE+self.attachedFrame.size.height : -(ARROW_SPACE*2+size.height);
}

return CGRectMake(x, y, [self size].width+ARROW_SIZE, [self size].height+ARROW_SIZE);
CGSize offsets = [self offsets];
return CGRectMake(x, y, size.width+offsets.width, size.height+offsets.height);
}

-(CGSize)size
-(CGSize)sizeWithFont:(UIFont*)font
{
// Calcultation of the bubble size
// size of bubble title determined by the strings attributes
CGSize result = [_title sizeWithAttributes:@{
NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:TITLE_FONT_SIZE]
}];
CGSize offset = [self offsets];
CGRect window = [[[UIApplication sharedApplication] keyWindow] frame];

CGSize result = [_title sizeWithFont:font constrainedToSize:CGSizeMake(window.size.width - offset.width - (PADDING*4), FLT_MAX) lineBreakMode:NSLineBreakByWordWrapping];

return CGSizeMake(result.width + (PADDING*3), result.height + (PADDING*2.5));
}
Expand All @@ -156,7 +168,9 @@ - (void)drawRect:(CGRect)rect
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);

CGPathRef clippath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake([self offsets].width,[self offsets].height, [self size].width, [self size].height) cornerRadius:RADIUS].CGPath;
CGSize size = [self sizeWithFont:[self font]];

CGPathRef clippath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake([self offsets].width,[self offsets].height, size.width, size.height) cornerRadius:RADIUS].CGPath;
CGContextAddPath(ctx, clippath);

CGContextSetFillColorWithColor(ctx, self.color.CGColor);
Expand Down Expand Up @@ -188,24 +202,24 @@ - (void)drawRect:(CGRect)rect

if(self.arrowPosition==CRArrowPositionTop)
{
CGAffineTransform trans = CGAffineTransformMakeTranslation([self size].width/2-(ARROW_SIZE)/2+_arrowOffset, 0);
CGAffineTransform trans = CGAffineTransformMakeTranslation(size.width/2-(ARROW_SIZE)/2+_arrowOffset, 0);
[path applyTransform:trans];
}else if(self.arrowPosition==CRArrowPositionBottom)
{
CGAffineTransform rot = CGAffineTransformMakeRotation(M_PI);
CGAffineTransform trans = CGAffineTransformMakeTranslation([self size].width/2+(ARROW_SIZE)/2+_arrowOffset, [self size].height+ARROW_SIZE);
CGAffineTransform trans = CGAffineTransformMakeTranslation(size.width/2+(ARROW_SIZE)/2+_arrowOffset, size.height+ARROW_SIZE);
[path applyTransform:rot];
[path applyTransform:trans];
}else if(self.arrowPosition==CRArrowPositionLeft)
{
CGAffineTransform rot = CGAffineTransformMakeRotation(M_PI*1.5);
CGAffineTransform trans = CGAffineTransformMakeTranslation(0, ([self size].height+ARROW_SIZE)/2);
CGAffineTransform trans = CGAffineTransformMakeTranslation(0, (size.height+ARROW_SIZE)/2);
[path applyTransform:rot];
[path applyTransform:trans];
}else if(self.arrowPosition==CRArrowPositionRight)
{
CGAffineTransform rot = CGAffineTransformMakeRotation(M_PI*0.5);
CGAffineTransform trans = CGAffineTransformMakeTranslation([self size].width+ARROW_SIZE, ([self size].height-ARROW_SIZE)/2);
CGAffineTransform trans = CGAffineTransformMakeTranslation(size.width+ARROW_SIZE, (size.height-ARROW_SIZE)/2);
[path applyTransform:rot];
[path applyTransform:trans];
}
Expand Down
6 changes: 6 additions & 0 deletions Coach Marks/DDCircleView.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@

#import <UIKit/UIKit.h>

enum EnumCircleSwipeDirection {
kCircleSwipeLeftToRight,
kCircleSwipeRightToLeft
};

@interface DDCircleView : UIView

@property BOOL animationShouldStop;
@property enum EnumCircleSwipeDirection swipeDirection;

- (void)swipeInFrame:(CGRect)frame;

Expand Down
32 changes: 25 additions & 7 deletions Coach Marks/DDCircleView.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,50 @@ - (void)userTap:(UITapGestureRecognizer *)recognizer {
- (void)swipeInFrame:(CGRect)frame
{
[self centerYPositioninView:self inFrame:frame];
[self animateSwipeRight];
[self animateSwipe];
}

- (void)animateSwipeRight
- (void)animateSwipe
{
if (!_animationShouldStop) {
self.transform = CGAffineTransformMakeScale(2, 2);
CGAffineTransform scale = CGAffineTransformMakeScale(2, 2);
CGAffineTransform translateRight = CGAffineTransformMakeTranslation(260, 0);
if (self.swipeDirection == kCircleSwipeLeftToRight) {
self.transform = scale;
} else {
// Start on the right hand side as well as scaling
self.transform = CGAffineTransformConcat(translateRight, scale);
}
self.alpha = 0.0f;
[UIView animateKeyframesWithDuration:0.6 delay:0.3 options:0
animations:^{
// Fade In
self.transform = CGAffineTransformMakeScale(1, 1);
if (self.swipeDirection == kCircleSwipeLeftToRight) {
// Scale down to normal
self.transform = CGAffineTransformMakeScale(1, 1);
} else {
// Start on the right hand side
self.transform = translateRight;
}
self.alpha = 1.0f;
}
completion:^(BOOL finished){
// End
[UIView animateWithDuration:1.0
animations:^{
// Slide Right
self.transform = CGAffineTransformMakeTranslation(260, 0);
if (self.swipeDirection == kCircleSwipeLeftToRight) {
// Slide Right
self.transform = translateRight;
} else {
// Slide left
self.transform = CGAffineTransformIdentity;
}
// Fade Out
self.alpha = 0.0f;
}
completion:^(BOOL finished) {
// End
[self performSelector:@selector(animateSwipeRight)];
[self performSelector:@selector(animateSwipe)];
}];
}];

Expand Down
8 changes: 8 additions & 0 deletions Coach Marks/DDCoachMarksView.m
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ - (void)showSwipeGesture
CGRect frame = [[coachMarkInfo objectForKey:@"rect"] CGRectValue];
BOOL shouldAnimateSwipe = [[coachMarkInfo objectForKey:@"swipe"] boolValue];

NSString* swipeDirection = [coachMarkInfo objectForKey:@"direction"];
enum EnumCircleSwipeDirection direction = kCircleSwipeLeftToRight;
if ((swipeDirection != nil) && ([swipeDirection isEqualToString:@"righttoleft"])) {
direction = kCircleSwipeRightToLeft;
}

// if next animation doesn't need swipe
// remove current swiping circle if one exists
if (self.animatingCircle) {
Expand All @@ -230,6 +236,8 @@ - (void)showSwipeGesture
[self addSubview:self.animatingCircle];
}

self.animatingCircle.swipeDirection = direction;

[self.animatingCircle swipeInFrame:frame];
}
}
Expand Down
13 changes: 13 additions & 0 deletions DDCoachMarks.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Pod::Spec.new do |s|
s.name = "DDCoachMarks"
s.version = "1.0.0"
s.summary = "Quick and easy coach marks to use in any iOS app."
s.homepage = "https://github.com/ddoria921/DDCoachMarks"
s.license = 'MIT'
s.author = { "Darin Doria" => "[email protected]" }
s.source = { :git => "https://github.com/ddoria921/DDCoachMarks.git", :branch => "master" }
s.platform = :ios, '6.0'
s.source_files = 'Coach Marks/DD*.{h,m}'
s.frameworks = 'Foundation', 'UIKit', 'QuartzCore'
s.requires_arc = true
end
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Text that goes in the bubbles
Stands for 'point of interest'. You can define a whole region using the `@"rect"` value, but defining a different CGRect value here makes the bubble caption position itself under the POI rect.
* `@"swipe"`
Use "YES" here if you want to show a row swipe gesture on a table view cell. Disabled by default.
* `@"direction"`
Direction that swipe gestures should animate in. The default is `@"lefttoright"` but you can also specify `@"righttoleft"`.

## DDCoachMarksViewDelegate

Expand Down

0 comments on commit f931d31

Please sign in to comment.