Popup view的实现

以前写过一篇文章:UIPopoverController的定位,这篇文章实现了类似的功能。

首先,导入CoreGraphics.framework和QuartzCore.framework。

pch文件:

#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#endif

#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#endif

#ifdef	_DEBUG
#define	DNSLog(...);	NSLog(__VA_ARGS__);
#define DNSLogMethod	NSLog(@"[%s] %@", class_getName([self class]), NSStringFromSelector(_cmd));
#define DNSLogPoint(p)	NSLog(@"%f,%f", p.x, p.y);
#define DNSLogSize(p)	NSLog(@"%f,%f", p.width, p.height);
#define DNSLogRect(p)	NSLog(@"%f,%f %f,%f", p.origin.x, p.origin.y, p.size.width, p.size.height);

CFAbsoluteTime startTime;
#define D_START			startTime=CFAbsoluteTimeGetCurrent();
#define D_END			DNSLog(@"[%s] %@ %f seconds", class_getName([self class]), NSStringFromSelector(_cmd), CFAbsoluteTimeGetCurrent() - startTime );
#else
#define DNSLog(...);	 NSLog(__VA_ARGS__);
#define DNSLogMethod	 NSLog(@"[%s] %@", class_getName([self class]), NSStringFromSelector(_cmd) );
#define DNSLogPoint(p)	 NSLog(@"%f,%f", p.x, p.y);
#define DNSLogSize(p)	 NSLog(@"%f,%f", p.width, p.height);
#define DNSLogRect(p)	 NSLog(@"%f,%f %f,%f", p.origin.x, p.origin.y, p.size.width, p.size.height);

#define D_START			 CFAbsoluteTime startTime=CFAbsoluteTimeGetCurrent();
#define D_END			 DNSLog(@"New %f seconds", CFAbsoluteTimeGetCurrent() - startTime );
#endif

#define SAFE_FREE(p) { if(p) { free(p); (p)=NULL; } }

  

SNPopupView.h

 

#import <UIKit/UIKit.h>

#define SHADOW_OFFSET					CGSizeMake(10, 10)
#define CONTENT_OFFSET					CGSizeMake(10, 10)
#define POPUP_ROOT_SIZE					CGSizeMake(20, 10)

#define HORIZONTAL_SAFE_MARGIN			30

#define POPUP_ANIMATION_DURATION		0.3
#define DISMISS_ANIMATION_DURATION		0.2

#define DEFAULT_TITLE_SIZE				20

#define ALPHA							0.6

#define BAR_BUTTON_ITEM_UPPER_MARGIN	10
#define BAR_BUTTON_ITEM_BOTTOM_MARGIN	5

@class TouchPeekView;

typedef enum {
	SNPopupViewUp		= 1,
	SNPopupViewDown		= 2,
	SNPopupViewRight	= 1 << 8,
	SNPopupViewLeft		= 2 << 8,
} SNPopupViewDirection;

@class SNPopupView;

@protocol SNPopupViewModalDelegate <NSObject>

- (void)didDismissModal:(SNPopupView *)popupview;

@end

@interface SNPopupView : UIView {
	CGGradientRef gradient;
	CGGradientRef gradient2;
	
	CGRect		contentRect;
	CGRect		contentBounds;
	
	CGRect		popupRect;
	CGRect		popupBounds;
	
	CGRect		viewRect;
	CGRect		viewBounds;
	
	CGPoint		pointToBeShown;
	
	NSString	*title;
	UIImage		*image;
	float		fontSize;
	
	UIView		*contentView;
	
	float		horizontalOffset;
	SNPopupViewDirection	direction;
	id			target;
	SEL			action;
	
	TouchPeekView	*peekView;
	id<SNPopupViewModalDelegate>delegate;
	
	BOOL		animatedWhenAppering;
}

@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) UIImage *image;
@property (nonatomic, readonly) UIView *contentView;
@property (nonatomic, assign) id <SNPopupViewModalDelegate> delegate;

- (id)initWithString:(NSString *)newValue withFontOfSize:(float)newFontSize;
- (id)initWithString:(NSString *)newValue;
- (id)initWithImage:(UIImage *)newImage;
- (id)initWithContentView:(UIView *)newContentView contentSize:(CGSize)contentSize;

- (void)showAtPoint:(CGPoint)p inView:(UIView *)inView;
- (void)showAtPoint:(CGPoint)p inView:(UIView *)inView animated:(BOOL)animated;

