mirror of
https://github.com/viq/NewsBlur.git
synced 2025-08-21 05:44:54 +00:00
377 lines
12 KiB
Objective-C
Executable file
377 lines
12 KiB
Objective-C
Executable file
//
|
|
// 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 <WEPopoverParentView>)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
|