// // WEPopoverController.m // WEPopover // // Created by Werner Altewischer on 02/09/10. // Copyright 2010 Werner IT Consultancy. All rights reserved. // #import "WEPopoverController.h" #import "WEPopoverParentView.h" #import "UIBarButtonItem+WEPopover.h" #define FADE_DURATION 0.3 @interface WEPopoverController(Private) - (UIView *)keyView; - (void)updateBackgroundPassthroughViews; - (void)setView:(UIView *)v; - (CGRect)displayAreaForView:(UIView *)theView; - (WEPopoverContainerViewProperties *)defaultContainerViewProperties; - (void)dismissPopoverAnimated:(BOOL)animated userInitiated:(BOOL)userInitiated; @end @implementation WEPopoverController @synthesize contentViewController; @synthesize popoverContentSize; @synthesize popoverVisible; @synthesize popoverArrowDirection; @synthesize delegate; @synthesize view; @synthesize parentView; @synthesize containerViewProperties; @synthesize context; @synthesize passthroughViews; - (id)init { if ((self = [super init])) { } return self; } - (id)initWithContentViewController:(UIViewController *)viewController { if ((self = [self init])) { self.contentViewController = viewController; } return self; } - (void)dealloc { [self dismissPopoverAnimated:NO]; [contentViewController release]; [containerViewProperties release]; [passthroughViews release]; self.context = nil; [super dealloc]; } - (void)setContentViewController:(UIViewController *)vc { if (vc != contentViewController) { [contentViewController release]; contentViewController = [vc retain]; popoverContentSize = CGSizeZero; } } - (BOOL)forwardAppearanceMethods { return YES; return ![contentViewController respondsToSelector:@selector(shouldAutomaticallyForwardAppearanceMethods)]; } //Overridden setter to copy the passthroughViews to the background view if it exists already - (void)setPassthroughViews:(NSArray *)array { [passthroughViews release]; passthroughViews = nil; if (array) { passthroughViews = [[NSArray alloc] initWithArray:array]; } [self updateBackgroundPassthroughViews]; } - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)theContext { if ([animationID isEqual:@"FadeIn"]) { self.view.userInteractionEnabled = YES; popoverVisible = YES; if ([self forwardAppearanceMethods]) { [contentViewController viewDidAppear:YES]; } } else if ([animationID isEqual:@"FadeOut"]) { popoverVisible = NO; if ([self forwardAppearanceMethods]) { [contentViewController viewDidDisappear:YES]; } [self.view removeFromSuperview]; self.view = nil; [backgroundView removeFromSuperview]; [backgroundView release]; backgroundView = nil; BOOL userInitiatedDismissal = [(NSNumber *)theContext boolValue]; if (userInitiatedDismissal) { //Only send message to delegate in case the user initiated this event, which is if he touched outside the view [delegate popoverControllerDidDismissPopover:self]; } } } - (void)dismissPopoverAnimated:(BOOL)animated { [self dismissPopoverAnimated:animated userInitiated:NO]; } - (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated { UIView *v = [self keyView]; CGRect rect = [item frameInView:v]; return [self presentPopoverFromRect:rect inView:v permittedArrowDirections:arrowDirections animated:animated]; } - (void)presentPopoverFromRect:(CGRect)rect inView:(UIView *)theView permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated { [self dismissPopoverAnimated:NO]; //First force a load view for the contentViewController so the popoverContentSize is properly initialized [contentViewController view]; if (CGSizeEqualToSize(popoverContentSize, CGSizeZero)) { popoverContentSize = contentViewController.contentSizeForViewInPopover; } CGRect displayArea = [self displayAreaForView:theView]; WEPopoverContainerViewProperties *props = self.containerViewProperties ? self.containerViewProperties : [self defaultContainerViewProperties]; WEPopoverContainerView *containerView = [[[WEPopoverContainerView alloc] initWithSize:self.popoverContentSize anchorRect:rect displayArea:displayArea permittedArrowDirections:arrowDirections properties:props] autorelease]; popoverArrowDirection = containerView.arrowDirection; UIView *keyView = self.keyView; backgroundView = [[WETouchableView alloc] initWithFrame:keyView.bounds]; backgroundView.contentMode = UIViewContentModeScaleToFill; backgroundView.autoresizingMask = ( UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin); backgroundView.backgroundColor = [UIColor clearColor]; backgroundView.delegate = self; [keyView addSubview:backgroundView]; containerView.frame = [theView convertRect:containerView.frame toView:backgroundView]; [backgroundView addSubview:containerView]; containerView.contentView = contentViewController.view; containerView.autoresizingMask = ( UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin); self.view = containerView; [self updateBackgroundPassthroughViews]; if ([self forwardAppearanceMethods]) { [contentViewController viewWillAppear:animated]; } [self.view becomeFirstResponder]; popoverVisible = YES; if (animated) { self.view.alpha = 0.0; [UIView animateWithDuration:FADE_DURATION delay:0.0 options:UIViewAnimationCurveLinear animations:^{ self.view.alpha = 1.0; } completion:^(BOOL finished) { [self animationDidStop:@"FadeIn" finished:[NSNumber numberWithBool:finished] context:nil]; }]; } else { if ([self forwardAppearanceMethods]) { [contentViewController viewDidAppear:animated]; } } } - (void)repositionPopoverFromRect:(CGRect)rect inView:(UIView *)theView permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections { [self repositionPopoverFromRect:rect inView:theView permittedArrowDirections:arrowDirections animated:NO]; } - (void)repositionPopoverFromRect:(CGRect)rect inView:(UIView *)theView permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated { if (animated) { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:FADE_DURATION]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; } if (CGSizeEqualToSize(popoverContentSize, CGSizeZero)) { popoverContentSize = contentViewController.contentSizeForViewInPopover; } CGRect displayArea = [self displayAreaForView:theView]; WEPopoverContainerView *containerView = (WEPopoverContainerView *)self.view; [containerView updatePositionWithSize:self.popoverContentSize anchorRect:rect displayArea:displayArea permittedArrowDirections:arrowDirections]; popoverArrowDirection = containerView.arrowDirection; containerView.frame = [theView convertRect:containerView.frame toView:backgroundView]; if (animated) { [UIView commitAnimations]; } } #pragma mark - #pragma mark WETouchableViewDelegate implementation - (void)viewWasTouched:(WETouchableView *)view { if (popoverVisible) { if (!delegate || [delegate popoverControllerShouldDismissPopover:self]) { [self dismissPopoverAnimated:YES userInitiated:YES]; } } } - (BOOL)isPopoverVisible { if (!popoverVisible) { return NO; } UIView *sv = self.view; BOOL foundWindowAsSuperView = NO; while ((sv = sv.superview) != nil) { if ([sv isKindOfClass:[UIWindow class]]) { foundWindowAsSuperView = YES; break; } } return foundWindowAsSuperView; } @end @implementation WEPopoverController(Private) - (UIView *)keyView { if (self.parentView) { return self.parentView; } else { UIWindow *w = [[UIApplication sharedApplication] keyWindow]; if (w.subviews.count > 0) { return [w.subviews objectAtIndex:0]; } else { return w; } } } - (void)setView:(UIView *)v { if (view != v) { [view release]; view = [v retain]; } } - (void)updateBackgroundPassthroughViews { backgroundView.passthroughViews = passthroughViews; } - (void)dismissPopoverAnimated:(BOOL)animated userInitiated:(BOOL)userInitiated { if (self.view) { if ([self forwardAppearanceMethods]) { [contentViewController viewWillDisappear:animated]; } popoverVisible = NO; [self.view resignFirstResponder]; if (animated) { self.view.userInteractionEnabled = NO; [UIView animateWithDuration:FADE_DURATION delay:0.0 options:UIViewAnimationCurveLinear animations:^{ self.view.alpha = 0.0; } completion:^(BOOL finished) { [self animationDidStop:@"FadeOut" finished:[NSNumber numberWithBool:finished] context:[NSNumber numberWithBool:userInitiated]]; }]; } else { if ([self forwardAppearanceMethods]) { [contentViewController viewDidDisappear:animated]; } [self.view removeFromSuperview]; self.view = nil; [backgroundView removeFromSuperview]; [backgroundView release]; backgroundView = nil; } } } - (CGRect)displayAreaForView:(UIView *)theView { CGRect displayArea = CGRectZero; if ([theView conformsToProtocol:@protocol(WEPopoverParentView)] && [theView respondsToSelector:@selector(displayAreaForPopover)]) { displayArea = [(id )theView displayAreaForPopover]; } else { UIView *keyView = [self keyView]; displayArea = [keyView convertRect:keyView.bounds toView:theView]; } return displayArea; } //Enable to use the simple popover style - (WEPopoverContainerViewProperties *)defaultContainerViewProperties { WEPopoverContainerViewProperties *ret = [[WEPopoverContainerViewProperties new] autorelease]; CGSize imageSize = CGSizeMake(30.0f, 30.0f); NSString *bgImageName = @"popoverBgSimple.png"; CGFloat bgMargin = 6.0; CGFloat contentMargin = 2.0; ret.leftBgMargin = bgMargin; ret.rightBgMargin = bgMargin; ret.topBgMargin = bgMargin; ret.bottomBgMargin = bgMargin; ret.leftBgCapSize = imageSize.width/2; ret.topBgCapSize = imageSize.height/2; ret.bgImageName = bgImageName; ret.leftContentMargin = contentMargin; ret.rightContentMargin = contentMargin; ret.topContentMargin = contentMargin; ret.bottomContentMargin = contentMargin; ret.arrowMargin = 1.0; ret.upArrowImageName = @"popoverArrowUpSimple.png"; ret.downArrowImageName = @"popoverArrowDownSimple.png"; ret.leftArrowImageName = @"popoverArrowLeftSimple.png"; ret.rightArrowImageName = @"popoverArrowRightSimple.png"; return ret; } @end