- (void)presentModalAtPoint:(CGPoint)p inView:(UIView *)inView;
- (void)presentModalAtPoint:(CGPoint)p inView:(UIView *)inView animated:(BOOL)animated;

- (BOOL)shouldBeDismissedFor:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)dismiss;
- (void)dismiss:(BOOL)animtaed;
- (void)dismissModal;

- (void)addTarget:(id)target action:(SEL)action;
@end

 

SNPopupView.m

 

#import "SNPopupView.h"

#import <QuartzCore/QuartzCore.h>

@interface TouchPeekView : UIView {
	SNPopupView *delegate;
}

@property (nonatomic, assign) SNPopupView *delegate;

@end

@interface SNPopupView(Private)

- (void)popup;

@end

@implementation TouchPeekView

@synthesize delegate;

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor clearColor]];
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	DNSLogMethod
	if ([delegate shouldBeDismissedFor:touches withEvent:event])
		[delegate dismissModal];
}

@end

@implementation SNPopupView

@synthesize title, image, contentView, delegate;

#pragma mark - Prepare

- (void)setupGradientColors {		
	CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
	CGFloat colors[] =
	{
		155.0 / 255.0, 155.0 / 255.0, 155.0 / 255.0, ALPHA,
		70.0 / 255.0, 70.0 / 255.0, 70.0 / 255.0, ALPHA,
	};
	gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0]) * 4));
	
	CGFloat colors2[] =
	{
		20.0 / 255.0, 20.0 / 255.0, 20.0 / 255.0, ALPHA,
		0.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, ALPHA,
	};
	gradient2 = CGGradientCreateWithColorComponents(rgb, colors2, NULL, sizeof(colors2) / (sizeof(colors2[0]) * 4));
	CGColorSpaceRelease(rgb);
}

- (id) initWithString:(NSString *)newValue {
	return [self initWithString:newValue withFontOfSize:DEFAULT_TITLE_SIZE];
}

- (id) initWithString:(NSString *)newValue withFontOfSize:(float)newFontSize {
	self = [super init];
	if (self != nil) {
		title = [newValue copy];
		
		[self setBackgroundColor:[UIColor clearColor]];
		
		fontSize = newFontSize;
		UIFont *font = [UIFont boldSystemFontOfSize:fontSize];
		
		CGSize titleRenderingSize = [title sizeWithFont:font];
		
		contentBounds = CGRectMake(0, 0, 0, 0);
		contentBounds.size = titleRenderingSize;
		
		[self setupGradientColors];		
	}
	return self;
}

- (id) initWithImage:(UIImage *)newImage {
	self = [super init];
	if (self != nil) {
		image = [newImage retain];
		
		[self setBackgroundColor:[UIColor clearColor]];
		
		contentBounds = CGRectMake(0, 0, 0, 0);
		contentBounds.size = image.size;
		
		[self setupGradientColors];	
	}
	return self;
}

- (id) initWithContentView:(UIView *)newContentView contentSize:(CGSize)contentSize {
	self = [super init];
	if (self != nil) {
		contentView = [newContentView retain];
        
		[self setBackgroundColor:[UIColor clearColor]];
		
		contentBounds = CGRectMake(0, 0, 0, 0);
		contentBounds.size = contentSize;
		
		[self setupGradientColors];
	}
	return self;
}

- (void)addTarget:(id)newTarget action:(SEL)newAction {
	if ([newTarget respondsToSelector:newAction]) {
		target = newTarget;
		action = newAction;
	}
}

#pragma mark - Present modal

- (void)createAndAttachTouchPeekView {
	UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    
	[peekView removeFromSuperview];
	[peekView release];
	peekView = nil;
	peekView = [[TouchPeekView alloc] initWithFrame:window.frame];
	[peekView setDelegate:self];
	
	[window addSubview:peekView];
}

- (void)presentModalAtPoint:(CGPoint)p inView:(UIView *)inView {
	animatedWhenAppering = YES;
	[self createAndAttachTouchPeekView];
	[self showAtPoint:[inView convertPoint:p toView:[[UIApplication sharedApplication] keyWindow]] inView:[[UIApplication sharedApplication] keyWindow]];
}

- (void)presentModalAtPoint:(CGPoint)p inView:(UIView *)inView animated:(BOOL)animated {
	animatedWhenAppering = animated;
	[self createAndAttachTouchPeekView];
	[self showAtPoint:[inView convertPoint:p toView:[[UIApplication sharedApplication] keyWindow]] inView:[[UIApplication sharedApplication] keyWindow] animated:animated];
}

