Project import
diff --git a/WEPopover/LICENSE b/WEPopover/LICENSE new file mode 100755 index 0000000..163d133 --- /dev/null +++ b/WEPopover/LICENSE
@@ -0,0 +1,19 @@ +Copyright (c) 2010 Werner IT Consultancy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
diff --git a/WEPopover/UIBarButtonItem+WEPopover.h b/WEPopover/UIBarButtonItem+WEPopover.h new file mode 100755 index 0000000..0b8391a --- /dev/null +++ b/WEPopover/UIBarButtonItem+WEPopover.h
@@ -0,0 +1,15 @@ +/* + * UIBarButtonItem+WEPopover.h + * WEPopover + * + * Created by Werner Altewischer on 07/05/11. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +@interface UIBarButtonItem(WEPopover) + +- (CGRect)frameInView:(UIView *)v; +- (UIView *)superview; + +@end
diff --git a/WEPopover/UIBarButtonItem+WEPopover.m b/WEPopover/UIBarButtonItem+WEPopover.m new file mode 100755 index 0000000..8b33330 --- /dev/null +++ b/WEPopover/UIBarButtonItem+WEPopover.m
@@ -0,0 +1,52 @@ +/* + * UIBarButtonItem+WEPopover.m + * WEPopover + * + * Created by Werner Altewischer on 07/05/11. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +#import "UIBarButtonItem+WEPopover.h" + +@implementation UIBarButtonItem(WEPopover) + +- (CGRect)frameInView:(UIView *)v { + + BOOL hasCustomView = (self.customView != nil); + + if (!hasCustomView) { + UIView *tempView = [[UIView alloc] initWithFrame:CGRectZero]; + self.customView = tempView; + [tempView release]; + } + + UIView *parentView = self.customView.superview; + NSUInteger indexOfView = [parentView.subviews indexOfObject:self.customView]; + + if (!hasCustomView) { + self.customView = nil; + } + UIView *button = [parentView.subviews objectAtIndex:indexOfView]; + return [parentView convertRect:button.frame toView:v]; +} + +- (UIView *)superview { + + BOOL hasCustomView = (self.customView != nil); + + if (!hasCustomView) { + UIView *tempView = [[UIView alloc] initWithFrame:CGRectZero]; + self.customView = tempView; + [tempView release]; + } + + UIView *parentView = self.customView.superview; + + if (!hasCustomView) { + self.customView = nil; + } + return parentView; +} + +@end
diff --git a/WEPopover/WEPopoverContainerView.h b/WEPopover/WEPopoverContainerView.h new file mode 100755 index 0000000..0aeacd9 --- /dev/null +++ b/WEPopover/WEPopoverContainerView.h
@@ -0,0 +1,98 @@ +// +// WEPopoverContainerView.h +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import <Foundation/Foundation.h> + +/** + * @brief Properties for the container view determining the area where the actual content view can/may be displayed. Also Images can be supplied for the arrow images and background. + */ +@interface WEPopoverContainerViewProperties : NSObject +{ + NSString *bgImageName; + NSString *upArrowImageName; + NSString *downArrowImageName; + NSString *leftArrowImageName; + NSString *rightArrowImageName; + CGFloat leftBgMargin; + CGFloat rightBgMargin; + CGFloat topBgMargin; + CGFloat bottomBgMargin; + NSInteger topBgCapSize; + NSInteger leftBgCapSize; + CGFloat arrowMargin; +} + +@property(nonatomic, retain) NSString *bgImageName; +@property(nonatomic, retain) NSString *upArrowImageName; +@property(nonatomic, retain) NSString *downArrowImageName; +@property(nonatomic, retain) NSString *leftArrowImageName; +@property(nonatomic, retain) NSString *rightArrowImageName; +@property(nonatomic, assign) CGFloat leftBgMargin; +@property(nonatomic, assign) CGFloat rightBgMargin; +@property(nonatomic, assign) CGFloat topBgMargin; +@property(nonatomic, assign) CGFloat bottomBgMargin; +@property(nonatomic, assign) CGFloat leftContentMargin; +@property(nonatomic, assign) CGFloat rightContentMargin; +@property(nonatomic, assign) CGFloat topContentMargin; +@property(nonatomic, assign) CGFloat bottomContentMargin; +@property(nonatomic, assign) NSInteger topBgCapSize; +@property(nonatomic, assign) NSInteger leftBgCapSize; +@property(nonatomic, assign) CGFloat arrowMargin; + +@end + +@class WEPopoverContainerView; + +/** + * @brief Container/background view for displaying a popover view. + */ +@interface WEPopoverContainerView : UIView { + UIImage *bgImage; + UIImage *arrowImage; + + WEPopoverContainerViewProperties *properties; + + UIPopoverArrowDirection arrowDirection; + + CGRect arrowRect; + CGRect bgRect; + CGPoint offset; + CGPoint arrowOffset; + + CGSize correctedSize; + UIView *contentView; +} + +/** + * @brief The current arrow direction for the popover. + */ +@property (nonatomic, readonly) UIPopoverArrowDirection arrowDirection; + +/** + * @brief The content view being displayed. + */ +@property (nonatomic, retain) UIView *contentView; + +/** + * @brief Initializes the position of the popover with a size, anchor rect, display area and permitted arrow directions and optionally the properties. + * If the last is not supplied the defaults are taken (requires images to be present in bundle representing a black rounded background with partial transparency). + */ +- (id)initWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea +permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections + properties:(WEPopoverContainerViewProperties *)properties; + +/** + * @brief To update the position of the popover with a new anchor rect, display area and permitted arrow directions + */ +- (void)updatePositionWithAnchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea + permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections; + +@end
diff --git a/WEPopover/WEPopoverContainerView.m b/WEPopover/WEPopoverContainerView.m new file mode 100755 index 0000000..838e935 --- /dev/null +++ b/WEPopover/WEPopoverContainerView.m
@@ -0,0 +1,221 @@ +// +// WEPopoverContainerViewProperties.m +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import "WEPopoverContainerView.h" + +@implementation WEPopoverContainerViewProperties + +@synthesize bgImageName, upArrowImageName, downArrowImageName, leftArrowImageName, rightArrowImageName, topBgMargin, bottomBgMargin, leftBgMargin, rightBgMargin, topBgCapSize, leftBgCapSize; +@synthesize leftContentMargin, rightContentMargin, topContentMargin, bottomContentMargin, arrowMargin; + +- (void)dealloc { + self.bgImageName = nil; + self.upArrowImageName = nil; + self.downArrowImageName = nil; + self.leftArrowImageName = nil; + self.rightArrowImageName = nil; + [super dealloc]; +} + +@end + +@interface WEPopoverContainerView(Private) + +- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections; +- (CGRect)contentRect; +- (CGSize)contentSize; +- (void)setProperties:(WEPopoverContainerViewProperties *)props; +- (void)initFrame; + +@end + +@implementation WEPopoverContainerView + +@synthesize arrowDirection, contentView; + +- (id)initWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea +permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections + properties:(WEPopoverContainerViewProperties *)theProperties { + if ((self = [super initWithFrame:CGRectZero])) { + + [self setProperties:theProperties]; + correctedSize = CGSizeMake(theSize.width + properties.leftBgMargin + properties.rightBgMargin + properties.leftContentMargin + properties.rightContentMargin, + theSize.height + properties.topBgMargin + properties.bottomBgMargin + properties.topContentMargin + properties.bottomContentMargin); + [self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections]; + [self initFrame]; + self.backgroundColor = [UIColor clearColor]; + UIImage *theImage = [UIImage imageNamed:properties.bgImageName]; + bgImage = [[theImage stretchableImageWithLeftCapWidth:properties.leftBgCapSize topCapHeight:properties.topBgCapSize] retain]; + + self.clipsToBounds = YES; + self.userInteractionEnabled = YES; + + self.layer.shadowOffset = CGSizeMake(3, 3); + self.layer.shadowRadius = 6; + self.layer.shadowOpacity = 0.3; + self.clipsToBounds = NO; + } + return self; +} + +- (void)dealloc { + [properties release]; + [contentView release]; + [bgImage release]; + [arrowImage release]; + [super dealloc]; +} + +- (void)drawRect:(CGRect)rect { + [bgImage drawInRect:bgRect blendMode:kCGBlendModeNormal alpha:1.0]; + [arrowImage drawInRect:arrowRect blendMode:kCGBlendModeNormal alpha:1.0]; +} + +- (void)updatePositionWithAnchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea + permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections { + [self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections]; + [self initFrame]; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + return CGRectContainsPoint(self.contentRect, point); +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)setContentView:(UIView *)v { + if (v != contentView) { + [contentView release]; + contentView = [v retain]; + contentView.frame = self.contentRect; + [self addSubview:contentView]; + } +} + + + +@end + +@implementation WEPopoverContainerView(Private) + +- (void)initFrame { + CGRect theFrame = CGRectOffset(CGRectUnion(bgRect, arrowRect), offset.x, offset.y); + + //If arrow rect origin is < 0 the frame above is extended to include it so we should offset the other rects + arrowOffset = CGPointMake(MAX(0, -arrowRect.origin.x), MAX(0, -arrowRect.origin.y)); + bgRect = CGRectOffset(bgRect, arrowOffset.x, arrowOffset.y); + arrowRect = CGRectOffset(arrowRect, arrowOffset.x, arrowOffset.y); + + self.frame = theFrame; +} + +- (CGSize)contentSize { + return self.contentRect.size; +} + +- (CGRect)contentRect { + CGRect rect = CGRectMake(properties.leftBgMargin + properties.leftContentMargin + arrowOffset.x, + properties.topBgMargin + properties.topContentMargin + arrowOffset.y, + bgRect.size.width - properties.leftBgMargin - properties.rightBgMargin - properties.leftContentMargin - properties.rightContentMargin, + bgRect.size.height - properties.topBgMargin - properties.bottomBgMargin - properties.topContentMargin - properties.bottomContentMargin); + return rect; +} + +- (void)setProperties:(WEPopoverContainerViewProperties *)props { + if (properties != props) { + [properties release]; + properties = [props retain]; + } +} + +// NOTE: this method is completely rewritten by Nest Labs to do layout that meets our UI needs. +// It's kinda shoehorned in so we didn't need to modify other WEPopover code. +// It's not a general solution, does not obey supportedArrowDirections, and has bugs. +// Sorry. +- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)supportedArrowDirections { + UIImage *downArrowImage = [UIImage imageNamed:properties.downArrowImageName]; + UIImage *leftArrowImage = [UIImage imageNamed:properties.leftArrowImageName]; + UIImage *rightArrowImage = [UIImage imageNamed:properties.rightArrowImageName]; + + // try displaying above anchor + float topFreeSpace = (anchorRect.origin.y - (theSize.height + downArrowImage.size.height)) - displayArea.origin.y; + float leftFreeSpace = CGRectGetMidX(anchorRect) - CGRectGetMinX(displayArea); + float rightFreeSpace = CGRectGetMaxX(displayArea) - CGRectGetMidX(anchorRect); + if ((topFreeSpace >= 0) && (leftFreeSpace >= (rightArrowImage.size.width / 2) + 16) && (rightFreeSpace >= (leftArrowImage.size.width / 2) + 16)) { + bgRect = CGRectMake(0, 0, theSize.width, theSize.height); + arrowRect = CGRectMake((theSize.width - downArrowImage.size.width) / 2, theSize.height, downArrowImage.size.width, downArrowImage.size.height); + offset = CGPointMake(CGRectGetMidX(anchorRect) - (bgRect.size.width / 2), -(arrowRect.origin.y + arrowRect.size.height - anchorRect.origin.y)); + arrowDirection = UIPopoverArrowDirectionDown; + arrowImage = [downArrowImage retain]; + + // ...check for going off right edge + float overage = (bgRect.size.width + offset.x) - (displayArea.origin.x + displayArea.size.width); + if (overage > 0) { + arrowRect = CGRectOffset(arrowRect, overage, 0); + offset.x -= overage; + } + + // ...check for going off left edge + float underage = offset.x - displayArea.origin.x; + if (underage < 0) { + arrowRect = CGRectOffset(arrowRect, underage, 0); + offset.x -= underage; + } + } else { + // try left or right side + if (leftFreeSpace > rightFreeSpace) { + // ...to left + bgRect = CGRectMake(0, 0, theSize.width, theSize.height); + arrowRect = CGRectMake(theSize.width, (theSize.height - rightArrowImage.size.height) / 2, rightArrowImage.size.width, rightArrowImage.size.height); + offset = CGPointMake(anchorRect.origin.x - theSize.width - rightArrowImage.size.width, -((theSize.height - anchorRect.size.height) / 2) + anchorRect.origin.y); + arrowDirection = UIPopoverArrowDirectionRight; + arrowImage = [rightArrowImage retain]; + } else { + // ...to right + bgRect = CGRectMake(0, 0, theSize.width, theSize.height); + arrowRect = CGRectMake(-leftArrowImage.size.width, (theSize.height - leftArrowImage.size.height) / 2, leftArrowImage.size.width, leftArrowImage.size.height); + offset = CGPointMake(anchorRect.origin.x + anchorRect.size.width + leftArrowImage.size.width, -((theSize.height - anchorRect.size.height) / 2) + anchorRect.origin.y); + arrowDirection = UIPopoverArrowDirectionLeft; + arrowImage = [leftArrowImage retain]; + } + + // ...check for going off bottom + float overage = (CGRectGetMaxY(bgRect) + offset.y) - CGRectGetMaxY(displayArea); + if (overage > 0) { + overage = MIN(overage, CGRectGetMaxY(bgRect) - CGRectGetMaxY(arrowRect) - 12); + arrowRect = CGRectOffset(arrowRect, 0, overage); + offset.y -= overage; + } + + // ...check for going off top + float underage = (CGRectGetMinY(bgRect) + offset.y) - CGRectGetMinY(displayArea); + if (underage < 0) { + arrowRect = CGRectOffset(arrowRect, 0, underage); + offset.y -= underage; + } + } +} + +@end \ No newline at end of file
diff --git a/WEPopover/WEPopoverController.h b/WEPopover/WEPopoverController.h new file mode 100755 index 0000000..324c869 --- /dev/null +++ b/WEPopover/WEPopoverController.h
@@ -0,0 +1,67 @@ +// +// WEPopoverController.h +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "WEPopoverContainerView.h" +#import "WETouchableView.h" + +@class WEPopoverController; + +@protocol WEPopoverControllerDelegate<NSObject> + +- (void)popoverControllerDidDismissPopover:(WEPopoverController *)popoverController; +- (BOOL)popoverControllerShouldDismissPopover:(WEPopoverController *)popoverController; + +@end + +/** + * @brief Popover controller for the iPhone, mimicing the iPad UIPopoverController interface. See that class for more details. + */ +@interface WEPopoverController : NSObject<WETouchableViewDelegate> { + UIViewController *contentViewController; + UIView *view; + WETouchableView *backgroundView; + + BOOL popoverVisible; + UIPopoverArrowDirection popoverArrowDirection; + id <WEPopoverControllerDelegate> delegate; + CGSize popoverContentSize; + WEPopoverContainerViewProperties *containerViewProperties; + id <NSObject> context; + NSArray *passthroughViews; +} + +@property(nonatomic, retain) UIViewController *contentViewController; + +@property (nonatomic, readonly) UIView *view; +@property (nonatomic, readonly, getter=isPopoverVisible) BOOL popoverVisible; +@property (nonatomic, readonly) UIPopoverArrowDirection popoverArrowDirection; +@property (nonatomic, assign) id <WEPopoverControllerDelegate> delegate; +@property (nonatomic, assign) CGSize popoverContentSize; +@property (nonatomic, retain) WEPopoverContainerViewProperties *containerViewProperties; +@property (nonatomic, retain) id <NSObject> context; +@property (nonatomic, copy) NSArray *passthroughViews; + +- (id)initWithContentViewController:(UIViewController *)theContentViewController; + +- (void)dismissPopoverAnimated:(BOOL)animated; + +- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated; + +- (void)presentPopoverFromRect:(CGRect)rect + inView:(UIView *)view + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated; + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)view + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections; + +@end
diff --git a/WEPopover/WEPopoverController.m b/WEPopover/WEPopoverController.m new file mode 100755 index 0000000..d749fed --- /dev/null +++ b/WEPopover/WEPopoverController.m
@@ -0,0 +1,288 @@ +// +// 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 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; + } +} + +//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)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 + // NEST: edit to avoid warning + if (contentViewController.view == nil) return; + + 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]; + + [contentViewController viewWillAppear:animated]; + + [self.view becomeFirstResponder]; + + if (animated) { + self.view.alpha = 0.0; + + [UIView animateWithDuration:FADE_DURATION animations:^{ + self.view.alpha = 1.0; + } completion:^(BOOL finished) { + self.view.userInteractionEnabled = YES; + popoverVisible = YES; + [contentViewController viewDidAppear:YES]; + }]; + } else { + popoverVisible = YES; + [contentViewController viewDidAppear:animated]; + } +} + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)theView + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections { + + CGRect displayArea = [self displayAreaForView:theView]; + WEPopoverContainerView *containerView = (WEPopoverContainerView *)self.view; + [containerView updatePositionWithAnchorRect:rect + displayArea:displayArea + permittedArrowDirections:arrowDirections]; + + popoverArrowDirection = containerView.arrowDirection; + containerView.frame = [theView convertRect:containerView.frame toView:backgroundView]; +} + +#pragma mark - +#pragma mark WETouchableViewDelegate implementation + +- (void)viewWasTouched:(WETouchableView *)view { + if (popoverVisible) { + if (!delegate || [delegate popoverControllerShouldDismissPopover:self]) { + [self dismissPopoverAnimated:YES userInitiated:YES]; + } + } +} + +@end + + +@implementation WEPopoverController(Private) + +- (UIView *)keyView { + 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) { + [contentViewController viewWillDisappear:animated]; + popoverVisible = NO; + [self.view resignFirstResponder]; + if (animated) { + + self.view.userInteractionEnabled = NO; + [UIView animateWithDuration:FADE_DURATION animations:^{ + self.view.alpha = 0.0; + } completion:^(BOOL finished) { + popoverVisible = NO; + [contentViewController viewDidDisappear:YES]; + [self.view removeFromSuperview]; + self.view = nil; + [backgroundView removeFromSuperview]; + [backgroundView release]; + backgroundView = nil; + + if (userInitiated) { + //Only send message to delegate in case the user initiated this event, which is if he touched outside the view + [delegate popoverControllerDidDismissPopover:self]; + } + }]; + } else { + [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 <WEPopoverParentView>)theView displayAreaForPopover]; + } else { + displayArea = [[[UIApplication sharedApplication] keyWindow] convertRect:[[UIScreen mainScreen] applicationFrame] toView:theView]; + displayArea = CGRectInset(displayArea, 6, 6); // give some margin from the edges of the screen + } + 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 = 0.0; + CGFloat contentMargin = 12.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
diff --git a/WEPopover/WEPopoverParentView.h b/WEPopover/WEPopoverParentView.h new file mode 100755 index 0000000..111f8fa --- /dev/null +++ b/WEPopover/WEPopoverParentView.h
@@ -0,0 +1,15 @@ +/* + * WEPopoverParentView.h + * WEPopover + * + * Created by Werner Altewischer on 02/09/10. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +@protocol WEPopoverParentView + +@optional +- (CGRect)displayAreaForPopover; + +@end \ No newline at end of file
diff --git a/WEPopover/WETouchableView.h b/WEPopover/WETouchableView.h new file mode 100755 index 0000000..b260812 --- /dev/null +++ b/WEPopover/WETouchableView.h
@@ -0,0 +1,36 @@ +// +// WETouchableView.h +// WEPopover +// +// Created by Werner Altewischer on 12/21/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@class WETouchableView; + +/** + * @brief delegate to receive touch events + */ +@protocol WETouchableViewDelegate<NSObject> + +- (void)viewWasTouched:(WETouchableView *)view; + +@end + +/** + * @brief View that can handle touch events and/or disable touch forwording to child views + */ +@interface WETouchableView : UIView { + BOOL touchForwardingDisabled; + id <WETouchableViewDelegate> delegate; + NSArray *passthroughViews; + BOOL testHits; +} + +@property (nonatomic, assign) BOOL touchForwardingDisabled; +@property (nonatomic, assign) id <WETouchableViewDelegate> delegate; +@property (nonatomic, copy) NSArray *passthroughViews; + +@end
diff --git a/WEPopover/WETouchableView.m b/WEPopover/WETouchableView.m new file mode 100755 index 0000000..e04fc9d --- /dev/null +++ b/WEPopover/WETouchableView.m
@@ -0,0 +1,70 @@ +// +// WETouchableView.m +// WEPopover +// +// Created by Werner Altewischer on 12/21/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import "WETouchableView.h" + +@interface WETouchableView(Private) + +- (BOOL)isPassthroughView:(UIView *)v; + +@end + +@implementation WETouchableView + +@synthesize touchForwardingDisabled, delegate, passthroughViews; + +- (void)dealloc { + [passthroughViews release]; + [super dealloc]; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + if (testHits) { + return nil; + } else if (touchForwardingDisabled) { + return self; + } else { + UIView *hitView = [super hitTest:point withEvent:event]; + + if (hitView == self) { + //Test whether any of the passthrough views would handle this touch + testHits = YES; + UIView *superHitView = [self.superview hitTest:point withEvent:event]; + testHits = NO; + + if ([self isPassthroughView:superHitView]) { + hitView = superHitView; + } + } + + return hitView; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.delegate viewWasTouched:self]; +} + +@end + +@implementation WETouchableView(Private) + +- (BOOL)isPassthroughView:(UIView *)v { + + if (v == nil) { + return NO; + } + + if ([passthroughViews containsObject:v]) { + return YES; + } + + return [self isPassthroughView:v.superview]; +} + +@end