| // |
| // WEPopoverContainerViewProperties.m |
| // WEPopover |
| // |
| // Created by Werner Altewischer on 02/09/10. |
| // Copyright 2010 Werner IT Consultancy. All rights reserved. |
| // |
| |
| static const CGFloat NLPopoverAlpha = 0.96f; |
| static const CGFloat ArrowDropShadowOffset = 1.0f; |
| |
| #import "WEPopoverContainerView.h" |
| |
| @implementation WEPopoverContainerViewProperties |
| |
| @synthesize bgImageName, upArrowImageName, downArrowImageName, leftArrowImageName, rightArrowImageName, topBgMargin, bottomBgMargin, leftBgMargin, rightBgMargin, topBgCapSize, leftBgCapSize; |
| @synthesize leftContentMargin, rightContentMargin, topContentMargin, bottomContentMargin, arrowMargin; |
| |
| |
| @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]; |
| |
| self.clipsToBounds = YES; |
| self.userInteractionEnabled = YES; |
| |
| self.layer.shadowOffset = CGSizeMake(3, 3); |
| self.layer.shadowRadius = 6; |
| self.layer.shadowOpacity = 0.3f; |
| self.clipsToBounds = NO; |
| } |
| return self; |
| } |
| |
| |
| - (void)drawRect:(CGRect)rect { |
| [bgImage drawInRect:bgRect blendMode:kCGBlendModeNormal alpha:NLPopoverAlpha]; |
| [arrowImage drawInRect:arrowRect blendMode:kCGBlendModeNormal alpha:NLPopoverAlpha]; |
| } |
| |
| - (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 = v; |
| 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 = props; |
| } |
| } |
| |
| // 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]; |
| UIImage *upArrowImage = [UIImage imageNamed:properties.upArrowImageName]; |
| |
| // try displaying above anchor |
| CGFloat topFreeSpace = (anchorRect.origin.y - (theSize.height + downArrowImage.size.height)) - displayArea.origin.y; |
| CGFloat leftFreeSpace = CGRectGetMidX(anchorRect) - CGRectGetMinX(displayArea); |
| CGFloat 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); |
| // Substarct 1 from actual top to overlap arrow image and background image. |
| arrowRect = CGRectMake((theSize.width - downArrowImage.size.width) / 2, (theSize.height - ArrowDropShadowOffset), 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; |
| |
| // ...check for going off right edge |
| CGFloat 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 |
| CGFloat underage = offset.x - displayArea.origin.x; |
| if (underage < 0) { |
| arrowRect = CGRectOffset(arrowRect, underage, 0); |
| offset.x -= underage; |
| } |
| } else if (((leftFreeSpace - (theSize.width + rightArrowImage.size.width)) > 0) || |
| ((rightFreeSpace - (theSize.width + leftArrowImage.size.width)) > 0)) { |
| |
| // try left or right side |
| if (leftFreeSpace > rightFreeSpace) { |
| // ...to left |
| bgRect = CGRectMake(0, 0, theSize.width, theSize.height); |
| // Substarct 1 from actual left to overlap arrow image and background image. |
| arrowRect = CGRectMake(theSize.width - ArrowDropShadowOffset, (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; |
| } else { |
| // ...to right |
| bgRect = CGRectMake(0, 0, theSize.width, theSize.height); |
| // Add 1 to actual left to overlap arrow image and background image. |
| arrowRect = CGRectMake(-leftArrowImage.size.width + ArrowDropShadowOffset, (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; |
| } |
| |
| // ...check for going off bottom |
| CGFloat 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 |
| CGFloat underage = (CGRectGetMinY(bgRect) + offset.y) - CGRectGetMinY(displayArea); |
| if (underage < 0) { |
| arrowRect = CGRectOffset(arrowRect, 0, underage); |
| offset.y -= underage; |
| } |
| } else { |
| bgRect = CGRectMake(0, 0, theSize.width, theSize.height); |
| // Add 1 to actual top to overlap arrow image and background image. |
| arrowRect = CGRectMake((theSize.width - upArrowImage.size.width) / 2, - (upArrowImage.size.height - 1), upArrowImage.size.width, upArrowImage.size.height); |
| offset = CGPointMake(CGRectGetMidX(anchorRect) - (bgRect.size.width / 2), CGRectGetMaxY(anchorRect) + upArrowImage.size.height); |
| |
| arrowDirection = UIPopoverArrowDirectionUp; |
| arrowImage = upArrowImage; |
| |
| // ...check for going off right edge |
| CGFloat 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 |
| CGFloat underage = offset.x - displayArea.origin.x; |
| if (underage < 0) { |
| arrowRect = CGRectOffset(arrowRect, underage, 0); |
| offset.x -= underage; |
| } |
| } |
| } |
| |
| @end |