5

We're moving away from MBProgressHUD because it's too glitchy in our app, and doesn't have features such as blocking user input or providing a Cancel button.

So, I've attempted to implement Swipesight's How to display activity indicator in center of UIAlertController?, and I ran into improper positioning of the indicator:

A <code>UIAlertController</code> with "Loading" and censored text in the center of the screen, but the activity indicator far to the left and below the alert controller
It is green because our app's tint is green.

As you can see, it's not in the white rectangle part of the controller, but the grey background. This uses something similar to his "@62Shark" solution:

// in implementation:

@property (nonatomic, strong) UIActivityIndicatorView *spinner;
@property (nonatomic, strong) UIAlertController *alertController;


// in init:

_alertController = [UIAlertController alertControllerWithTitle: @"Loading"
                                                         message: nil
                                                  preferredStyle: UIAlertControllerStyleAlert];
_spinner = [UIActivityIndicatorView new];
_spinner.translatesAutoresizingMaskIntoConstraints = false;
_spinner.userInteractionEnabled = false;
_spinner.color = [ThemingAssistant tintColor];
_spinner.frame = _alertController.view.bounds;
_spinner.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[_spinner startAnimating];
[_alertController.view addSubview: _spinner];


// ...

- (void) showOn: (UIViewController *)target
          title: (NSString *)title
        message: (NSString *)message
      canCancel: (BOOL)canCancel
{
    self.alertController.title = title;
    self.alertController.message = message;

    if (canCancel)
    {
        [self.alertController addAction:[UIAlertAction actionWithTitle: @"Cancel"
                                                                 style: UIAlertActionStyleCancel
                                                               handler: ^(UIAlertAction *name){
                                                                   [self customDismiss];
                                                               }]];
    }

    NSDictionary *views = @{@"pending"   : self.alertController.view,
                            @"indicator" : self.spinner};
    NSArray *constraints =
    [NSLayoutConstraint constraintsWithVisualFormat: @"V:[indicator]-(-50)-|"
                                            options: 0
                                            metrics: nil
                                              views: views];
    [constraints arrayByAddingObjectsFromArray:
     [NSLayoutConstraint constraintsWithVisualFormat: @"H:|[indicator]|"
                                             options: 0
                                             metrics: nil
                                               views: views]];


    [target presentViewController: self.alertController
                         animated: true
                       completion: nil];
}

Even if this was in the white rectangle, I fear it might run into text (also, when I get it in there, I want it to be in the middle-top, much like MBProgressHUD does), so I'll need a way to reserve some space for it.

So, my question is two-fold: How do I reserve space for a UIActivityIndicatorView in a UIAlertController's white rectangle, and then how do I actually place it in there?

Community
  • 1
  • 1
Ky -
  • 30,724
  • 51
  • 192
  • 308
  • There is a POD that make what you want. This POD is well known in the community, easy to use and used by large number of applications. It is MIT license. I think you should try. [MBProgressHUD](https://github.com/jdg/MBProgressHUD) – Haroldo Gondim Jul 14 '15 at 14:07
  • In fact, I am trying to do this because we're moving away from MBProgressHUD. It's too glitchy in our app, and doesn't have features such as blocking user input or providing a Cancel button. – Ky - Jul 14 '15 at 14:12

1 Answers1

18

Like JonasG mentioned here there is a property named contentViewController and we can use KVC for access

Example :

UIViewController *v = [[UIViewController alloc] init];
v.view.backgroundColor = [UIColor redColor];

[alertController setValue:v forKey:@"contentViewController"];

So here is how your code should looks like (tested and works fine) :

- (IBAction)buttonClicked:(id)sender
{

    self.alertController = [UIAlertController alertControllerWithTitle: @"Loading"
                                                           message: nil
                                                    preferredStyle: UIAlertControllerStyleAlert];


    [self.alertController addAction:[UIAlertAction actionWithTitle: @"Cancel" style: UIAlertActionStyleCancel handler:nil]];


    UIViewController *customVC     = [[UIViewController alloc] init];


    UIActivityIndicatorView* spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [spinner startAnimating];
    [customVC.view addSubview:spinner];


    [customVC.view addConstraint:[NSLayoutConstraint
                           constraintWithItem: spinner
                           attribute:NSLayoutAttributeCenterX
                           relatedBy:NSLayoutRelationEqual
                           toItem:customVC.view
                           attribute:NSLayoutAttributeCenterX
                           multiplier:1.0f
                           constant:0.0f]];



    [customVC.view addConstraint:[NSLayoutConstraint
                           constraintWithItem: spinner
                           attribute:NSLayoutAttributeCenterY
                           relatedBy:NSLayoutRelationEqual
                           toItem:customVC.view
                           attribute:NSLayoutAttributeCenterY
                           multiplier:1.0f
                           constant:0.0f]];


    [self.alertController setValue:customVC forKey:@"contentViewController"];


    [self presentViewController: self.alertController
                         animated: true
                       completion: nil];

}

You can override -preferredContentSize to return a custom size in the view controller that you are setting as contentViewController.

in our case it's customVC

Result:

alert

Want the text to be below the indicator ?

I have created a UIViewController with an xib to act as a custom controller for our contentViewController in the first example we have created the view controller without xib file, Now we can add views using interface builder, I have added an Activity indicator and set the constraints to be centered horizontally and vertically and a label under the activity indicator which is centered horizontally here is my interface builder:

contentViewController

we have less code now:

- (IBAction)buttonClicked:(id)sender
{

    self.alertController = [UIAlertController alertControllerWithTitle: nil
                                                           message: nil
                                                    preferredStyle: UIAlertControllerStyleAlert];

    MyCustomViewController *v     = [[MyCustomViewController alloc] init];

    [self.alertController setValue:v forKey:@"contentViewController"];
    [self presentViewController: self.alertController animated: true  completion: nil];

}

Result :

Custom alert

Community
  • 1
  • 1
Alaeddine
  • 6,104
  • 3
  • 28
  • 45
  • Thanks! I'll try this when it comes back up my queue – Ky - Jul 14 '15 at 18:12
  • Well it's now beautifully inside the white rectangle, but on the bottom. How do I put it above the text? – Ky - Jul 14 '15 at 19:34
  • Also, the padding around the indicator is huge (yours seems to be flush, but mine is about a half inch on all sides) even after setting `preferredContentSize` – Ky - Jul 14 '15 at 20:56
  • Thank you, beautiful! I'll use this in my new design – Ky - Jul 14 '15 at 23:47
  • I haven't tried this yet, but I'm hoping this still allows for the Cancel button. – Ky - Jul 14 '15 at 23:53
  • 1
    yes sure, as you can see from the code I have deleted the `addAction` line just for design purposes ;) – Alaeddine Jul 14 '15 at 23:55
  • creating from ViewController does not seem to work anymore, or at least for me. the view controller gets added to the alert but never shows – tyczj Dec 30 '15 at 16:36
  • 1
    contentViewController is a private property, be careful with this as is may change and/or be rejected. – lostintranslation Sep 27 '16 at 15:44