我正在制作一个 iPad 应用程序,客户想要一个具有透明度 (alpha) 的 popoverview。
他们给了我一个示例应用程序,可以找到屏幕截图 here

我读过 UIPopoverController 是最难自定义的,因为只需要设置几个属性。
LINK & LINK

我尝试降低 subview 的 alpha,但随后 View 的内容也变得透明了。

我是不是错过了一个选项,或者做错了什么才能做到这一点?

另外,我在子类化方面的经验很少,因此如果是这种情况,任何建议都将不胜感激。

-碧 Jade

最佳答案

我为你创建了一个 UIView 的子类,它应该可以满足你的需要......

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

箭头 View .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

箭头 View .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

示例用法:
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];

请注意,这是在启用 ARC 的情况下制作的,因此如果您使用引用计数,则此代码需要进行一些调整。

为方便起见,我将整个项目放在我的保管箱中,供您下载:
http://dl.dropbox.com/u/25258177/personal/popovertest.zip

关于iOS UIPopoverController 透明度/alpha,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8417179/

10-12 00:18