#pragma mark - Show as normal view

- (void)showAtPoint:(CGPoint)p inView:(UIView *)inView {
	[self showAtPoint:p inView:inView animated:NO];
}

- (void)showAtPoint:(CGPoint)p inView:(UIView *)inView animated:(BOOL)animated {
	if ((p.y - contentBounds.size.height - POPUP_ROOT_SIZE.height - 2 * CONTENT_OFFSET.height - SHADOW_OFFSET.height) < 0) {
		direction = SNPopupViewDown;
	}
	else {
		direction = SNPopupViewUp;
	}
	
	if (direction & SNPopupViewUp) {
        
		pointToBeShown = p;
		
		contentRect.origin.x = p.x - (int)contentBounds.size.width / 2;
		contentRect.origin.y = p.y - CONTENT_OFFSET.height - POPUP_ROOT_SIZE.height - contentBounds.size.height;
		contentRect.size = contentBounds.size;
		
		popupBounds.origin = CGPointMake(0, 0);
		popupBounds.size.width = contentBounds.size.width + CONTENT_OFFSET.width + CONTENT_OFFSET.width;
		popupBounds.size.height = contentBounds.size.height + CONTENT_OFFSET.height + CONTENT_OFFSET.height + POPUP_ROOT_SIZE.height;
		
		popupRect.origin.x = contentRect.origin.x - CONTENT_OFFSET.width;
		popupRect.origin.y = contentRect.origin.y - CONTENT_OFFSET.height;
		popupRect.size = popupBounds.size;
		
		viewBounds.origin = CGPointMake(0, 0);
		viewBounds.size.width = popupRect.size.width + SHADOW_OFFSET.width + SHADOW_OFFSET.width;
		viewBounds.size.height = popupRect.size.height + SHADOW_OFFSET.height + SHADOW_OFFSET.height;
		
		viewRect.origin.x = popupRect.origin.x - SHADOW_OFFSET.width;
		viewRect.origin.y = popupRect.origin.y - SHADOW_OFFSET.height;
		viewRect.size = viewBounds.size;
        
		float left_viewRect = viewRect.origin.x + viewRect.size.width;
		
		if (viewRect.origin.x < 0) {
			direction = direction | SNPopupViewRight;
			horizontalOffset = viewRect.origin.x;
			
			if (viewRect.origin.x - horizontalOffset < pointToBeShown.x - HORIZONTAL_SAFE_MARGIN) {
			}
			else {
				pointToBeShown.x = HORIZONTAL_SAFE_MARGIN;
			}
			viewRect.origin.x -= horizontalOffset;
			contentRect.origin.x -= horizontalOffset;
			popupRect.origin.x -= horizontalOffset;
		}
		else if (left_viewRect > inView.frame.size.width) {
			direction = direction | SNPopupViewLeft;
			horizontalOffset = inView.frame.size.width - left_viewRect;
			
			if (left_viewRect + horizontalOffset > pointToBeShown.x + HORIZONTAL_SAFE_MARGIN) {
			}
			else {
				pointToBeShown.x = inView.frame.size.width - HORIZONTAL_SAFE_MARGIN;
			}
			viewRect.origin.x += horizontalOffset;
			contentRect.origin.x += horizontalOffset;
			popupRect.origin.x += horizontalOffset;
		}
	}
	else {
		pointToBeShown = p;
		
		contentRect.origin.x = p.x - (int)contentBounds.size.width / 2;
		contentRect.origin.y = p.y + CONTENT_OFFSET.height + POPUP_ROOT_SIZE.height;
		contentRect.size = contentBounds.size;
		
		popupBounds.origin = CGPointMake(0, 0);
		popupBounds.size.width = contentBounds.size.width + CONTENT_OFFSET.width + CONTENT_OFFSET.width;
		popupBounds.size.height = contentBounds.size.height + CONTENT_OFFSET.height + CONTENT_OFFSET.height + POPUP_ROOT_SIZE.height;
		
		popupRect.origin.x = contentRect.origin.x - CONTENT_OFFSET.width;
		popupRect.origin.y = contentRect.origin.y - CONTENT_OFFSET.height - POPUP_ROOT_SIZE.height;
		popupRect.size = popupBounds.size;
		
		viewBounds.origin = CGPointMake(0, 0);
		viewBounds.size.width = popupRect.size.width + SHADOW_OFFSET.width + SHADOW_OFFSET.width;
		viewBounds.size.height = popupRect.size.height + SHADOW_OFFSET.height + SHADOW_OFFSET.height;
		
		viewRect.origin.x = popupRect.origin.x - SHADOW_OFFSET.width;
		viewRect.origin.y = popupRect.origin.y - SHADOW_OFFSET.height;
		viewRect.size = viewBounds.size;
		
		float left_viewRect = viewRect.origin.x + viewRect.size.width;
		
		if (viewRect.origin.x < 0) {
			direction = direction | SNPopupViewRight;
			horizontalOffset = viewRect.origin.x;
			
			if (viewRect.origin.x - horizontalOffset < pointToBeShown.x - HORIZONTAL_SAFE_MARGIN) {
			}
			else {
				pointToBeShown.x = HORIZONTAL_SAFE_MARGIN;
			}
			viewRect.origin.x -= horizontalOffset;
			contentRect.origin.x -= horizontalOffset;
			popupRect.origin.x -= horizontalOffset;
		}
		else if (left_viewRect > inView.frame.size.width) {
			direction = direction | SNPopupViewLeft;
			horizontalOffset = inView.frame.size.width - left_viewRect;
			
			if (left_viewRect + horizontalOffset > pointToBeShown.x + HORIZONTAL_SAFE_MARGIN) {
			}
			else {
				pointToBeShown.x = inView.frame.size.width - HORIZONTAL_SAFE_MARGIN;
			}
			viewRect.origin.x += horizontalOffset;
			contentRect.origin.x += horizontalOffset;
			popupRect.origin.x += horizontalOffset;
		}
	}
	
	contentRect.origin.x -= viewRect.origin.x;
	contentRect.origin.y -= viewRect.origin.y;
	popupRect.origin.x -= viewRect.origin.x;
	popupRect.origin.y -= viewRect.origin.y;
	pointToBeShown.x -= viewRect.origin.x;
	pointToBeShown.y -= viewRect.origin.y;
	
	BOOL isAlreadyShown = (self.superview == inView);
	
	if (isAlreadyShown) {
		[self setNeedsDisplay];
		
		if (animated) {
			[UIView beginAnimations:@"move" context:nil];
			[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
		}
		self.frame = viewRect;
		if (animated) {
			[UIView commitAnimations];
		}
	}
	else {
		[inView addSubview:self];
		self.frame = viewRect;		
		
		if (contentView) {
			[self addSubview:contentView];
			[contentView setFrame:contentRect];
		}
		
		if (animated)
			[self popup];
	}
}

#pragma mark - Core Animation call back

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
	[self removeFromSuperview];
}

