Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS UIPopoverController transparency / alpha

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

like image 367
Jasper Avatar asked Jan 19 '23 02:01

Jasper


1 Answers

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

like image 105
Richard J. Ross III Avatar answered Jan 21 '23 17:01

Richard J. Ross III