NewsBlur-viq/clients/ios/Popover/WEPopoverContainerView.m
2013-06-27 09:43:22 -07:00

366 lines
No EOL
14 KiB
Objective-C
Executable file

//
// 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;
}
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)updatePositionWithSize:(CGSize)theSize
anchorRect:(CGRect)anchorRect
displayArea:(CGRect)displayArea
permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
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 setNeedsDisplay];
}
- (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 = CGRectIntegral(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];
}
}
- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)supportedArrowDirections {
//Determine the frame, it should not go outside the display area
UIPopoverArrowDirection theArrowDirection = UIPopoverArrowDirectionUp;
offset = CGPointZero;
bgRect = CGRectZero;
arrowRect = CGRectZero;
arrowDirection = UIPopoverArrowDirectionUnknown;
CGFloat biggestSurface = 0.0f;
CGFloat currentMinMargin = 0.0f;
UIImage *upArrowImage = [UIImage imageNamed:properties.upArrowImageName];
UIImage *downArrowImage = [UIImage imageNamed:properties.downArrowImageName];
UIImage *leftArrowImage = [UIImage imageNamed:properties.leftArrowImageName];
UIImage *rightArrowImage = [UIImage imageNamed:properties.rightArrowImageName];
while (theArrowDirection <= UIPopoverArrowDirectionRight) {
if ((supportedArrowDirections & theArrowDirection)) {
CGRect theBgRect = CGRectMake(0, 0, theSize.width, theSize.height);
CGRect theArrowRect = CGRectZero;
CGPoint theOffset = CGPointZero;
CGFloat xArrowOffset = 0.0;
CGFloat yArrowOffset = 0.0;
CGPoint anchorPoint = CGPointZero;
switch (theArrowDirection) {
case UIPopoverArrowDirectionUp:
anchorPoint = CGPointMake(CGRectGetMidX(anchorRect) - displayArea.origin.x, CGRectGetMaxY(anchorRect) - displayArea.origin.y);
xArrowOffset = theSize.width / 2 - upArrowImage.size.width / 2;
yArrowOffset = properties.topBgMargin - upArrowImage.size.height;
theOffset = CGPointMake(anchorPoint.x - xArrowOffset - upArrowImage.size.width / 2, anchorPoint.y - yArrowOffset);
if (theOffset.x < 0) {
xArrowOffset += theOffset.x;
theOffset.x = 0;
} else if (theOffset.x + theSize.width > displayArea.size.width) {
xArrowOffset += (theOffset.x + theSize.width - displayArea.size.width);
theOffset.x = displayArea.size.width - theSize.width;
}
//Cap the arrow offset
xArrowOffset = MAX(xArrowOffset, properties.leftBgMargin + properties.arrowMargin);
xArrowOffset = MIN(xArrowOffset, theSize.width - properties.rightBgMargin - properties.arrowMargin - upArrowImage.size.width);
theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, upArrowImage.size.width, upArrowImage.size.height);
break;
case UIPopoverArrowDirectionDown:
anchorPoint = CGPointMake(CGRectGetMidX(anchorRect) - displayArea.origin.x, CGRectGetMinY(anchorRect) - displayArea.origin.y);
xArrowOffset = theSize.width / 2 - downArrowImage.size.width / 2;
yArrowOffset = theSize.height - properties.bottomBgMargin;
theOffset = CGPointMake(anchorPoint.x - xArrowOffset - downArrowImage.size.width / 2, anchorPoint.y - yArrowOffset - downArrowImage.size.height);
if (theOffset.x < 0) {
xArrowOffset += theOffset.x;
theOffset.x = 0;
} else if (theOffset.x + theSize.width > displayArea.size.width) {
xArrowOffset += (theOffset.x + theSize.width - displayArea.size.width);
theOffset.x = displayArea.size.width - theSize.width;
}
//Cap the arrow offset
xArrowOffset = MAX(xArrowOffset, properties.leftBgMargin + properties.arrowMargin);
xArrowOffset = MIN(xArrowOffset, theSize.width - properties.rightBgMargin - properties.arrowMargin - downArrowImage.size.width);
theArrowRect = CGRectMake(xArrowOffset , yArrowOffset, downArrowImage.size.width, downArrowImage.size.height);
break;
case UIPopoverArrowDirectionLeft:
anchorPoint = CGPointMake(CGRectGetMaxX(anchorRect) - displayArea.origin.x, CGRectGetMidY(anchorRect) - displayArea.origin.y);
xArrowOffset = properties.leftBgMargin - leftArrowImage.size.width;
yArrowOffset = theSize.height / 2 - leftArrowImage.size.height / 2;
theOffset = CGPointMake(anchorPoint.x - xArrowOffset, anchorPoint.y - yArrowOffset - leftArrowImage.size.height / 2);
if (theOffset.y < 0) {
yArrowOffset += theOffset.y;
theOffset.y = 0;
} else if (theOffset.y + theSize.height > displayArea.size.height) {
yArrowOffset += (theOffset.y + theSize.height - displayArea.size.height);
theOffset.y = displayArea.size.height - theSize.height;
}
//Cap the arrow offset
yArrowOffset = MAX(yArrowOffset, properties.topBgMargin + properties.arrowMargin);
yArrowOffset = MIN(yArrowOffset, theSize.height - properties.bottomBgMargin - properties.arrowMargin - leftArrowImage.size.height);
theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, leftArrowImage.size.width, leftArrowImage.size.height);
break;
case UIPopoverArrowDirectionRight:
anchorPoint = CGPointMake(CGRectGetMinX(anchorRect) - displayArea.origin.x, CGRectGetMidY(anchorRect) - displayArea.origin.y);
xArrowOffset = theSize.width - properties.rightBgMargin;
yArrowOffset = theSize.height / 2 - rightArrowImage.size.width / 2;
theOffset = CGPointMake(anchorPoint.x - xArrowOffset - rightArrowImage.size.width, anchorPoint.y - yArrowOffset - rightArrowImage.size.height / 2);
if (theOffset.y < 0) {
yArrowOffset += theOffset.y;
theOffset.y = 0;
} else if (theOffset.y + theSize.height > displayArea.size.height) {
yArrowOffset += (theOffset.y + theSize.height - displayArea.size.height);
theOffset.y = displayArea.size.height - theSize.height;
}
//Cap the arrow offset
yArrowOffset = MAX(yArrowOffset, properties.topBgMargin + properties.arrowMargin);
yArrowOffset = MIN(yArrowOffset, theSize.height - properties.bottomBgMargin - properties.arrowMargin - rightArrowImage.size.height);
theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, rightArrowImage.size.width, rightArrowImage.size.height);
break;
default:
break;
}
CGRect bgFrame = CGRectOffset(theBgRect, theOffset.x, theOffset.y);
CGFloat minMarginLeft = CGRectGetMinX(bgFrame);
CGFloat minMarginRight = CGRectGetWidth(displayArea) - CGRectGetMaxX(bgFrame);
CGFloat minMarginTop = CGRectGetMinY(bgFrame);
CGFloat minMarginBottom = CGRectGetHeight(displayArea) - CGRectGetMaxY(bgFrame);
if (minMarginLeft < 0) {
// Popover is too wide and clipped on the left; decrease width
// and move it to the right
theOffset.x -= minMarginLeft;
theBgRect.size.width += minMarginLeft;
minMarginLeft = 0;
if (theArrowDirection == UIPopoverArrowDirectionRight) {
theArrowRect.origin.x = CGRectGetMaxX(theBgRect) - properties.rightBgMargin;
}
}
if (minMarginRight < 0) {
// Popover is too wide and clipped on the right; decrease width.
theBgRect.size.width += minMarginRight;
minMarginRight = 0;
if (theArrowDirection == UIPopoverArrowDirectionLeft) {
theArrowRect.origin.x = CGRectGetMinX(theBgRect) - leftArrowImage.size.width + properties.leftBgMargin;
}
}
if (minMarginTop < 0) {
// Popover is too high and clipped at the top; decrease height
// and move it down
theOffset.y -= minMarginTop;
theBgRect.size.height += minMarginTop;
minMarginTop = 0;
if (theArrowDirection == UIPopoverArrowDirectionDown) {
theArrowRect.origin.y = CGRectGetMaxY(theBgRect) - properties.bottomBgMargin;
}
}
if (minMarginBottom < 0) {
// Popover is too high and clipped at the bottom; decrease height.
theBgRect.size.height += minMarginBottom;
minMarginBottom = 0;
if (theArrowDirection == UIPopoverArrowDirectionUp) {
theArrowRect.origin.y = CGRectGetMinY(theBgRect) - upArrowImage.size.height + properties.topBgMargin;
}
}
CGFloat minMargin = MIN(minMarginLeft, minMarginRight);
minMargin = MIN(minMargin, minMarginTop);
minMargin = MIN(minMargin, minMarginBottom);
// Calculate intersection and surface
CGFloat surface = theBgRect.size.width * theBgRect.size.height;
if (surface >= biggestSurface && minMargin >= currentMinMargin) {
biggestSurface = surface;
offset = CGPointMake(theOffset.x + displayArea.origin.x, theOffset.y + displayArea.origin.y);
arrowRect = theArrowRect;
bgRect = theBgRect;
arrowDirection = theArrowDirection;
currentMinMargin = minMargin;
}
}
theArrowDirection <<= 1;
}
switch (arrowDirection) {
case UIPopoverArrowDirectionUp:
arrowImage = [upArrowImage retain];
break;
case UIPopoverArrowDirectionDown:
arrowImage = [downArrowImage retain];
break;
case UIPopoverArrowDirectionLeft:
arrowImage = [leftArrowImage retain];
break;
case UIPopoverArrowDirectionRight:
arrowImage = [rightArrowImage retain];
break;
default:
break;
}
}
@end