#pragma mark - Make CoreAnimation object

- (CAKeyframeAnimation *)getAlphaAnimationForPopup {
	
	CAKeyframeAnimation *alphaAnimation = [CAKeyframeAnimation	animationWithKeyPath:@"opacity"];
	alphaAnimation.removedOnCompletion = NO;
	alphaAnimation.values = [NSArray arrayWithObjects:
							 [NSNumber numberWithFloat:0],
							 [NSNumber numberWithFloat:0.7],
							 [NSNumber numberWithFloat:1],
							 nil];
	alphaAnimation.keyTimes = [NSArray arrayWithObjects:
							   [NSNumber numberWithFloat:0],
							   [NSNumber numberWithFloat:0.1],
							   [NSNumber numberWithFloat:1],
							   nil];
	return alphaAnimation;
}

- (CAKeyframeAnimation *)getPositionAnimationForPopup {	
	float r1 = 0.1;
	float r2 = 1.4;
	float r3 = 1;
	float r4 = 0.8;
	float r5 = 1;
	
	float y_offset =  (popupRect.size.height / 2 - POPUP_ROOT_SIZE.height);
	
	CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
	CATransform3D tm1, tm2, tm3, tm4, tm5;
	
	if (direction & SNPopupViewUp) {
		if (direction & SNPopupViewLeft)
			horizontalOffset = -horizontalOffset;
		tm1 = CATransform3DMakeTranslation(horizontalOffset * (1 - r1), y_offset * (1 - r1), 0);
		tm2 = CATransform3DMakeTranslation(horizontalOffset * (1 - r2), y_offset * (1 - r2), 0);
		tm3 = CATransform3DMakeTranslation(horizontalOffset * (1 - r3), y_offset * (1 - r3), 0);
		tm4 = CATransform3DMakeTranslation(horizontalOffset * (1 - r4), y_offset * (1 - r4), 0);
		tm5 = CATransform3DMakeTranslation(horizontalOffset * (1 - r5), y_offset * (1 - r5), 0);
	}
	else {
		if (direction & SNPopupViewLeft)
			horizontalOffset = -horizontalOffset;		
		tm1 = CATransform3DMakeTranslation(horizontalOffset * (1 - r1), -y_offset * (1 - r1), 0);
		tm2 = CATransform3DMakeTranslation(horizontalOffset * (1 - r2), -y_offset * (1 - r2), 0);
		tm3 = CATransform3DMakeTranslation(horizontalOffset * (1 - r3), -y_offset * (1 - r3), 0);
		tm4 = CATransform3DMakeTranslation(horizontalOffset * (1 - r4), -y_offset * (1 - r4), 0);
		tm5 = CATransform3DMakeTranslation(horizontalOffset * (1 - r5), -y_offset * (1 - r5), 0);
	}
	tm1 = CATransform3DScale(tm1, r1, r1, 1);
	tm2 = CATransform3DScale(tm2, r2, r2, 1);
	tm3 = CATransform3DScale(tm3, r3, r3, 1);
	tm4 = CATransform3DScale(tm4, r4, r4, 1);
	tm5 = CATransform3DScale(tm5, r5, r5, 1);
	
	positionAnimation.values = [NSArray arrayWithObjects:
								[NSValue valueWithCATransform3D:tm1],
								[NSValue valueWithCATransform3D:tm2],
								[NSValue valueWithCATransform3D:tm3],
								[NSValue valueWithCATransform3D:tm4],
								[NSValue valueWithCATransform3D:tm5],
								nil];
	positionAnimation.keyTimes = [NSArray arrayWithObjects:
								  [NSNumber numberWithFloat:0.0],
								  [NSNumber numberWithFloat:0.2],
								  [NSNumber numberWithFloat:0.4],
								  [NSNumber numberWithFloat:0.7], 
								  [NSNumber numberWithFloat:1.0],
								  nil];
	return positionAnimation;
}

