I was recently working on an iOS project and was implementing a login view. As usual, the user enters their credentials, the credentials are sent to a server for verification and, if successful, the user is allowed into the app. The problem I ran into is that the asynchronous account validation leaves the user on the login view where the user could press the login button again, causing another login request.

The first way I thought of to deal with this was to disable the login button. But then I’d have to also disable the username and password fields because the user shouldn’t edit those either. Then I realized that what I really don’t like is that there isn’t much indication what’s happening - or that anything is happening.

This made me think about how my bank’s banking app works. It shows a full-screen activity indicator which I really like. I decided I wanted to replicate this and make it generic so it could be shared with other projects. This is really useful for situations where it’s valid to halting user interaction while waiting on network requests (without blocking the main thread). The banking app uses a very tailored indicator but I didn’t want to go this far, I just wanted something generic.

This class ActivityOverlay will create a full-screen overlay with the screen greyed out and an animated activity indicator showing in the middle of the screen (for an undetermined amount of time). This should be paired with dispatch_async. On completion of the request the overlay will be hidden.

ActivityOverlay.h

#ifndef ActivityOverlay_h
#define ActivityOverlay_h

@interface ActivityOverlay : NSObject

+ (id)activityOverlay;

- (id)init;

- (void)show:(UIView *)view;
- (void)hide;

@end

#endif /* ActivityOverlay_h */

The show function takes the view that this will overlay as an argument. The ActivityOverlay object itself is not tied to this view as part of the object creation. Instead it is paired dynamically when show is called so the object can be reused more easily.

ActivityOverlay.m

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#import "ActivityOverlay.h"

@implementation ActivityOverlay {
    UIView *_overlay = nil;
    UIActivityIndicatorView *_indicator = nil;
}

+ (id)activityOverlay {
    return [[ActivityOverlay alloc] init];
}

- (id)init {
    self = [super self];
    if (!self)
        return nil;

    _overlay = [[UIView alloc] init];
    _overlay.backgroundColor = [UIColor colorWithWhite:0x0 alpha:0.5];

    _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];

    [_overlay addSubview:_indicator];

    return self;
}

- (void)show:(UIView *)view {
    _overlay.frame = [UIScreen mainScreen].bounds;
    _overlay.center = view.center;
    _indicator.center = _overlay.center;

    [view addSubview:_overlay];
    [_indicator startAnimating];
}

- (void)hide {
    [_indicator stopAnimating];
    [_overlay removeFromSuperview];
}

@end

Right now this is a very basic and generic activity indicator. It works and gets the job done but a more customized indicator might be a better way to go. At the very least, this demonstrates the necessary underpinnings.

- (id)init {
   ...
    _overlay.backgroundColor = [UIColor colorWithWhite:0x0 alpha:0.5];
   ...
    [_overlay addSubview:_indicator];

The overlay is actually a view which is constructed using a black background with the alpha set to 50% opacity. The indicator is created and added to the overlay so that when the overlay is shown, the indicator will also be shown.

- (void)show:(UIView *)view {
    ...

The overlay is set to the same size as the screen and centered, and the activity indicator is centered in the overlay. Then the overlay is added to the view, which will show the overlay, and the activity indicator’s animation starts.

- (void)hide {
    ...

When the ActivityOverlay is told to hide the overlay disappears and is not associated with the view it was overlaying. There is no need to worry about ARC issues because the reference to the parent view is released. The view that’s being overlaid does not need to be referenced here because UIView’s (ActivityOverlay is a view) are able to remove themselves from views they’re put into.