123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050 |
- //
- // PopoverView.m
- // Embark
- //
- // Created by Oliver Rickard on 20/08/2012.
- //
- //
- #import "PopoverView.h"
- #import "PopoverView_Configuration.h"
- #import <QuartzCore/QuartzCore.h>
- #pragma mark - Implementation
- @implementation PopoverView
- @synthesize subviewsArray;
- @synthesize contentView;
- @synthesize titleView;
- @synthesize delegate;
- #pragma mark - Static Methods
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withText:text];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withText:(NSString *)text delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withTitle:title withText:text];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withViewArray:viewArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withTitle:title withViewArray:viewArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withStringArray:stringArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withTitle:title withStringArray:stringArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withStringArray:stringArray withImageArray:imageArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withTitle:title withStringArray:stringArray withImageArray:imageArray];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withContentView:(UIView *)cView delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withTitle:title withContentView:cView];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- + (PopoverView *)showPopoverAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)cView delegate:(id<PopoverViewDelegate>)delegate {
- PopoverView *popoverView = [[PopoverView alloc] initWithFrame:CGRectZero];
- [popoverView showAtPoint:point inView:view withContentView:cView];
- popoverView.delegate = delegate;
- [popoverView RELEASE];
- return popoverView;
- }
- #pragma mark - View Lifecycle
- - (id)initWithFrame:(CGRect)frame
- {
- self = [super initWithFrame:frame];
- if (self) {
- // Initialization code
-
- self.backgroundColor = [UIColor clearColor];
-
- self.titleView = nil;
- self.contentView = nil;
-
- showDividerRects = kShowDividersBetweenViews;
- }
- return self;
- }
- - (void)dealloc
- {
- self.subviewsArray = nil;
-
- if (dividerRects) {
- [dividerRects RELEASE];
- dividerRects = nil;
- }
-
- self.contentView = nil;
- self.titleView = nil;
-
- [super DEALLOC];
- }
- #pragma mark - Display methods
- // get the screen size, adjusted for orientation and status bar display
- // see http://stackoverflow.com/questions/7905432/how-to-get-orientation-dependent-height-and-width-of-the-screen/7905540#7905540
- - (CGSize) screenSize
- {
- UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
- CGSize size = [UIScreen mainScreen].bounds.size;
- UIApplication *application = [UIApplication sharedApplication];
- if (UIInterfaceOrientationIsLandscape(orientation))
- {
- size = CGSizeMake(size.height, size.width);
- }
- if (application.statusBarHidden == NO)
- {
- size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height);
- }
- return size;
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withText:(NSString *)text
- {
- UIFont *font = kTextFont;
-
- CGSize screenSize = [self screenSize];
- CGSize textSize = [text boundingRectWithSize:CGSizeMake(screenSize.width - kHorizontalMargin*4.f, 1000.f) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;
-
- UILabel *textView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)];
- textView.backgroundColor = [UIColor clearColor];
- textView.userInteractionEnabled = NO;
- [textView setNumberOfLines:0]; //This is so the label word wraps instead of cutting off the text
- textView.font = font;
- textView.textAlignment = kTextAlignment;
- textView.textColor = kTextColor;
- textView.text = text;
-
- [self showAtPoint:point inView:view withViewArray:[NSArray arrayWithObject:[textView AUTORELEASE]]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withText:(NSString *)text
- {
- UIFont *font = kTextFont;
-
- CGSize screenSize = [self screenSize];
- CGSize textSize = [text boundingRectWithSize:CGSizeMake(screenSize.width - kHorizontalMargin*4.f, 1000.f) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;
-
- UILabel *textView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)];
- textView.backgroundColor = [UIColor clearColor];
- textView.userInteractionEnabled = NO;
- [textView setNumberOfLines:0]; //This is so the label word wraps instead of cutting off the text
- textView.font = font;
- textView.textAlignment = kTextAlignment;
- textView.textColor = kTextColor;
- textView.text = text;
-
- [self showAtPoint:point inView:view withTitle:title withViewArray:[NSArray arrayWithObject:[textView AUTORELEASE]]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withViewArray:(NSArray *)viewArray
- {
- UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
-
- float totalHeight = 0.f;
- float totalWidth = 0.f;
-
- int i = 0;
-
- //Position each view the first time, and identify which view has the largest width that controls
- //the sizing of the popover.
- for (UIView *view in viewArray) {
-
- view.frame = CGRectMake(0, totalHeight, view.frame.size.width, view.frame.size.height);
- //Only add padding below the view if it's not the last item
- float padding = (i == viewArray.count-1) ? 0 : kBoxPadding;
-
- totalHeight += view.frame.size.height + padding;
-
- if (view.frame.size.width > totalWidth) {
- totalWidth = view.frame.size.width;
- }
-
- [container addSubview:view];
-
- i++;
- }
-
- //If dividers are enabled, then we allocate the divider rect array. This will hold NSValues
- if (kShowDividersBetweenViews) {
- dividerRects = [[NSMutableArray alloc] initWithCapacity:viewArray.count-1];
- }
-
- container.frame = CGRectMake(0, 0, totalWidth, totalHeight);
-
- i = 0;
-
- totalHeight = 0;
-
- //Now we actually change the frame element for each subview, and center the views horizontally.
- for (UIView *view in viewArray) {
- if ([view autoresizingMask] == UIViewAutoresizingFlexibleWidth) {
- //Now make sure all flexible views are the full width
- view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, totalWidth, view.frame.size.height);
- } else {
- //If the view is not flexible width, then we position it centered in the view
- //without stretching it.
- view.frame = CGRectMake(floorf(CGRectGetMinX(boxFrame) + totalWidth*0.5f - view.frame.size.width*0.5f), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
- }
-
- //and if dividers are enabled, we record their position for the drawing methods
- if (kShowDividersBetweenViews && i != viewArray.count-1) {
- CGRect dividerRect = CGRectMake(view.frame.origin.x, floorf(view.frame.origin.y + view.frame.size.height + kBoxPadding*0.5f), view.frame.size.width, 0.5f);
-
- [((NSMutableArray *)dividerRects) addObject:[NSValue valueWithCGRect:dividerRect]];
- }
-
- //Only add padding below the view if it's not the last item
- float padding = (i == viewArray.count-1) ? 0.f : kBoxPadding;
-
- totalHeight += view.frame.size.height + padding;
-
- i++;
- }
-
- self.subviewsArray = viewArray;
-
- [self showAtPoint:point inView:view withContentView:[container AUTORELEASE]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withViewArray:(NSArray *)viewArray
- {
- UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
-
- //Create a label for the title text.
- CGSize titleSize = [title sizeWithAttributes:@{NSFontAttributeName : kTitleFont}];
-
- UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.f, 0.f, titleSize.width, titleSize.height)];
- titleLabel.backgroundColor = [UIColor clearColor];
- titleLabel.font = kTitleFont;
- titleLabel.textAlignment = UITextAlignmentCenter;
- titleLabel.textColor = kTitleColor;
- titleLabel.text = title;
-
- //Make sure that the title's label will have non-zero height. If it has zero height, then we don't allocate any space
- //for it in the positioning of the views.
- float titleHeightOffset = (titleSize.height > 0.f ? kBoxPadding : 0.f);
-
- float totalHeight = titleSize.height + titleHeightOffset + kBoxPadding;
- float totalWidth = titleSize.width;
-
- int i = 0;
-
- //Position each view the first time, and identify which view has the largest width that controls
- //the sizing of the popover.
- for (UIView *view in viewArray) {
-
- view.frame = CGRectMake(0, totalHeight, view.frame.size.width, view.frame.size.height);
-
- //Only add padding below the view if it's not the last item.
- float padding = (i == viewArray.count-1) ? 0.f : kBoxPadding;
-
- totalHeight += view.frame.size.height + padding;
-
- if (view.frame.size.width > totalWidth) {
- totalWidth = view.frame.size.width;
- }
-
- [container addSubview:view];
-
- i++;
- }
-
- //If dividers are enabled, then we allocate the divider rect array. This will hold NSValues
- if (kShowDividersBetweenViews) {
- dividerRects = [[NSMutableArray alloc] initWithCapacity:viewArray.count-1];
- }
-
- i = 0;
-
- for (UIView *view in viewArray) {
- if ([view autoresizingMask] == UIViewAutoresizingFlexibleWidth) {
- //Now make sure all flexible views are the full width
- view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, totalWidth, view.frame.size.height);
- } else {
- //If the view is not flexible width, then we position it centered in the view
- //without stretching it.
- view.frame = CGRectMake(floorf(CGRectGetMinX(boxFrame) + totalWidth*0.5f - view.frame.size.width*0.5f), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
- }
-
- //and if dividers are enabled, we record their position for the drawing methods
- if (kShowDividersBetweenViews && i != viewArray.count-1) {
- CGRect dividerRect = CGRectMake(view.frame.origin.x, floorf(view.frame.origin.y + view.frame.size.height + kBoxPadding*0.5f), view.frame.size.width, 0.5f);
-
- [((NSMutableArray *)dividerRects) addObject:[NSValue valueWithCGRect:dividerRect]];
- }
-
- i++;
- }
-
- titleLabel.frame = CGRectMake(floorf(totalWidth*0.5f - titleSize.width*0.5f), 0, titleSize.width, titleSize.height);
-
- //Store the titleView as an instance variable if it is larger than 0 height (not an empty string)
- if (titleSize.height > 0) {
- self.titleView = titleLabel;
- }
-
- [container addSubview:[titleLabel AUTORELEASE]];
-
- container.frame = CGRectMake(0, 0, totalWidth, totalHeight);
-
- self.subviewsArray = viewArray;
-
- [self showAtPoint:point inView:view withContentView:[container AUTORELEASE]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray
- {
- NSMutableArray *labelArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count];
-
- UIFont *font = kTextFont;
-
- for (NSString *string in stringArray) {
- CGSize textSize = [string sizeWithAttributes:@{NSFontAttributeName : font}];
- UIButton *textButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)];
- textButton.backgroundColor = [UIColor clearColor];
- textButton.titleLabel.font = font;
- textButton.titleLabel.textAlignment = kTextAlignment;
- textButton.titleLabel.textColor = kTextColor;
- [textButton setTitle:string forState:UIControlStateNormal];
- textButton.layer.cornerRadius = 4.f;
- [textButton setTitleColor:kTextColor forState:UIControlStateNormal];
- [textButton setTitleColor:kTextHighlightColor forState:UIControlStateHighlighted];
- [textButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
-
- [labelArray addObject:[textButton AUTORELEASE]];
- }
-
- [self showAtPoint:point inView:view withViewArray:[labelArray AUTORELEASE]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray
- {
- NSMutableArray *labelArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count];
-
- UIFont *font = kTextFont;
-
- for (NSString *string in stringArray) {
- CGSize textSize = [string sizeWithAttributes:@{NSFontAttributeName : font}];
- UIButton *textButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)];
- textButton.backgroundColor = [UIColor clearColor];
- textButton.titleLabel.font = font;
- textButton.titleLabel.textAlignment = kTextAlignment;
- textButton.titleLabel.textColor = kTextColor;
- [textButton setTitle:string forState:UIControlStateNormal];
- textButton.layer.cornerRadius = 4.f;
- [textButton setTitleColor:kTextColor forState:UIControlStateNormal];
- [textButton setTitleColor:kTextHighlightColor forState:UIControlStateHighlighted];
- [textButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
-
- [labelArray addObject:[textButton AUTORELEASE]];
- }
-
- [self showAtPoint:point inView:view withTitle:title withViewArray:[labelArray AUTORELEASE]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray
- {
- //Here we do something pretty similar to the stringArray method above.
- //We create an array of subviews that contains the strings and images centered above a label.
-
- NSAssert((stringArray.count == imageArray.count), @"stringArray.count should equal imageArray.count");
- NSMutableArray* tempViewArray = [self makeTempViewsWithStrings:stringArray andImages:imageArray];
-
- [self showAtPoint:point inView:view withViewArray:tempViewArray];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withStringArray:(NSArray *)stringArray withImageArray:(NSArray *)imageArray
- {
- NSAssert((stringArray.count == imageArray.count), @"stringArray.count should equal imageArray.count");
- NSMutableArray* tempViewArray = [self makeTempViewsWithStrings:stringArray andImages:imageArray];
-
- [self showAtPoint:point inView:view withTitle:title withViewArray:tempViewArray];
- }
- - (NSMutableArray*) makeTempViewsWithStrings:(NSArray *)stringArray andImages:(NSArray *)imageArray
- {
- NSMutableArray *tempViewArray = [[NSMutableArray alloc] initWithCapacity:stringArray.count];
-
- UIFont *font = kTextFont;
-
- for (int i = 0; i < stringArray.count; i++) {
- NSString *string = [stringArray objectAtIndex:i];
-
- //First we build a label for the text to set in.
- CGSize textSize = [string sizeWithAttributes:@{NSFontAttributeName : font}];
- UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, textSize.width, textSize.height)];
- label.backgroundColor = [UIColor clearColor];
- label.font = font;
- label.textAlignment = kTextAlignment;
- label.textColor = kTextColor;
- label.text = string;
- label.layer.cornerRadius = 4.f;
-
- //Now we grab the image at the same index in the imageArray, and create
- //a UIImageView for it.
- UIImage *image = [imageArray objectAtIndex:i];
- UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
-
- //Take the larger of the two widths as the width for the container
- float containerWidth = MAX(imageView.frame.size.width, label.frame.size.width);
- float containerHeight = label.frame.size.height + kImageTopPadding + kImageBottomPadding + imageView.frame.size.height;
-
- //This container will hold both the image and the label
- UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerWidth, containerHeight)];
-
- //Now we do the frame manipulations to put the imageView on top of the label, both centered
- imageView.frame = CGRectMake(floorf(containerWidth*0.5f - imageView.frame.size.width*0.5f), kImageTopPadding, imageView.frame.size.width, imageView.frame.size.height);
- label.frame = CGRectMake(floorf(containerWidth*0.5f - label.frame.size.width*0.5f), imageView.frame.size.height + kImageBottomPadding + kImageTopPadding, label.frame.size.width, label.frame.size.height);
-
- [containerView addSubview:imageView];
- [containerView addSubview:label];
-
- [label RELEASE];
- [imageView RELEASE];
-
- [tempViewArray addObject:containerView];
- [containerView RELEASE];
- }
- return [tempViewArray AUTORELEASE];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title withContentView:(UIView *)cView
- {
- [self showAtPoint:point inView:view withTitle:title withViewArray:[NSArray arrayWithObject:cView]];
- }
- - (void)showAtPoint:(CGPoint)point inView:(UIView *)view withContentView:(UIView *)cView {
-
- //NSLog(@"point:%f,%f", point.x, point.y);
-
- self.contentView = cView;
- parentView = view;
-
- // get the top view
- // http://stackoverflow.com/questions/3843411/getting-reference-to-the-top-most-view-window-in-ios-application/8045804#8045804
- topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
-
- [self setupLayout:point inView:view];
-
- // Make the view small and transparent before animation
- self.alpha = 0.f;
- self.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
-
- // animate into full size
- // First stage animates to 1.05x normal size, then second stage animates back down to 1x size.
- // This two-stage animation creates a little "pop" on open.
- [UIView animateWithDuration:0.2f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
- self.alpha = 1.f;
- self.transform = CGAffineTransformMakeScale(1.05f, 1.05f);
- } completion:^(BOOL finished) {
- [UIView animateWithDuration:0.08f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
- self.transform = CGAffineTransformIdentity;
- } completion:nil];
- }];
- }
- - (void)layoutAtPoint:(CGPoint)point inView:(UIView *)view
- {
- // make transparent
- self.alpha = 0.f;
-
- [self setupLayout:point inView:view];
-
- // animate back to full opacity
- [UIView animateWithDuration:0.2f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
- self.alpha = 1.f;
- } completion:nil];
- }
- -(void)setupLayout:(CGPoint)point inView:(UIView*)view
- {
- CGPoint topPoint = [topView convertPoint:point fromView:view];
- arrowPoint = topPoint;
- //NSLog(@"arrowPoint:%f,%f", arrowPoint.x, arrowPoint.y);
- CGRect topViewBounds = topView.bounds;
- //NSLog(@"topViewBounds %@", NSStringFromCGRect(topViewBounds));
- float contentHeight = contentView.frame.size.height;
- float contentWidth = contentView.frame.size.width;
- float padding = kBoxPadding;
- float boxHeight = contentHeight + 2.f*padding;
- float boxWidth = contentWidth + 2.f*padding;
- float xOrigin = 0.f;
- //Make sure the arrow point is within the drawable bounds for the popover.
- if (arrowPoint.x + kArrowHeight > topViewBounds.size.width - kHorizontalMargin - kBoxRadius - kArrowHorizontalPadding) {//Too far to the right
- arrowPoint.x = topViewBounds.size.width - kHorizontalMargin - kBoxRadius - kArrowHorizontalPadding - kArrowHeight;
- //NSLog(@"Correcting Arrow Point because it's too far to the right");
- } else if (arrowPoint.x - kArrowHeight < kHorizontalMargin + kBoxRadius + kArrowHorizontalPadding) {//Too far to the left
- arrowPoint.x = kHorizontalMargin + kArrowHeight + kBoxRadius + kArrowHorizontalPadding;
- //NSLog(@"Correcting Arrow Point because it's too far to the left");
- }
- //NSLog(@"arrowPoint:%f,%f", arrowPoint.x, arrowPoint.y);
- xOrigin = floorf(arrowPoint.x - boxWidth*0.5f);
- //Check to see if the centered xOrigin value puts the box outside of the normal range.
- if (xOrigin < CGRectGetMinX(topViewBounds) + kHorizontalMargin) {
- xOrigin = CGRectGetMinX(topViewBounds) + kHorizontalMargin;
- } else if (xOrigin + boxWidth > CGRectGetMaxX(topViewBounds) - kHorizontalMargin) {
- //Check to see if the positioning puts the box out of the window towards the left
- xOrigin = CGRectGetMaxX(topViewBounds) - kHorizontalMargin - boxWidth;
- }
- float arrowHeight = kArrowHeight;
- float topPadding = kTopMargin;
- above = YES;
- if (topPoint.y - contentHeight - arrowHeight - topPadding < CGRectGetMinY(topViewBounds)) {
- //Position below because it won't fit above.
- above = NO;
-
- boxFrame = CGRectMake(xOrigin, arrowPoint.y + arrowHeight, boxWidth, boxHeight);
- } else {
- //Position above.
- above = YES;
-
- boxFrame = CGRectMake(xOrigin, arrowPoint.y - arrowHeight - boxHeight, boxWidth, boxHeight);
- }
- //NSLog(@"boxFrame:(%f,%f,%f,%f)", boxFrame.origin.x, boxFrame.origin.y, boxFrame.size.width, boxFrame.size.height);
- CGRect contentFrame = CGRectMake(boxFrame.origin.x + padding, boxFrame.origin.y + padding, contentWidth, contentHeight);
- contentView.frame = contentFrame;
- //We set the anchorPoint here so the popover will "grow" out of the arrowPoint specified by the user.
- //You have to set the anchorPoint before setting the frame, because the anchorPoint property will
- //implicitly set the frame for the view, which we do not want.
- self.layer.anchorPoint = CGPointMake(arrowPoint.x / topViewBounds.size.width, arrowPoint.y / topViewBounds.size.height);
- self.frame = topViewBounds;
- [self setNeedsDisplay];
- [self addSubview:contentView];
- [topView addSubview:self];
- //Add a tap gesture recognizer to the large invisible view (self), which will detect taps anywhere on the screen.
- UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
- tap.cancelsTouchesInView = NO; // Allow touches through to a UITableView or other touchable view, as suggested by Dimajp.
- [self addGestureRecognizer:tap];
- [tap RELEASE];
- self.userInteractionEnabled = YES;
- }
- #pragma mark - Activity Indicator
- //Animates in a progress indicator, and removes
- - (void)showActivityIndicatorWithMessage:(NSString *)msg
- {
- if ([titleView isKindOfClass:[UILabel class]]) {
- ((UILabel *)titleView).text = msg;
- }
-
- if (subviewsArray && (subviewsArray.count > 0)) {
- [UIView animateWithDuration:0.2f animations:^{
- for (UIView *view in subviewsArray) {
- view.alpha = 0.f;
- }
- }];
-
- if (showDividerRects) {
- showDividerRects = NO;
- [self setNeedsDisplay];
- }
- }
-
- if (activityIndicator) {
- [activityIndicator RELEASE];
- [activityIndicator removeFromSuperview];
- activityIndicator = nil;
- }
-
- activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
- activityIndicator.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 10.f, CGRectGetMidY(contentView.bounds) - 10.f + 20.f, 20.f, 20.f);
- [contentView addSubview:activityIndicator];
-
- [activityIndicator startAnimating];
- }
- - (void)hideActivityIndicatorWithMessage:(NSString *)msg
- {
- if ([titleView isKindOfClass:[UILabel class]]) {
- ((UILabel *)titleView).text = msg;
- }
-
- [activityIndicator stopAnimating];
- [UIView animateWithDuration:0.1f animations:^{
- activityIndicator.alpha = 0.f;
- } completion:^(BOOL finished) {
- [activityIndicator RELEASE];
- [activityIndicator removeFromSuperview];
- activityIndicator = nil;
- }];
- }
- - (void)showImage:(UIImage *)image withMessage:(NSString *)msg
- {
- UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
- imageView.alpha = 0.f;
- imageView.frame = CGRectMake(floorf(CGRectGetMidX(contentView.bounds) - image.size.width*0.5f), floorf(CGRectGetMidY(contentView.bounds) - image.size.height*0.5f + ((self.titleView) ? 20 : 0.f)), image.size.width, image.size.height);
- imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
-
- [contentView addSubview:[imageView AUTORELEASE]];
-
- if (subviewsArray && (subviewsArray.count > 0)) {
- [UIView animateWithDuration:0.2f animations:^{
- for (UIView *view in subviewsArray) {
- view.alpha = 0.f;
- }
- }];
-
- if (showDividerRects) {
- showDividerRects = NO;
- [self setNeedsDisplay];
- }
- }
-
- if (msg) {
- if ([titleView isKindOfClass:[UILabel class]]) {
- ((UILabel *)titleView).text = msg;
- }
- }
-
- [UIView animateWithDuration:0.2f delay:0.2f options:UIViewAnimationOptionCurveEaseOut animations:^{
- imageView.alpha = 1.f;
- imageView.transform = CGAffineTransformIdentity;
- } completion:^(BOOL finished) {
- //[imageView removeFromSuperview];
- }];
- }
- - (void)showError
- {
- UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"error"]];
- imageView.alpha = 0.f;
- imageView.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 20.f, CGRectGetMidY(contentView.bounds) - 20.f + ((self.titleView) ? 20 : 0.f), 40.f, 40.f);
- imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
-
- [contentView addSubview:[imageView AUTORELEASE]];
-
- if (subviewsArray && (subviewsArray.count > 0)) {
- [UIView animateWithDuration:0.1f animations:^{
- for (UIView *view in subviewsArray) {
- view.alpha = 0.f;
- }
- }];
-
- if (showDividerRects) {
- showDividerRects = NO;
- [self setNeedsDisplay];
- }
- }
-
- [UIView animateWithDuration:0.1f delay:0.1f options:UIViewAnimationOptionCurveEaseOut animations:^{
- imageView.alpha = 1.f;
- imageView.transform = CGAffineTransformIdentity;
- } completion:^(BOOL finished) {
- //[imageView removeFromSuperview];
- }];
-
- }
- - (void)showSuccess
- {
- UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"success"]];
- imageView.alpha = 0.f;
- imageView.frame = CGRectMake(CGRectGetMidX(contentView.bounds) - 20.f, CGRectGetMidY(contentView.bounds) - 20.f + ((self.titleView) ? 20 : 0.f), 40.f, 40.f);
- imageView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
-
- [contentView addSubview:[imageView AUTORELEASE]];
-
- if (subviewsArray && (subviewsArray.count > 0)) {
- [UIView animateWithDuration:0.1f animations:^{
- for (UIView *view in subviewsArray) {
- view.alpha = 0.f;
- }
- }];
-
- if (showDividerRects) {
- showDividerRects = NO;
- [self setNeedsDisplay];
- }
- }
-
- [UIView animateWithDuration:0.1f delay:0.1f options:UIViewAnimationOptionCurveEaseOut animations:^{
- imageView.alpha = 1.f;
- imageView.transform = CGAffineTransformIdentity;
- } completion:^(BOOL finished) {
- //[imageView removeFromSuperview];
- }];
-
- }
- #pragma mark - User Interaction
- - (void)tapped:(UITapGestureRecognizer *)tap
- {
- CGPoint point = [tap locationInView:contentView];
-
- //NSLog(@"point:(%f,%f)", point.x, point.y);
-
- BOOL found = NO;
-
- //NSLog(@"subviewsArray:%@", subviewsArray);
-
- for (int i = 0; i < subviewsArray.count && !found; i++) {
- UIView *view = [subviewsArray objectAtIndex:i];
-
- //NSLog(@"Rect:(%f,%f,%f,%f)", view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
-
- if (CGRectContainsPoint(view.frame, point)) {
- //The tap was within this view, so we notify the delegate, and break the loop.
-
- found = YES;
-
- //NSLog(@"Tapped subview:%d", i);
-
- if ([view isKindOfClass:[UIButton class]]) {
- return;
- }
-
- if (delegate && [delegate respondsToSelector:@selector(popoverView:didSelectItemAtIndex:)]) {
- [delegate popoverView:self didSelectItemAtIndex:i];
- }
-
- break;
- }
- }
-
- if (!found && CGRectContainsPoint(contentView.bounds, point)) {
- found = YES;
- //NSLog(@"popover box contains point, ignoring user input");
- }
-
- if (!found) {
- [self dismiss:YES];
- }
-
- }
- - (void)didTapButton:(UIButton *)sender
- {
- NSUInteger index = [subviewsArray indexOfObject:sender];
-
- if (index == NSNotFound) {
- return;
- }
-
- if (delegate && [delegate respondsToSelector:@selector(popoverView:didSelectItemAtIndex:)]) {
- [delegate popoverView:self didSelectItemAtIndex:index];
- }
- }
- - (void)dismiss
- {
- [self dismiss:YES];
- }
- - (void)dismiss:(BOOL)animated
- {
- if (!animated)
- {
- [self dismissComplete];
- }
- else
- {
- [UIView animateWithDuration:0.3f animations:^{
- self.alpha = 0.1f;
- self.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
- } completion:^(BOOL finished) {
- [self dismissComplete];
- }];
- }
- }
- - (void)dismissComplete
- {
- [self removeFromSuperview];
-
- if (self.delegate && [self.delegate respondsToSelector:@selector(popoverViewDidDismiss:)]) {
- [delegate popoverViewDidDismiss:self];
- }
- }
- - (void)animateRotationToNewPoint:(CGPoint)point inView:(UIView *)view withDuration:(NSTimeInterval)duration
- {
- [self layoutAtPoint:point inView:view];
- }
- #pragma mark - Drawing Routines
- // Only override drawRect: if you perform custom drawing.
- // An empty implementation adversely affects performance during animation.
- - (void)drawRect:(CGRect)rect
- {
- // Drawing code
-
- // Build the popover path
- CGRect frame = boxFrame;
-
- float xMin = CGRectGetMinX(frame);
- float yMin = CGRectGetMinY(frame);
-
- float xMax = CGRectGetMaxX(frame);
- float yMax = CGRectGetMaxY(frame);
-
- float radius = kBoxRadius; //Radius of the curvature.
-
- float cpOffset = kCPOffset; //Control Point Offset. Modifies how "curved" the corners are.
-
-
- /*
- LT2 RT1
- LT1⌜⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⌝RT2
- | |
- | popover |
- | |
- LB2⌞_______________⌟RB1
- LB1 RB2
-
- Traverse rectangle in clockwise order, starting at LT1
- L = Left
- R = Right
- T = Top
- B = Bottom
- 1,2 = order of traversal for any given corner
-
- */
-
- UIBezierPath *popoverPath = [UIBezierPath bezierPath];
- [popoverPath moveToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + radius)];//LT1
- [popoverPath addCurveToPoint:CGPointMake(xMin + radius, yMin) controlPoint1:CGPointMake(xMin, yMin + radius - cpOffset) controlPoint2:CGPointMake(xMin + radius - cpOffset, yMin)];//LT2
-
- //If the popover is positioned below (!above) the arrowPoint, then we know that the arrow must be on the top of the popover.
- //In this case, the arrow is located between LT2 and RT1
- if (!above) {
- [popoverPath addLineToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMin)];//left side
- [popoverPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMin) controlPoint2:arrowPoint];//actual arrow point
- [popoverPath addCurveToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMin) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMin)];//right side
- }
-
- [popoverPath addLineToPoint:CGPointMake(xMax - radius, yMin)];//RT1
- [popoverPath addCurveToPoint:CGPointMake(xMax, yMin + radius) controlPoint1:CGPointMake(xMax - radius + cpOffset, yMin) controlPoint2:CGPointMake(xMax, yMin + radius - cpOffset)];//RT2
- [popoverPath addLineToPoint:CGPointMake(xMax, yMax - radius)];//RB1
- [popoverPath addCurveToPoint:CGPointMake(xMax - radius, yMax) controlPoint1:CGPointMake(xMax, yMax - radius + cpOffset) controlPoint2:CGPointMake(xMax - radius + cpOffset, yMax)];//RB2
-
- //If the popover is positioned above the arrowPoint, then we know that the arrow must be on the bottom of the popover.
- //In this case, the arrow is located somewhere between LB1 and RB2
- if (above) {
- [popoverPath addLineToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMax)];//right side
- [popoverPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMax) controlPoint2:arrowPoint];//arrow point
- [popoverPath addCurveToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMax) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMax)];
- }
-
- [popoverPath addLineToPoint:CGPointMake(xMin + radius, yMax)];//LB1
- [popoverPath addCurveToPoint:CGPointMake(xMin, yMax - radius) controlPoint1:CGPointMake(xMin + radius - cpOffset, yMax) controlPoint2:CGPointMake(xMin, yMax - radius + cpOffset)];//LB2
- [popoverPath closePath];
-
- //// General Declarations
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- //// Shadow Declarations
- UIColor* shadow = [UIColor colorWithWhite:0.0f alpha:kShadowAlpha];
- CGSize shadowOffset = CGSizeMake(0, 1);
- CGFloat shadowBlurRadius = kShadowBlur;
-
- //// Gradient Declarations
- NSArray* gradientColors = [NSArray arrayWithObjects:
- (id)kGradientTopColor.CGColor,
- (id)kGradientBottomColor.CGColor, nil];
- CGFloat gradientLocations[] = {0, 1};
- CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFTYPECAST(CFArrayRef)gradientColors), gradientLocations);
-
-
- //These floats are the top and bottom offsets for the gradient drawing so the drawing includes the arrows.
- float bottomOffset = (above ? kArrowHeight : 0.f);
- float topOffset = (!above ? kArrowHeight : 0.f);
-
- //Draw the actual gradient and shadow.
- CGContextSaveGState(context);
- CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor);
- CGContextBeginTransparencyLayer(context, NULL);
- [popoverPath addClip];
- CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) - topOffset), CGPointMake(CGRectGetMidX(frame), CGRectGetMaxY(frame) + bottomOffset), 0);
- CGContextEndTransparencyLayer(context);
- CGContextRestoreGState(context);
-
- //// Cleanup
- CGGradientRelease(gradient);
- CGColorSpaceRelease(colorSpace);
-
-
- //Draw the title background
- if (kDrawTitleGradient) {
- //Calculate the height of the title bg
- float titleBGHeight = -1;
-
- //NSLog(@"titleView:%@", titleView);
-
- if (titleView != nil) {
- titleBGHeight = kBoxPadding*2.f + titleView.frame.size.height;
- }
-
-
- //Draw the title bg height, but only if we need to.
- if (titleBGHeight > 0.f) {
- CGPoint startingPoint = CGPointMake(xMin, yMin + titleBGHeight);
- CGPoint endingPoint = CGPointMake(xMax, yMin + titleBGHeight);
-
- UIBezierPath *titleBGPath = [UIBezierPath bezierPath];
- [titleBGPath moveToPoint:startingPoint];
- [titleBGPath addLineToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + radius)];//LT1
- [titleBGPath addCurveToPoint:CGPointMake(xMin + radius, yMin) controlPoint1:CGPointMake(xMin, yMin + radius - cpOffset) controlPoint2:CGPointMake(xMin + radius - cpOffset, yMin)];//LT2
-
- //If the popover is positioned below (!above) the arrowPoint, then we know that the arrow must be on the top of the popover.
- //In this case, the arrow is located between LT2 and RT1
- if (!above) {
- [titleBGPath addLineToPoint:CGPointMake(arrowPoint.x - kArrowHeight, yMin)];//left side
- [titleBGPath addCurveToPoint:arrowPoint controlPoint1:CGPointMake(arrowPoint.x - kArrowHeight + kArrowCurvature, yMin) controlPoint2:arrowPoint];//actual arrow point
- [titleBGPath addCurveToPoint:CGPointMake(arrowPoint.x + kArrowHeight, yMin) controlPoint1:arrowPoint controlPoint2:CGPointMake(arrowPoint.x + kArrowHeight - kArrowCurvature, yMin)];//right side
- }
-
- [titleBGPath addLineToPoint:CGPointMake(xMax - radius, yMin)];//RT1
- [titleBGPath addCurveToPoint:CGPointMake(xMax, yMin + radius) controlPoint1:CGPointMake(xMax - radius + cpOffset, yMin) controlPoint2:CGPointMake(xMax, yMin + radius - cpOffset)];//RT2
- [titleBGPath addLineToPoint:endingPoint];
- [titleBGPath addLineToPoint:startingPoint];
- [titleBGPath closePath];
-
- //// General Declarations
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- //// Gradient Declarations
- NSArray* gradientColors = [NSArray arrayWithObjects:
- (id)kGradientTitleTopColor.CGColor,
- (id)kGradientTitleBottomColor.CGColor, nil];
- CGFloat gradientLocations[] = {0, 1};
- CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFTYPECAST(CFArrayRef)gradientColors), gradientLocations);
-
-
- //These floats are the top and bottom offsets for the gradient drawing so the drawing includes the arrows.
- float topOffset = (!above ? kArrowHeight : 0.f);
-
- //Draw the actual gradient and shadow.
- CGContextSaveGState(context);
- CGContextBeginTransparencyLayer(context, NULL);
- [titleBGPath addClip];
- CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) - topOffset), CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame) + titleBGHeight), 0);
- CGContextEndTransparencyLayer(context);
- CGContextRestoreGState(context);
-
- UIBezierPath *dividerLine = [UIBezierPath bezierPathWithRect:CGRectMake(startingPoint.x, startingPoint.y, (endingPoint.x - startingPoint.x), 0.5f)];
- [[UIColor colorWithRed:0.741 green:0.741 blue:0.741 alpha:0.5f] setFill];
- [dividerLine fill];
-
- //// Cleanup
- CGGradientRelease(gradient);
- CGColorSpaceRelease(colorSpace);
- }
- }
-
-
-
- //Draw the divider rects if we need to
- {
- if (kShowDividersBetweenViews && showDividerRects) {
- if (dividerRects && dividerRects.count > 0) {
- for (NSValue *value in dividerRects) {
- CGRect rect = value.CGRectValue;
- rect.origin.x += contentView.frame.origin.x;
- rect.origin.y += contentView.frame.origin.y;
-
- UIBezierPath *dividerPath = [UIBezierPath bezierPathWithRect:rect];
- [kDividerColor setFill];
- [dividerPath fill];
- }
- }
- }
- }
-
- //Draw border if we need to
- //The border is done last because it needs to be drawn on top of everything else
- if (kDrawBorder) {
- [kBorderColor setStroke];
- popoverPath.lineWidth = kBorderWidth;
- [popoverPath stroke];
- }
-
- }
- @end
|