#pragma mark - Popup and dismiss

- (void)popup {	
	CAKeyframeAnimation *positionAnimation = [self getPositionAnimationForPopup];
	CAKeyframeAnimation *alphaAnimation = [self getAlphaAnimationForPopup];
	
	CAAnimationGroup *group = [CAAnimationGroup animation];
	group.animations = [NSArray arrayWithObjects:positionAnimation, alphaAnimation, nil];
	group.duration = POPUP_ANIMATION_DURATION;
	group.removedOnCompletion = YES;
	group.fillMode = kCAFillModeForwards;
	
	[self.layer addAnimation:group forKey:@"hoge"];
}

- (BOOL)shouldBeDismissedFor:(NSSet *)touches withEvent:(UIEvent *)event {
	UITouch *touch = [touches anyObject];
	
	CGPoint p = [touch locationInView:self];
	return !CGRectContainsPoint(contentRect, p);
}

- (void)dismissModal {
	if ([peekView superview]) 
		[delegate didDismissModal:self];
	[peekView removeFromSuperview];
	
	[self dismiss:animatedWhenAppering];
}

- (void)dismiss:(BOOL)animtaed {
	if (animtaed)
		[self dismiss];
	else {
		[self removeFromSuperview];
	}
}

- (void)dismiss {
	CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
	
	float r1 = 1.0;
	float r2 = 0.1;
	
	float y_offset =  (popupRect.size.height/2 - POPUP_ROOT_SIZE.height);
	
	CAKeyframeAnimation *alphaAnimation = [CAKeyframeAnimation	animationWithKeyPath:@"opacity"];
	alphaAnimation.removedOnCompletion = NO;
	alphaAnimation.values = [NSArray arrayWithObjects:
							 [NSNumber numberWithFloat:1],
							 [NSNumber numberWithFloat:0],
							 nil];
	alphaAnimation.keyTimes = [NSArray arrayWithObjects:
							   [NSNumber numberWithFloat:0],
							   [NSNumber numberWithFloat:1],
							   nil];
	
	CATransform3D tm1, tm2;
	if (direction & SNPopupViewUp) {
		tm1 = CATransform3DMakeTranslation(horizontalOffset * (1 - r1), y_offset * (1 - r1), 0);
		tm2 = CATransform3DMakeTranslation(horizontalOffset * (1 - r2), y_offset * (1 - r2), 0);
	}
	else {	
		tm1 = CATransform3DMakeTranslation(horizontalOffset * (1 - r1), -y_offset * (1 - r1), 0);
		tm2 = CATransform3DMakeTranslation(horizontalOffset * (1 - r2), -y_offset * (1 - r2), 0);
		
	}
	tm1 = CATransform3DScale(tm1, r1, r1, 1);
	tm2 = CATransform3DScale(tm2, r2, r2, 1);
	
	positionAnimation.values = [NSArray arrayWithObjects:
								[NSValue valueWithCATransform3D:tm1],
								[NSValue valueWithCATransform3D:tm2],
								nil];
	positionAnimation.keyTimes = [NSArray arrayWithObjects:
								  [NSNumber numberWithFloat:0],
								  [NSNumber numberWithFloat:1.0],
								  nil];
	
	CAAnimationGroup *group = [CAAnimationGroup animation];
	group.animations = [NSArray arrayWithObjects:positionAnimation, alphaAnimation, nil];
	group.duration = DISMISS_ANIMATION_DURATION;
	group.removedOnCompletion = NO;
	group.fillMode = kCAFillModeForwards;
	group.delegate = self;
	
	[self.layer addAnimation:group forKey:@"hoge"];
}

