Skip to content

Instantly share code, notes, and snippets.

@andyshep
Last active February 10, 2020 14:51
Show Gist options
  • Save andyshep/6240110 to your computer and use it in GitHub Desktop.
Save andyshep/6240110 to your computer and use it in GitHub Desktop.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
@interface AJSPopoverView : UIPopoverBackgroundView
@end
#import "AJSPopoverView.h"
#define ARROW_HEIGHT 15.0f
#define ARROW_BASE 30.0f
#define EDGE_INSET 15.0f
// convert degrees to radians
#define DEG_TO_RAD(angle) ((angle)/180.0f * M_PI)
// return UIColor with r,g,b values and no alpha channel
#define RGB(r, g, b) [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f]
@implementation AJSPopoverView
@synthesize arrowDirection = _arrowDirection;
@synthesize arrowOffset = _arrowOffset;
+ (CGFloat)arrowBase {
return ARROW_BASE;
}
+ (CGFloat)arrowHeight {
return ARROW_HEIGHT;
}
+ (UIEdgeInsets)contentViewInsets {
return UIEdgeInsetsMake(EDGE_INSET, EDGE_INSET, EDGE_INSET, EDGE_INSET);
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.arrowDirection = UIPopoverArrowDirectionDown;
[self setBackgroundColor:[UIColor clearColor]];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.shadowOpacity = 0.00f;
self.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.layer.shadowRadius = 15.0f;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *path = [self popoverPathWithRect:rect direction:self.arrowDirection];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0f, 1.0f};
UIColor *topColor = RGB(35.0f, 35.0f, 35.0f);
UIColor *bottomColor = RGB(86.0f, 86.0f, 86.0f);
NSArray *colors = @[(__bridge id)[topColor CGColor],
(__bridge id)[bottomColor CGColor]];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddPath(context, path.CGPath);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
[[UIColor blackColor] setStroke];
[path setLineWidth:1.0f];
[path stroke];
}
- (UIEdgeInsets)edgeInsetsForArrowDirection:(UIPopoverArrowDirection)direction {
if (direction == UIPopoverArrowDirectionUp) {
return UIEdgeInsetsMake(ARROW_HEIGHT, 0.0f, 0.0f, 0.0f);
} else if (direction == UIPopoverArrowDirectionDown) {
return UIEdgeInsetsMake(0.0f, 0.0f, ARROW_HEIGHT, 0.0f);
} else if (direction == UIPopoverArrowDirectionLeft) {
return UIEdgeInsetsMake(0.0f, ARROW_HEIGHT, 0.0f, 0.0f);
} else if (direction == UIPopoverArrowDirectionRight) {
return UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, ARROW_HEIGHT);
} else {
return UIEdgeInsetsZero;
}
}
- (UIBezierPath *)popoverPathWithRect:(CGRect)rect direction:(UIPopoverArrowDirection)direction {
// corner radius of popover
CGFloat radius = 10.0f;
// edge insets adjusted for arrow direction
UIEdgeInsets edgeInsets = [self edgeInsetsForArrowDirection:self.arrowDirection];
// setup two rectangles for popover
CGRect outerRect = UIEdgeInsetsInsetRect(rect, edgeInsets);
CGRect innerRect = CGRectInset(outerRect, radius, radius);
UIBezierPath *path = [UIBezierPath bezierPath];
if (self.arrowDirection == UIPopoverArrowDirectionUp) {
CGFloat arrowEdge = CGRectGetMidX(innerRect) + self.arrowOffset - (ARROW_BASE / 2.0f);
CGRect arrowRect = CGRectMake(CGRectGetMinX(outerRect) + arrowEdge, CGRectGetMinY(rect), ARROW_BASE, ARROW_HEIGHT);
[path moveToPoint:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMinX(arrowRect) - 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMidX(arrowRect) - 0.5f, CGRectGetMinY(arrowRect) + 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(arrowRect) - 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(270.0f) endAngle:DEG_TO_RAD(0.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(0.0f) endAngle:DEG_TO_RAD(90.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(90.0f) endAngle:DEG_TO_RAD(180.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(180.0f) endAngle:DEG_TO_RAD(270.0f) clockwise:YES];
} else if (self.arrowDirection == UIPopoverArrowDirectionDown) {
CGFloat arrowEdge = CGRectGetMidX(innerRect) + self.arrowOffset - (ARROW_BASE / 2.0f);
CGRect arrowRect = CGRectMake(CGRectGetMinX(outerRect) + arrowEdge, CGRectGetMaxY(outerRect), ARROW_BASE, ARROW_HEIGHT);
[path moveToPoint:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(270.0f) endAngle:DEG_TO_RAD(0.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(0.0f) endAngle:DEG_TO_RAD(90.0f) clockwise:YES];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(arrowRect) - 0.5f, CGRectGetMaxY(outerRect) - 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMidX(arrowRect) - 0.5f, CGRectGetMaxY(rect) - 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMinX(arrowRect) - 0.5f, CGRectGetMaxY(outerRect) - 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(90.0f) endAngle:DEG_TO_RAD(180.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(180.0f) endAngle:DEG_TO_RAD(270.0f) clockwise:YES];
} else if (self.arrowDirection == UIPopoverArrowDirectionLeft) {
CGFloat arrowEdge = CGRectGetMidY(outerRect) + self.arrowOffset - (ARROW_BASE / 2.0f);
CGRect arrowRect = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(outerRect) + arrowEdge, ARROW_HEIGHT, ARROW_BASE);
[path moveToPoint:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(270.0f) endAngle:DEG_TO_RAD(0.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(0.0f) endAngle:DEG_TO_RAD(90.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(90.0f) endAngle:DEG_TO_RAD(180.0f) clockwise:YES];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(arrowRect) + 0.5f, CGRectGetMaxY(arrowRect) - 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMinX(rect) + 0.5f, CGRectGetMidY(arrowRect) - 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(arrowRect) + 0.5f, CGRectGetMinY(arrowRect) - 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(180.0f) endAngle:DEG_TO_RAD(270.0f) clockwise:YES];
} else if (self.arrowDirection == UIPopoverArrowDirectionRight) {
CGFloat arrowEdge = CGRectGetMidY(outerRect) + self.arrowOffset - (ARROW_BASE / 2.0f);
CGRect arrowRect = CGRectMake(CGRectGetMaxX(outerRect), CGRectGetMinY(outerRect) + arrowEdge, ARROW_HEIGHT, ARROW_BASE);
[path moveToPoint:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(outerRect) + 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(270.0f) endAngle:DEG_TO_RAD(0.0f) clockwise:YES];
[path addLineToPoint:CGPointMake(CGRectGetMinX(arrowRect) - 0.5f, CGRectGetMinY(arrowRect) + 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(arrowRect) - 0.5f, CGRectGetMidY(arrowRect) + 0.5f)];
[path addLineToPoint:CGPointMake(CGRectGetMinX(arrowRect) - 0.5f, CGRectGetMaxY(arrowRect) + 0.5f)];
[path addArcWithCenter:CGPointMake(CGRectGetMaxX(innerRect) - 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(0.0f) endAngle:DEG_TO_RAD(90.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMaxY(innerRect) - 0.5f)
radius:radius startAngle:DEG_TO_RAD(90.0f) endAngle:DEG_TO_RAD(180.0f) clockwise:YES];
[path addArcWithCenter:CGPointMake(CGRectGetMinX(innerRect) + 0.5f, CGRectGetMinY(innerRect) + 0.5f)
radius:radius startAngle:DEG_TO_RAD(180.0f) endAngle:DEG_TO_RAD(270.0f) clockwise:YES];
}
return path;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment