I'm making an iPad app where the client wants a popoverview with transparency (alpha). They gave me an example app, screenshot can be found here
I've read that UIPopoverController is the worst thing to customize as there are only a few properties to set. LINK & LINK
I tried it with lowering a subview's alpha but then the content of the view gets transparant too.
Am I missing an option, or doing something wrong to get this right?
Also, I have little experience with subclassing, so any suggestions if thats the case would be more than appreciated.
-Jasper
I created a subclass of UIView for you that should work for what you need...
PopoverView.h:
//
// PopoverView.h
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <CoreGraphics/CoreGraphics.h>
@interface PopoverView : UIView
{
UIColor *backgroundColor;
CGSize contentSize;
UIViewController *contentViewController;
CGSize borders;
}
@property(nonatomic, retain) UIColor *backgroundColor;
@property(readonly, assign) CGSize contentSize;
@property(nonatomic, retain) UIViewController *contentViewController;
@property(nonatomic, assign) CGSize borders;
@end
PopoverView.m
//
// PopoverView.m
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import "PopoverView.h"
@implementation PopoverView
@synthesize backgroundColor, borders, contentSize, contentViewController;
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"frame"] || [keyPath isEqualToString:@"borders"])
{
contentSize = CGRectInset(self.frame, borders.width, borders.height).size;
contentViewController.view.frame = CGRectMake(borders.width, borders.height, contentSize.width, contentSize.height);
}
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.opaque = NO;
self.clipsToBounds = NO;
self.backgroundColor = [UIColor colorWithRed:50/256 green:50/256 blue:50/256 alpha:0.75f];
self.borders = CGSizeMake(25, 25);
contentSize = CGRectInset(frame, borders.width * 2, borders.height * 2).size;
self.userInteractionEnabled = YES;
[self addObserver:self forKeyPath:@"frame" options:kNilOptions context:nil];
[self addObserver:self forKeyPath:@"borders" options:kNilOptions context:nil];
}
return self;
}
-(void) setContentViewController:(UIViewController *)contentViewController_
{
if (contentViewController_ != self->contentViewController)
{
[self->contentViewController.view removeFromSuperview];
self->contentViewController = contentViewController_;
[self addSubview:contentViewController_.view];
contentViewController_.view.frame = CGRectMake(borders.width, borders.height, contentSize.width, contentSize.height);
}
}
void CGContextDrawRoundedRect(CGContextRef context, CGColorRef color, CGRect bounds, CGFloat radius);
void CGContextDrawRoundedRect(CGContextRef context, CGColorRef color, CGRect bounds, CGFloat radius)
{
CGContextSetFillColorWithColor(context, color);
// If you were making this as a routine, you would probably accept a rectangle
// that defines its bounds, and a radius reflecting the "rounded-ness" of the rectangle.
CGRect rrect = bounds;
// NOTE: At this point you may want to verify that your radius is no more than half
// the width and height of your rectangle, as this technique degenerates for those cases.
// In order to draw a rounded rectangle, we will take advantage of the fact that
// CGContextAddArcToPoint will draw straight lines past the start and end of the arc
// in order to create the path from the current position and the destination position.
// In order to create the 4 arcs correctly, we need to know the min, mid and max positions
// on the x and y lengths of the given rectangle.
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);
// Next, we will go around the rectangle in the order given by the figure below.
// minx midx maxx
// miny 2 3 4
// midy 1 9 5
// maxy 8 7 6
// Which gives us a coincident start and end point, which is incidental to this technique, but still doesn't
// form a closed path, so we still need to close the path to connect the ends correctly.
// Thus we start by moving to point 1, then adding arcs through each pair of points that follows.
// You could use a similar tecgnique to create any shape with rounded corners.
// Start at 1
CGContextMoveToPoint(context, minx, midy);
// Add an arc through 2 to 3
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
// Add an arc through 4 to 5
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
// Add an arc through 6 to 7
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
// Add an arc through 8 to 9
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
// Close the path
CGContextClosePath(context);
// Fill the path
CGContextDrawPath(context, kCGPathFill);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGColorRef color = [backgroundColor CGColor];
CGContextDrawRoundedRect(currentContext, color, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height), 10);
}
@end
ArrowView.h:
//
// ArrowView.h
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ArrowView : UIView
{
UIColor *arrowColor;
}
@property(nonatomic, retain) UIColor *arrowColor;
@end
ArrowView.m:
//
// ArrowView.m
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import "ArrowView.h"
#define CGRectCenter(rect) CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))
#define CGSizeDiv(size, div) CGSizeMake(size.width / div, size.height / div)
@implementation ArrowView
@synthesize arrowColor;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.opaque = NO;
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(currentContext, arrowColor.CGColor);
CGPoint arrowLocation = CGRectCenter(self.bounds);
CGSize arrowSize = CGSizeDiv(self.frame.size, 1);
CGPoint arrowTip = CGPointMake(arrowLocation.x, arrowLocation.y + (arrowSize.height / 2));
CGPoint arrowLeftFoot = CGPointMake(arrowLocation.x - (arrowSize.width / 2), arrowLocation.y - (arrowSize.height / 2));
CGPoint arrowRightFoot = CGPointMake(arrowLocation.x + (arrowSize.width / 2), arrowLocation.y - (arrowSize.height / 2));
// now we draw the triangle
CGContextMoveToPoint(currentContext, arrowTip.x, arrowTip.y);
CGContextAddLineToPoint(currentContext, arrowLeftFoot.x, arrowLeftFoot.y);
CGContextAddLineToPoint(currentContext, arrowRightFoot.x, arrowRightFoot.y);
CGContextClosePath(currentContext);
CGContextDrawPath(currentContext, kCGPathFill);
}
@end
PopoverViewController.h:
//
// PopoverViewController.h
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PopoverView.h"
#import "ArrowView.h"
@interface PopoverViewController : UIViewController
{
PopoverView *popover;
ArrowView *arrow;
UIPopoverArrowDirection arrowDirection;
UIViewController *parentViewController;
}
// for managing the content
@property(readonly, retain) PopoverView *popover;
// the arrow
@property(readonly, retain) ArrowView *arrow;
@property(nonatomic, assign) UIPopoverArrowDirection arrowDirection;
-(void) presentModallyInViewController:(UIViewController *) controller animated:(BOOL) animated;
-(void) dismissModallyFromViewController:(UIViewController *) controller animated:(BOOL) animated;
-(void) dismiss;
@end
PopoverViewController.m:
//
// PopoverViewController.m
// popovertest
//
// Created by Richard Ross III on 12/7/11.
// Copyright (c) 2011 Ultimate Computer Services Inc. All rights reserved.
//
#import "PopoverViewController.h"
#define degreestoradians(x) (M_PI * x / 180)
@implementation PopoverViewController
@synthesize arrowDirection, arrow, popover;
-(void) updateArrowPosition
{
if (arrowDirection & UIPopoverArrowDirectionUp)
{
arrow.frame = CGRectMake((popover.frame.origin.x) + (popover.frame.size.width / 2) - (arrow.frame.size.width / 2), popover.frame.origin.y - (arrow.frame.size.height), arrow.frame.size.width, arrow.frame.size.height);
arrow.transform = CGAffineTransformMakeRotation(degreestoradians(180));
}
else if (arrowDirection & UIPopoverArrowDirectionLeft)
{
arrow.frame = CGRectMake((popover.frame.origin.x) - (arrow.frame.size.width), (popover.frame.origin.y) + (popover.frame.size.height / 2) - (arrow.frame.size.height / 2), arrow.frame.size.width, arrow.frame.size.height);
arrow.transform = CGAffineTransformMakeRotation(degreestoradians(90));
}
else if (arrowDirection & UIPopoverArrowDirectionRight)
{
arrow.frame = CGRectMake((popover.frame.origin.x) + (popover.frame.size.width), (popover.frame.origin.y) + (popover.frame.size.height / 2) - (arrow.frame.size.height / 2), arrow.frame.size.width, arrow.frame.size.height);
arrow.transform = CGAffineTransformMakeRotation(degreestoradians(-90));
}
else if (arrowDirection & UIPopoverArrowDirectionDown)
{
arrow.frame = CGRectMake((popover.frame.origin.x) + (popover.frame.size.width / 2) - (arrow.frame.size.width / 2), popover.frame.origin.y + popover.frame.size.height, arrow.frame.size.width, arrow.frame.size.height);
}
else
{
NSLog(@"unknown direction %i", arrowDirection);
}
}
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self updateArrowPosition];
}
-(void) dismiss
{
[self dismissModallyFromViewController:parentViewController animated:YES];
}
-(void) presentModallyInViewController:(UIViewController *)controller animated:(BOOL)animated
{
[controller.view addSubview:self.view];
if (animated)
{
self.view.alpha = 0.0f;
[UIView beginAnimations:@"Modal Entrance" context:nil];
[UIView setAnimationDuration:0.25];
self.view.alpha = 1.0f;
[UIView commitAnimations];
}
}
-(void) dismissModallyFromViewController:(UIViewController *)controller animated:(BOOL)animated
{
if (animated)
{
[UIView animateWithDuration:0.25 animations:^{
self.view.alpha = 0.0f;
} completion:^(BOOL finished) {
[controller dismissModalViewControllerAnimated:NO];
}];
}
else
{
[controller dismissModalViewControllerAnimated:NO];
}
}
-(id) init
{
if (self = [super init])
{
popover = [PopoverView new];
arrow = [ArrowView new];
popover.backgroundColor = [UIColor colorWithRed:50/255 green:50/255 blue:50/255 alpha:0.75];
arrow.arrowColor = [UIColor colorWithRed:50/255 green:50/255 blue:50/255 alpha:0.75];
arrow.frame = CGRectMake(0, 0, 20, 20);
[self addObserver:self forKeyPath:@"arrowDirection" options:kNilOptions context:nil];
[popover addObserver:self forKeyPath:@"frame" options:kNilOptions context:nil];
[self.view addSubview:popover];
[self.view addSubview:arrow];
arrowDirection = UIPopoverArrowDirectionRight;
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self dismiss];
}
@end
Example Usage:
PopoverViewController *popController = [[PopoverViewController alloc] init];
UIButton *contentView = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[contentView setTitle:@"Hello World!" forState:UIControlStateNormal];
UIViewController *controller = [[UIViewController alloc] init];
controller.view = contentView;
popController.popover.contentViewController = controller;
popController.popover.frame = CGRectMake(100, 100, 300, 400);
[popController presentModallyInViewController:self animated:YES];
Please note that this was made with ARC enabled, so if you are using reference counting this code will need some tweaking.
For convenience, I have put the entire project in my dropbox, for you to download: http://dl.dropbox.com/u/25258177/personal/popovertest.zip
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With