#pragma mark - Drawing

- (void)makePathCircleCornerRect:(CGRect)rect radius:(float)radius popPoint:(CGPoint)popPoint {
    CGContextRef context = UIGraphicsGetCurrentContext();
	
	if (direction & SNPopupViewUp) {
		rect.size.height -= POPUP_ROOT_SIZE.height;
        
		CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
		CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect ), maxy = CGRectGetMaxY( rect );
		
		CGFloat popRightEdgeX = popPoint.x + (int)POPUP_ROOT_SIZE.width / 2;
		CGFloat popRightEdgeY = maxy;
		
		CGFloat popLeftEdgeX = popPoint.x - (int)POPUP_ROOT_SIZE.width / 2;
		CGFloat popLeftEdgeY = maxy;
		
		CGContextMoveToPoint(context, minx, midy);
		CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
		CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
		
		
		CGContextAddArcToPoint(context, maxx, maxy, popRightEdgeX, popRightEdgeY, radius);
		CGContextAddLineToPoint(context, popRightEdgeX, popRightEdgeY);
		CGContextAddLineToPoint(context, popPoint.x, popPoint.y);
		CGContextAddLineToPoint(context, popLeftEdgeX, popLeftEdgeY);
		CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
		CGContextAddLineToPoint(context, minx, midy);
		
	}
	else {
		rect.origin.y += POPUP_ROOT_SIZE.height;
		rect.size.height -= POPUP_ROOT_SIZE.height;
        
		CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
		CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect ), maxy = CGRectGetMaxY( rect );
		
		CGFloat popRightEdgeX = popPoint.x + (int)POPUP_ROOT_SIZE.width / 2;
		CGFloat popRightEdgeY = miny;
		
		CGFloat popLeftEdgeX = popPoint.x - (int)POPUP_ROOT_SIZE.width / 2;
		CGFloat popLeftEdgeY = miny;
		
		CGContextMoveToPoint(context, minx, midy);
		CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
		CGContextAddLineToPoint(context, popLeftEdgeX, popLeftEdgeY);
		CGContextAddLineToPoint(context, popPoint.x, popPoint.y);
		CGContextAddLineToPoint(context, popRightEdgeX, popRightEdgeY);
		CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
		CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
		CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
	}
}

- (void)makeGrowingPathCircleCornerRect:(CGRect)rect radius:(float)radius {
	CGContextRef context = UIGraphicsGetCurrentContext();
	
	rect.origin.y += 1;
	rect.origin.x += 1;
	rect.size.width -= 2;
    
    CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
    CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect );
	
	CGFloat rightEdgeX = minx;
	CGFloat rightEdgeY = midy - 10;
	
	CGFloat leftEdgeX = maxx;
	CGFloat leftEdgeY = midy - 10;
	
    CGContextMoveToPoint(context, rightEdgeX, rightEdgeY);
    CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
    CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
    CGContextAddLineToPoint(context, leftEdgeX, leftEdgeY);
}

#pragma mark - Override

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	DNSLogMethod
	
	if ([self shouldBeDismissedFor:touches withEvent:event] && peekView != nil) {
		[self dismissModal];
		return;
	}
	
	if ([target respondsToSelector:action]) {
		[target performSelector:action withObject:self];
	}
}

