-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Included coach mark files with sample
- Loading branch information
Showing
9 changed files
with
817 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// | ||
// DDBubble.h | ||
// Coach Marks | ||
// | ||
// Created by Darin Doria on 02/17/2014. | ||
// Copyright (c) 2014 Darin Doria. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
@interface DDBubble : UIView | ||
|
||
typedef enum { | ||
CRArrowPositionTop, | ||
CRArrowPositionBottom, | ||
CRArrowPositionRight, | ||
CRArrowPositionLeft | ||
} CRArrowPosition; | ||
|
||
@property (nonatomic, assign) CRArrowPosition arrowPosition; | ||
@property (nonatomic, strong) UIView *attachedView; | ||
@property (nonatomic, strong) NSString *title; | ||
@property (nonatomic, strong) NSString *bubbleText; | ||
@property (nonatomic, strong) UIColor *color; | ||
@property (nonatomic) CGRect attachedFrame; | ||
@property (nonatomic) BOOL bouncing; | ||
@property (nonatomic) BOOL animationShouldStop; | ||
|
||
-(id)initWithAttachedView:(UIView*)view title:(NSString*)title description:(NSString*)description arrowPosition:(CRArrowPosition)arrowPosition andColor:(UIColor*)color; | ||
-(id)initWithFrame:(CGRect)frame title:(NSString*)title description:(NSString*)description arrowPosition:(CRArrowPosition)arrowPosition andColor:(UIColor*)color; | ||
-(void)animate; | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
// | ||
// DDBubble.m | ||
// Coach Marks | ||
// | ||
// Created by Darin Doria on 02/17/2014. | ||
// Copyright (c) 2014 Darin Doria. All rights reserved. | ||
// | ||
|
||
#import "DDBubble.h" | ||
|
||
#define ARROW_SPACE 6 // space between arrow and highlighted region | ||
#define ARROW_SIZE 8 | ||
#define PADDING 8 // padding between text and border of bubble | ||
#define RADIUS 6 | ||
#define TEXT_COLOR [UIColor blackColor] | ||
#define TITLE_FONT_SIZE 14 | ||
|
||
@interface DDBubble () | ||
{ | ||
float _arrowOffset; | ||
} | ||
|
||
@end | ||
|
||
@implementation DDBubble | ||
|
||
|
||
#pragma mark - Initialization | ||
|
||
-(id)initWithAttachedView:(UIView*)view title:(NSString*)title description:(NSString*)description arrowPosition:(CRArrowPosition)arrowPosition andColor:(UIColor*)color | ||
{ | ||
return [self initWithFrame:view.frame title:title description:description arrowPosition:arrowPosition andColor:color]; | ||
} | ||
|
||
-(id)initWithFrame:(CGRect)frame title:(NSString*)title description:(NSString*)description arrowPosition:(CRArrowPosition)arrowPosition andColor:(UIColor*)color | ||
{ | ||
self = [super init]; | ||
if(self) | ||
{ | ||
if(color!=nil) | ||
self.color=color; | ||
else | ||
self.color=[UIColor whiteColor]; | ||
|
||
self.attachedFrame = frame; | ||
self.title = title; | ||
self.bubbleText = description; | ||
self.arrowPosition = arrowPosition; | ||
[self setBackgroundColor:[UIColor clearColor]]; | ||
} | ||
|
||
// position bubble | ||
[self setFrame:[self calculateFrame]]; | ||
[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; | ||
|
||
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 setText:title]; | ||
[titleLabel setBackgroundColor:[UIColor clearColor]]; | ||
[self addSubview:titleLabel]; | ||
|
||
|
||
[self setNeedsDisplay]; | ||
return self; | ||
} | ||
|
||
|
||
#pragma mark - Positioning and Size | ||
|
||
- (void)fixFrameIfOutOfBounds | ||
{ | ||
/** | ||
* Description: | ||
* | ||
* Check if bubble is going off the screen using the | ||
* position and size. If it is, return YES. | ||
*/ | ||
|
||
const float xBounds = 320; | ||
// const float yBounds = 568; | ||
|
||
float x = self.frame.origin.x; | ||
float y = self.frame.origin.y; | ||
float width = self.frame.size.width; | ||
float height = self.frame.size.height; | ||
|
||
float padding = 3; | ||
|
||
// check for right most bound | ||
if (x + width > xBounds) { | ||
_arrowOffset = (x + width) - xBounds; | ||
x = x - _arrowOffset; | ||
} | ||
// check for left most bound | ||
else if (x < 0) { | ||
_arrowOffset = x - padding; | ||
x = x - _arrowOffset + padding; | ||
} | ||
|
||
[self setFrame:CGRectMake(x, y, width, height)]; | ||
} | ||
|
||
-(CGRect)calculateFrame | ||
{ | ||
//Calculation of the bubble position | ||
float x = self.attachedFrame.origin.x; | ||
float y = self.attachedFrame.origin.y; | ||
|
||
|
||
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); | ||
|
||
}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); | ||
} | ||
|
||
return CGRectMake(x, y, [self size].width+ARROW_SIZE, [self size].height+ARROW_SIZE); | ||
} | ||
|
||
-(CGSize)size | ||
{ | ||
// 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] | ||
|
||
}]; | ||
|
||
return CGSizeMake(result.width + (PADDING*3), result.height + (PADDING*2.5)); | ||
} | ||
|
||
-(CGSize)offsets | ||
{ | ||
return CGSizeMake((self.arrowPosition==CRArrowPositionLeft)? ARROW_SIZE : 0, (self.arrowPosition==CRArrowPositionTop)? ARROW_SIZE : 0); | ||
} | ||
|
||
|
||
|
||
#pragma mark - Drawing and Animation | ||
|
||
- (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; | ||
CGContextAddPath(ctx, clippath); | ||
|
||
CGContextSetFillColorWithColor(ctx, self.color.CGColor); | ||
|
||
CGContextClosePath(ctx); | ||
CGContextFillPath(ctx); | ||
|
||
[self.color set]; | ||
|
||
// tip of arrow needs to be centered under highlighted region | ||
// this center area is always arrow size divided by 2 | ||
float center = ARROW_SIZE/2; | ||
|
||
// points used to draw arrow | ||
// Wide Arrow --> x = center + - ArrowSize | ||
// Skinny Arrow --> x = center + - center | ||
// Normal Arrow --> | ||
CGPoint startPoint = CGPointMake(center - ARROW_SIZE, ARROW_SIZE); | ||
CGPoint midPoint = CGPointMake(center, 0); | ||
CGPoint endPoint = CGPointMake(center + ARROW_SIZE, ARROW_SIZE); | ||
|
||
|
||
UIBezierPath *path = [UIBezierPath bezierPath]; | ||
[path moveToPoint:startPoint]; | ||
[path addLineToPoint:endPoint]; | ||
[path addLineToPoint:midPoint]; | ||
[path addLineToPoint:startPoint]; | ||
|
||
|
||
if(self.arrowPosition==CRArrowPositionTop) | ||
{ | ||
CGAffineTransform trans = CGAffineTransformMakeTranslation([self 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); | ||
[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); | ||
[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); | ||
[path applyTransform:rot]; | ||
[path applyTransform:trans]; | ||
} | ||
|
||
[path closePath]; // Implicitly does a line between p4 and p1 | ||
[path fill]; // If you want it filled, or... | ||
[path stroke]; // ...if you want to draw the outline. | ||
CGContextRestoreGState(ctx); | ||
} | ||
|
||
- (void)animate | ||
{ | ||
[UIView animateWithDuration:2.0f | ||
delay:0.3 | ||
options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse) | ||
animations:^ { | ||
self.transform = CGAffineTransformMakeTranslation(0, -4); | ||
} | ||
completion:^(BOOL finished) { | ||
}]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// | ||
// DDCircleView.h | ||
// CoachMarks | ||
// | ||
// Created by Darin Doria on 2/17/14. | ||
// Copyright (c) 2014 Darin Doria. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface DDCircleView : UIView | ||
|
||
@property BOOL animationShouldStop; | ||
|
||
- (void)swipeInFrame:(CGRect)frame; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// | ||
// DDCircleView.m | ||
// CoachMarks | ||
// | ||
// Created by Darin Doria on 2/17/14. | ||
// Copyright (c) 2014 Darin Doria. All rights reserved. | ||
// | ||
|
||
#import "DDCircleView.h" | ||
|
||
@implementation DDCircleView | ||
|
||
- (id)initWithFrame:(CGRect)aRect | ||
{ | ||
self = [super initWithFrame:CGRectMake(10, 0, 40, 40)]; | ||
|
||
if (self) { | ||
self.backgroundColor = [UIColor clearColor]; | ||
CAShapeLayer *shapeLayer = (CAShapeLayer *) self.layer; | ||
shapeLayer.path = ([UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 40, 40)].CGPath); | ||
shapeLayer.fillColor = [UIColor colorWithWhite:1.000 alpha:1.00].CGColor; | ||
shapeLayer.shadowRadius = 8.0; | ||
shapeLayer.shadowOffset = CGSizeMake(0, 0); | ||
shapeLayer.shadowColor = [UIColor colorWithRed:0.000 green:0.299 blue:0.715 alpha:1.000].CGColor; | ||
shapeLayer.shadowOpacity = 1.0; | ||
shapeLayer.shadowPath = ([UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 40, 40)].CGPath); | ||
|
||
self.animationShouldStop = NO; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (void)userTap:(UITapGestureRecognizer *)recognizer { | ||
self.hidden = YES; | ||
[self removeFromSuperview]; | ||
} | ||
|
||
- (void)swipeInFrame:(CGRect)frame | ||
{ | ||
[self centerYPositioninView:self inFrame:frame]; | ||
[self animateSwipeRight]; | ||
} | ||
|
||
- (void)animateSwipeRight | ||
{ | ||
if (!_animationShouldStop) { | ||
self.transform = CGAffineTransformMakeScale(2, 2); | ||
self.alpha = 0.0f; | ||
[UIView animateKeyframesWithDuration:0.6 delay:0.3 options:0 | ||
animations:^{ | ||
// Fade In | ||
self.transform = CGAffineTransformMakeScale(1, 1); | ||
self.alpha = 1.0f; | ||
} | ||
completion:^(BOOL finished){ | ||
// End | ||
[UIView animateWithDuration:1.0 | ||
animations:^{ | ||
// Slide Right | ||
self.transform = CGAffineTransformMakeTranslation(260, 0); | ||
// Fade Out | ||
self.alpha = 0.0f; | ||
|
||
} | ||
completion:^(BOOL finished) { | ||
// End | ||
[UIView animateWithDuration:0.5 | ||
animations:^{ | ||
// Fade Out | ||
//self.alpha = 0.0f; | ||
} | ||
completion:^(BOOL finished) { | ||
// End | ||
[self performSelector:@selector(animateSwipeRight)]; | ||
}]; | ||
}]; | ||
}]; | ||
} | ||
|
||
} | ||
|
||
- (void)centerYPositioninView:(UIView *)view inFrame:(CGRect)frame | ||
{ | ||
CGFloat centerY = frame.origin.y + CGRectGetHeight(frame)/2; | ||
CGFloat offsetY = CGRectGetHeight(view.frame)/2; | ||
|
||
CGFloat newY = centerY - offsetY; | ||
view.frame = CGRectMake(view.frame.origin.x, newY, 40, 40); | ||
} | ||
|
||
- (void)centerXPositioninView:(UIView *)view inFrame:(CGRect)frame | ||
{ | ||
CGFloat centerX = frame.origin.x + CGRectGetWidth(frame)/2; | ||
CGFloat offsetX = CGRectGetWidth(view.frame)/2; | ||
|
||
CGFloat newX = centerX - offsetX; | ||
view.frame = CGRectMake(newX, view.frame.origin.y, 40, 40); | ||
} | ||
|
||
- (void)centerInView:(UIView *)view inFrame:(CGRect)frame | ||
{ | ||
[self centerYPositioninView:view inFrame:frame]; | ||
[self centerXPositioninView:view inFrame:frame]; | ||
} | ||
|
||
+ (Class)layerClass | ||
{ | ||
return [CAShapeLayer class]; | ||
} | ||
|
||
@end |
Oops, something went wrong.