15

I want to add progress bar in swift iOS 8 UIAlertController. Is this possible? Is there any way to subclass UIAlertController and add progres bar and connect some delegate functions?

thanks

Nik
  • 1,517
  • 5
  • 14
  • 19

4 Answers4

27

If you just need a progressbar you can simply add it as a subview as follows:

Updated for Swift 5:

//  Just create your alert as usual:
let alertView = UIAlertController(title: "Please wait", message: "Need to download some files.", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

//  Show it to your users
present(alertView, animated: true, completion: {
    //  Add your progressbar after alert is shown (and measured)
    let margin:CGFloat = 8.0
    let rect = CGRect(x: margin, y: 72.0, width: alertView.view.frame.width - margin * 2.0 , height: 2.0)
    self.progressView = UIProgressView(frame: rect)
    self.progressView!.progress = 0.5
    self.progressView!.tintColor = self.view.tintColor
    alertView.view.addSubview(self.progressView!)
})

Swift 2.0:

//  Just create your alert as usual:
let alertView = UIAlertController(title: "Please wait", message: "Need to download some files.", preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))

//  Show it to your users
presentViewController(alertView, animated: true, completion: {
    //  Add your progressbar after alert is shown (and measured)
    let margin:CGFloat = 8.0
    let rect = CGRectMake(margin, 72.0, alertView.view.frame.width - margin * 2.0 , 2.0)
    let progressView = UIProgressView(frame: rect)
    progressView.progress = 0.5
    progressView.tintColor = UIColor.blueColor()
    alertView.view.addSubview(progressView)
})

It's quite difficult to resize the UIAlertController for bigger content but for a progressbar this should do the trick.

Patrick
  • 1,717
  • 7
  • 21
  • 28
coyer
  • 4,122
  • 3
  • 28
  • 35
  • 2
    You can simply change "progressView.progress". It may be more clear to define "var progressView : UIProgressView?" outside (not in the completion-block) as an optional and then set "progressView?.progress = 0.123". – coyer Mar 10 '16 at 10:17
  • why do yo use constants? this only get different visual results on iPhone and iPad. Actually, only looks good in iPhone portrait because in landscape the progressview looks shifted – Ricardo May 09 '17 at 14:00
  • http://stackoverflow.com/questions/37409826/how-do-i-get-the-frame-width-of-uialertcontroller – Ricardo May 09 '17 at 14:30
5

A solution with auto layout:

    UIAlertController *alertCtr = [UIAlertController alertControllerWithTitle:@"Test" message:@"50%" preferredStyle:UIAlertControllerStyleAlert];
    [alertCtr addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        // Do things
    }]];

    UIView *alertView = alertCtr.view;

    UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectZero];
    progressView.progress = 0.5;
    progressView.translatesAutoresizingMaskIntoConstraints = false;
    [alertView addSubview:progressView];


    NSLayoutConstraint *bottomConstraint = [progressView.bottomAnchor constraintEqualToAnchor:alertView.bottomAnchor];
    [bottomConstraint setActive:YES];
    bottomConstraint.constant = -45; // How to constraint to Cancel button?

    [[progressView.leftAnchor constraintEqualToAnchor:alertView.leftAnchor] setActive:YES];
    [[progressView.rightAnchor constraintEqualToAnchor:alertView.rightAnchor] setActive:YES];

    [self presentViewController:alertCtr animated:true completion:nil];

enter image description here

Dong Phong
  • 111
  • 2
  • 6
4

My apologies, guys, for using objective c, in this solution, but I thought it might help others, who do not use Swift yet. And, also, you can probably convert this quite easily into Swift. It is more the methodology I wanted to highlight.

I am also not sure whether Apple might reject this solution, but here goes anyway.

Apple states that from iOS7 onwards, UIAlertView should not be subclassed. The view hierarchy for this class is private and must not be modified:

https://developer.apple.com/reference/uikit/uialertview?language=objc

In other words, adding a UIView to a UIAlertView has absolutely no effect.

However, I have a solution that involves adding the UIProgressView above the UIAlertView, but adding the former to the app window. Using the UIView superview.center property, and some slight adjustments, the desired affect can be achieved:

-(void)addProgressBar{
    float width = 232;
    float height = 5;
    self.progbar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
    self.progbar.backgroundColor = [UIColor colorWithWhite:0.75f alpha:1.0f];
    [self.progbar setFrame:CGRectMake(0,0,width,height)];
    [self.progbar setTrackTintColor:[UIColor colorWithWhite:0.75f alpha:1.0f]];
    [self.progbar setProgressTintColor:[UIColor colorWithRed:21.0f/255.0f green:126.0f/255.0f blue:251.0f/255.0f alpha:1.0f]];
    self.progbar.alpha = 0.0;
    [[UIApplication sharedApplication].keyWindow addSubview:self.progbar];
    self.progbar.center = self.progbar.superview.center;
    [self.progbar setFrame:CGRectMake(self.progbar.frame.origin.x,self.progbar.frame.origin.y+10,self.progbar.frame.size.width,self.progbar.frame.size.height)];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:2.0];
    [self.progbar setAlpha:1.0];
    [UIView commitAnimations];
}

I add the fade in, to allow the UIAlertView to fully appear first. Then add some other delegate functions to dismiss the UIProgressView, at the correct moments:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if(self.alert.cancelButtonIndex == buttonIndex){
        [self.progbar removeFromSuperview];
    }
}

- (void)alertViewCancel:(UIAlertView *)alertView{
    [self.progbar removeFromSuperview];
}

enter image description here

Charles Robertson
  • 1,760
  • 16
  • 21
2
func downloadAlert() {
        let alertController = UIAlertController(title: "Title", message: "Loading...", preferredStyle: .Alert)

        let progressDownload : UIProgressView = UIProgressView(progressViewStyle: .Default)

        progressDownload.setProgress(5.0/10.0, animated: true)
        progressDownload.frame = CGRect(x: 10, y: 70, width: 250, height: 0)

    alertController.view.addSubview(progressDownload)
    presentViewController(alertController, animated: true, completion: nil)
}
2Cubed
  • 3,401
  • 7
  • 23
  • 40
Chea Sambath
  • 1,305
  • 2
  • 13
  • 16
  • I run the app and got this error *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Failed to determine navigation direction for scroll' *** First throw call stack: – Saeed Rahmatolahi Sep 06 '17 at 07:24