- (void)drawRect:(CGRect)rect {	
	CGContextRef context = UIGraphicsGetCurrentContext();
    
#ifdef _CONFIRM_REGION
	CGContextFillRect(context, rect);
	CGContextSetRGBFillColor(context, 1, 0, 0, 1);
	CGContextFillRect(context, popupRect);
	CGContextSetRGBFillColor(context, 1, 1, 0, 1);
	CGContextFillRect(context, contentRect);
#endif
    
	CGContextSaveGState(context);
	
	CGContextSetRGBFillColor(context, 0.1, 0.1, 0.1, ALPHA);
	CGContextSetShadowWithColor (context, CGSizeMake(0, 2), 2, [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.5] CGColor]);
	[self makePathCircleCornerRect:popupRect radius:10 popPoint:pointToBeShown];
	CGContextClosePath(context);
	CGContextFillPath(context);
	CGContextRestoreGState(context);
    
	CGContextSaveGState(context);
	[self makePathCircleCornerRect:popupRect radius:10 popPoint:pointToBeShown];
	CGContextClip(context);
	if (direction & SNPopupViewUp) {
		CGContextDrawLinearGradient(context, gradient, CGPointMake(0, popupRect.origin.y), CGPointMake(0, popupRect.origin.y + (int)(popupRect.size.height-POPUP_ROOT_SIZE.height) / 2), 0);
		CGContextDrawLinearGradient(context, gradient2, CGPointMake(0, popupRect.origin.y + (int)(popupRect.size.height-POPUP_ROOT_SIZE.height) / 2), CGPointMake(0, popupRect.origin.y + popupRect.size.height-POPUP_ROOT_SIZE.height), 0);
	}
	else {
		int h = (int)(popupRect.size.height - POPUP_ROOT_SIZE.height);
		CGContextDrawLinearGradient(context, gradient, CGPointMake(0, popupRect.origin.y + POPUP_ROOT_SIZE.height), CGPointMake(0, popupRect.origin.y + h / 2 + POPUP_ROOT_SIZE.height), 0);
		CGContextDrawLinearGradient(context, gradient2, CGPointMake(0, popupRect.origin.y + h / 2 + POPUP_ROOT_SIZE.height), CGPointMake(0, popupRect.origin.y + popupRect.size.height), 0);
	}
	CGContextRestoreGState(context);
    
	if ([title length]) {
		CGContextSetRGBFillColor(context, 1, 1, 1, 1);
		UIFont *font = [UIFont boldSystemFontOfSize:fontSize];
		[title drawInRect:contentRect withFont:font];
	}
	if (image) {
		[image drawInRect:contentRect];
	}
}

#pragma mark - dealloc

- (void)dealloc {
	DNSLogMethod
	CGGradientRelease(gradient);
	CGGradientRelease(gradient2);
	
	[peekView release];
	[title release];
	[image release];
	[contentView release];
    [super dealloc];
}

@end

 

SNPopupView+UsingPrivateMethod.h

 

#import "SNPopupView.h"

@interface SNPopupView(UsingPrivateMethod)

- (void)showFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView;
- (void)showFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView animated:(BOOL)animated;

- (void)presentModalFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView;
- (void)presentModalFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView animated:(BOOL)animated;

@end

 

SNPopupView+UsingPrivateMethod.m

 

#import "SNPopupView+UsingPrivateMethod.h"

@interface SNPopupView(UsingPrivateMethod_Private)

- (void)createAndAttachTouchPeekView;

@end

@implementation SNPopupView(UsingPrivateMethod)

- (void)presentModalFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView {
	animatedWhenAppering = YES;
	[self createAndAttachTouchPeekView];
	[self showFromBarButtonItem:barButtonItem inView:[[UIApplication sharedApplication] keyWindow]];
}

- (void)presentModalFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView animated:(BOOL)animated {
	animatedWhenAppering = animated;
	[self createAndAttachTouchPeekView];
	[self showFromBarButtonItem:barButtonItem inView:[[UIApplication sharedApplication] keyWindow] animated:animated];
}

- (void)showFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView {
	[self showFromBarButtonItem:barButtonItem inView:inView animated:YES];
}

