Joris Kluivers

I like to build software.

Custom Popups Revisited

One of the most popular pages here is an old (iOS 2.x era) post that describes how to subclass an UIAlertView to customize it’s appearance. That post describes how to transform the default blue popup into something more arbitrary.

However ever since that post I’ve come to the conclusion you don’t want to subclass UIKit classes, unless they were specifically designed to do so. A UIAlertView was definitely not designed to subclass. Time for a post on how to do this correctly.

I’m still using custom overlays and alerts in my applications, using an improved technique instead. All my popups derive from a common popup class. This post will show how this popup class works and how you can use it to create your own custom alerts and popups by subclassing it. I’ll demonstrate this by creating a nice progress HUD. (Similar to MBProgressHUD, but based on the technique explained here).

To dive right into the code, download the sample project from my bitbucket:

Source: kluivers / JKProgressHUD (git repository)

JKPopup

The JKPopup is a common base class derived from UIView that takes care of showing itself above all other content. It’s interface is fairly simple.

JKPopup.h
1
2
3
4
@interface JKPopup : UIView
- (void) show;
- (void) hide;
@end

Upon calling - (void) show; it will add itself centered to a new UIWindow. This window will be presented on level UIWindowLevelAlert to make sure it will float over all other content.

JKPopup.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#import "JKPopup.h"

@interface JKPopup ()
@property(nonatomic, strong) UIWindow *window;
@end

@implementation JKPopup

@synthesize window;

- (void) show {
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    self.window.windowLevel = UIWindowLevelAlert;
    self.window.backgroundColor = [UIColor clearColor];
  
  self.center = CGPointMake(CGRectGetMidX(self.window.bounds), CGRectGetMidY(self.window.bounds));
  
  [self.window addSubview:self];
  [self.window makeKeyAndVisible];
}

- (void) hide {
  self.window.hidden = YES;
  self.window = nil;
}

@end

That’s all it takes. The example project actually has some code to animate the window in and out, but I left that out to keep the code as clear as possible.

JKProgressHUD

Now that we have a base class to handle the popup stuff all we have to do is change it’s appearance to our liking. For demonstration purposes we’ll make it look like a UIProgressHud, a private class used by Apple: black translucent background, rounded corners and a UIActivityIndicatorView.

We inherit from JKPopup and define an indicator view.

JKProgressHUD.h
1
2
3
4
5
#import "JKPopup.h"

@interface JKProgressHUD : JKPopup
@property(nonatomic, readonly) UIActivityIndicatorView *activityView;
@end

Customize the appearance in the initWithFrame:. Includes some layout and makes sure the activity indicator starts spinning upon being shown.

JKProgressHUD.m
1
2
3
4
5
6
7
8
9
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
#import "JKProgressHUD.h"

#import <QuartzCore/QuartzCore.h>

@implementation JKProgressHUD

@synthesize activityView=_activityView;

- (id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
      // translucent black with rounded corners
        self.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.8f];
      self.layer.cornerRadius = 9.0f;

      _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
      [self addSubview:_activityView];
    }
    return self;
}

- (void) layoutSubviews {
  [super layoutSubviews];
  
  // place the activity indicator in the center
  CGRect activityFrame = self.activityView.frame;
  activityFrame.origin = CGPointMake(
     floorf((CGRectGetWidth(self.frame) - CGRectGetWidth(activityFrame)) / 2.0f),
     floorf((CGRectGetHeight(self.frame) - CGRectGetHeight(activityFrame)) / 2.0f)
  );
  self.activityView.frame = activityFrame;
}

- (void) show {
    [self.activityView startAnimating];
    [super show];
}

@end

Conclusion

While the example JKProgressHUD is not that interesting, it demonstrates how to use the base JKPopup class. JKPopup makes it fairly simple to present any content over anything already on screen. By using a new UIWindow you as a programmer don’t have to worry about the existing view layout, your new ‘alert’ content will float centered on screen.