Customizing UIPopover with UIPopoverBackgroundView 1-1

requirement: iOS SDK 5.0 or later.

UIPopoverBackgroundView class

Example code:

1
2
yourPopoverController.popoverBackgroundViewClass
    = [YourCustomPopoverBackgorundView class];

Properties and class methods

Class methods:

  • arrowHeight – the height of the arrow (measured in points) from its base to its tip.
  • arrowBase – the width of the arrow triangle at its base.
  • contentViewInsets – the insets for the content portion of the popover.

Properties:

  • arrowOffset – the distance (measured in points) from the center of the view to the center line of the arrow (arrowOffset has negative value if left from center, positive if right from center)
  • arrowDirection – the direction in which the popover arrow is pointing: UIPopoverArrowDirection(Up / Down / Left / Right / Any / Unknown)

Example code:

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+ (CGFloat)arrowBase
{
    return ARROW_WIDTH;
}

+ (CGFloat)arrowHeight
{
    return ARROW_HEIGHT;
}
+ (UIEdgeInsets)contentViewInsets{
    return UIEdgeInsetsMake(TOP_CONTENT_INSET, LEFT_CONTENT_INSET,
                            BOTTOM_CONTENT_INSET, RIGHT_CONTENT_INSET);
}

// Custom setters

// Whenever arrow changes direction or position layout subviews
// will be called in order to update arrow and backgorund images frames

-(void) setArrowOffset:(CGFloat)arrowOffset
{
    _arrowOffset = arrowOffset;
    [self setNeedsLayout];
}

-(void) setArrowDirection:(UIPopoverArrowDirection)arrowDirection
{
    _arrowDirection = arrowDirection;
    [self setNeedsLayout];
}

Additional properties and iVars

There are two properties of UIImageView in my subclass implementation. I use them to layout stretchable background images (arrow + background)

Example code:

1
2
@property (nonatomic, strong) UIImageView *arrowImageView;
@property (nonatomic, strong) UIImageView *popoverBackgroundImageView;
They are initialized in initWithFrame: method.

Example code:

 
01
02
03
04
05
06
07
08
09
10
UIImage *popoverBackgroundImage
    = [[UIImage imageNamed:@"popover-black-bcg-image.png"]
            resizableImageWithCapInsets:UIEdgeInsetsMake(49, 46, 49, 45)];

self.popoverBackgroundImageView
           = [[UIImageView alloc] initWithImage:popoverBackgroundImage];
[self addSubview:self.popoverBackgroundImageView];

self.arrowImageView = [[UIImageView alloc] init];
[self addSubview:self.arrowImageView];

Additionally, in my class implementation file I made private interface with 4 UIImage iVars. One of  images is going to be set as arrowImageView.image later in layoutSubviews method, when I calculate other frames using arrowDirection property.

Example code:

1
2
3
4
5
6
7
8
9
@interface GTPopoverBackgorundView ()
{
    UIImage *_topArrowImage;
    UIImage *_leftArrowImage;
    UIImage *_rightArrowImage;
    UIImage *_bottomArrowImage;
}

@end
UIImage iVars are initialized in initWithFrame: method with appropriate images.

Example code:

 
1
2
3
4
_topArrowImage = [UIImage imageNamed:@"popover-black-top-arrow-image.png"];
_leftArrowImage = [UIImage imageNamed:@"popover-black-left-arrow-image.png"];
_bottomArrowImage = [UIImage imageNamed:@"popover-black-bottom-arrow-image.png"];
_rightArrowImage = [UIImage imageNamed:@"popover-black-right-arrow-image.png"];

layoutSubviews

The most important part of subclass is layoutSubviews method. It layouts arrow and background images according to popover size (self.bounds.size), arrow position (arrowOffset) and arrow direction (arrowDirection). Also popover arrow image is changed here for appropriate direction.

Example code:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
-(void)layoutSubviews
{
    CGFloat popoverImageOriginX = 0;
    CGFloat popoverImageOriginY = 0;

    CGFloat popoverWidth = self.bounds.size.width;
    CGFloat popoverHeight = self.bounds.size.height;

    CGFloat arrowImageOriginX = 0;
    CGFloat arrowImageOriginY = 0;

    CGFloat arrowImageWidth = ARROW_WIDTH;
    CGFloat arrowImageHeight = ARROW_HEIGHT;

    // Radius value you used to make rounded corners
    // in your popover background image
    CGFloat cornerRadius = 9;

    switch (self.arrowDirection) {

        case UIPopoverArrowDirectionUp:

            popoverImageOriginY = ARROW_HEIGHT - 2;
            popoverImageHeight = self.bounds.size.height - ARROW_HEIGHT;

            // Calculating arrow x position using arrow offset,
            // arrow width and popover width
            arrowImageOriginX = roundf((self.bounds.size.width
                                         - ARROW_WIDTH) / 2 + self.arrowOffset);

            // If arrow image exceeds rounded corner,
            // arrow image x postion is adjusted
            if (arrowImageOriginX + ARROW_WIDTH
                             > self.bounds.size.width - cornerRadius)
            {
                arrowImageOriginX -= cornerRadius;
            }

            if (arrowImageOriginX < cornerRadius)
            {
                arrowImageOriginX += cornerRadius;
            }

            // Setting arrow image for current arrow direction
            self.arrowImageView.image = _topArrowImage;

            break;

        case UIPopoverArrowDirectionDown:

            // Similar like in UIPopoverArrowDirectionUp case

            break;

        case UIPopoverArrowDirectionLeft:

            // Similar like in UIPopoverArrowDirectionUp case

            break;

        case UIPopoverArrowDirectionRight:

            // Similar like in UIPopoverArrowDirectionUp case

            break;

        default:

            // For popovers without arrows (Thanks Martin!)

            popoverImageHeight = self.bounds.size.height - ARROW_HEIGHT + 2;
            break;
    }

    self.popoverBackgroundImageView.frame = CGRectMake(popoverImageOriginX,
             popoverImageOriginY, popoverWidth, popoverHeight);
    self.arrowImageView.frame = CGRectMake(arrowImageOriginX,
             arrowImageOriginY, arrowImageWidth, arrowImageHeight);
}

Result

BTW,

popover size_____

_nav.contentSizeForViewInPopover=CGSizeMake(200, 420);
_popoverForSource = [[UIPopoverController alloc]initWithContentViewController:_nav];
Advertisements

One thought on “Customizing UIPopover with UIPopoverBackgroundView 1-1”

  1. UIPopoverBackgroundView: UIView
    @property arrowOffset;
    @property arrowDirection;
    +(CGFloat)arrowHeight;
    +(CGFloat)arrowBase;
    +(UIEdgeInsets)contentViewInsets;
    + (BOOL)wantsDefaultContentAppearance NS_AVAILABLE_IOS(6_0); //default as YES

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s