- (void)showFromBarButtonItem:(UIBarButtonItem *)barButtonItem inView:(UIView *)inView animated:(BOOL)animated {
	
	if(![barButtonItem respondsToSelector:@selector(view)]) {
		return;
	}
	
	UIView *targetView = (UIView *)[barButtonItem performSelector:@selector(view)];
	UIView *targetSuperview = [targetView superview];
	
	BOOL isOnNavigationBar = YES;
	
	if ([targetSuperview isKindOfClass:[UINavigationBar class]]) {
		isOnNavigationBar = YES;
	}
	else if ([targetSuperview isKindOfClass:[UIToolbar class]]) {
		isOnNavigationBar = NO;
	}
	else {
		return;
	}
	
	CGRect rect = [targetSuperview convertRect:targetView.frame toView:inView];
	
	CGPoint p;
	p.x = rect.origin.x + (int)rect.size.width / 2;
	
	if (isOnNavigationBar)
		p.y = rect.origin.y + rect.size.height + BAR_BUTTON_ITEM_UPPER_MARGIN;
	else
		p.y = rect.origin.y - BAR_BUTTON_ITEM_BOTTOM_MARGIN;
	
	[self showAtPoint:p inView:inView animated:animated];
}

@end

 

示例:

 

PopupViewTestViewController.h

 

#import <UIKit/UIKit.h>

#import "SNPopupView.h"
#import "SNPopupView+UsingPrivateMethod.h"

@interface PopupViewTestViewController : UIViewController <SNPopupViewModalDelegate> {
	SNPopupView		*popup;
	int				currentMessageIndex;
	IBOutlet UIView *testContentView;
	
	IBOutlet UISwitch *animationSwitch;
	IBOutlet UISwitch *modalSwitch;
}

- (IBAction)pushButton:(id)sender;
- (void)didDismissModal:(SNPopupView *)popupview;
- (void)didTouchPopupView:(SNPopupView *)sender;
@end

 

PopupViewTestViewController.m

 

#import "PopupViewTestViewController.h"

@implementation PopupViewTestViewController

- (IBAction)pushButton:(id)sender {
	DNSLogMethod
	
	if (popup == nil) {
		if (currentMessageIndex == 0) {
			popup = [[SNPopupView alloc] initWithContentView:testContentView contentSize:CGSizeMake(203, 63)];
			currentMessageIndex++;
		}
		else if (currentMessageIndex == 1) {
			popup = [[SNPopupView alloc] initWithString:@"test message" withFontOfSize:29];
			currentMessageIndex++;
		}
		else if (currentMessageIndex == 2) {
			popup = [[SNPopupView alloc] initWithImage:[UIImage imageNamed:@"2tchSmall.png"]];
			currentMessageIndex = 0;
		}
		if (modalSwitch.on)
			[popup presentModalFromBarButtonItem:sender inView:self.view animated:animationSwitch.on];
		else
			[popup showFromBarButtonItem:sender inView:self.view animated:animationSwitch.on];
		[popup addTarget:self action:@selector(didTouchPopupView:)];
		[popup release];
		[popup setDelegate:self];
	}
	else if (!modalSwitch.on) {
		[popup dismiss:animationSwitch.on];
		popup = nil;
	}
}

- (void)didTouchPopupView:(SNPopupView *)sender {
	DNSLogMethod
	DNSLog(@"%@", sender);
}

- (void)didDismissModal:(SNPopupView *)popupview {
	DNSLogMethod
	if (popupview == popup) {
		popup = nil;
	}
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	UITouch *touch = [touches anyObject];
	
	if (popup == nil) {
		if (currentMessageIndex == 0) {
			popup = [[SNPopupView alloc] initWithImage:[UIImage imageNamed:@"2tchSmall.png"]];
			currentMessageIndex++;
		}
		else if (currentMessageIndex == 1) {
			popup = [[SNPopupView alloc] initWithString:@"test message" withFontOfSize:16];
			currentMessageIndex++;
		}
		else if (currentMessageIndex == 2) {
			popup = [[SNPopupView alloc] initWithContentView:testContentView contentSize:CGSizeMake(203, 63)];
			currentMessageIndex = 0;
		}
		if (modalSwitch.on)
			[popup presentModalAtPoint:[touch locationInView:self.view] inView:self.view animated:animationSwitch.on];
		else
			[popup showAtPoint:[touch locationInView:self.view] inView:self.view animated:animationSwitch.on];
		[popup addTarget:self action:@selector(didTouchPopupView:)];
		[popup release];
		[popup setDelegate:self];
	}
	else if (!modalSwitch.on) {
		[popup dismiss:animationSwitch.on];
		popup = nil;
	}
}

@end

猜你喜欢

转载自eric-gao.iteye.com/blog/1568542