“Please wait” dialog in iOS8

浪子不回头ぞ 提交于 2019-12-17 22:16:26

问题


I used to have a "Please wait" dialog in my app for long time. It was quite simple thing using UIActivityIndicatorView and adding it to UIAlertView.

However iOS8 introduced UIAlertController. Is it possible to add anything to it to have similiar effect? Is there another way of doing such thing with iOS8?

I have searched a lot of sites and still have no idea how it can be done with the new API.

I would appreciate any answers - links to libs, tutorials etc., which could be helpful.

Regards,

Mateusz


回答1:


Instead of using a UIAlertController, you can use a custom UIViewController that is presented modally. Here is what I use in Swift 2.0:

class ActivityViewController: UIViewController {

    private let activityView = ActivityView()

    init(message: String) {
        super.init(nibName: nil, bundle: nil)
        modalTransitionStyle = .CrossDissolve
        modalPresentationStyle = .OverFullScreen
        activityView.messageLabel.text = message
        view = activityView
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

private class ActivityView: UIView {

    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
    let boundingBoxView = UIView(frame: CGRectZero)
    let messageLabel = UILabel(frame: CGRectZero)

    init() {
        super.init(frame: CGRectZero)

        backgroundColor = UIColor(white: 0.0, alpha: 0.5)

        boundingBoxView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        boundingBoxView.layer.cornerRadius = 12.0

        activityIndicatorView.startAnimating()

        messageLabel.font = UIFont.boldSystemFontOfSize(UIFont.labelFontSize())
        messageLabel.textColor = UIColor.whiteColor()
        messageLabel.textAlignment = .Center
        messageLabel.shadowColor = UIColor.blackColor()
        messageLabel.shadowOffset = CGSizeMake(0.0, 1.0)
        messageLabel.numberOfLines = 0

        addSubview(boundingBoxView)
        addSubview(activityIndicatorView)
        addSubview(messageLabel)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        boundingBoxView.frame.size.width = 160.0
        boundingBoxView.frame.size.height = 160.0
        boundingBoxView.frame.origin.x = ceil((bounds.width / 2.0) - (boundingBoxView.frame.width / 2.0))
        boundingBoxView.frame.origin.y = ceil((bounds.height / 2.0) - (boundingBoxView.frame.height / 2.0))

        activityIndicatorView.frame.origin.x = ceil((bounds.width / 2.0) - (activityIndicatorView.frame.width / 2.0))
        activityIndicatorView.frame.origin.y = ceil((bounds.height / 2.0) - (activityIndicatorView.frame.height / 2.0))

        let messageLabelSize = messageLabel.sizeThatFits(CGSizeMake(160.0 - 20.0 * 2.0, CGFloat.max))
        messageLabel.frame.size.width = messageLabelSize.width
        messageLabel.frame.size.height = messageLabelSize.height
        messageLabel.frame.origin.x = ceil((bounds.width / 2.0) - (messageLabel.frame.width / 2.0))
        messageLabel.frame.origin.y = ceil(activityIndicatorView.frame.origin.y + activityIndicatorView.frame.size.height + ((boundingBoxView.frame.height - activityIndicatorView.frame.height) / 4.0) - (messageLabel.frame.height / 2.0))
    }
}

You use it like this:

let activitiyViewController = ActivityViewController(message: "Loading...")
presentViewController(activitiyViewController, animated: true, completion: nil)

And it will look like this:

Presentation Controller and Animated Transitioning

See this answer for a sample implementation that recreates the UIAlertController animation using UIViewControllerAnimatedTransitioning.




回答2:


Try this I done some trick...

Below code is working for me in iPod iOS8beta5 + XCode6

UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
                                        message:@"Please wait\n\n\n"
                                 preferredStyle:UIAlertControllerStyleAlert];

    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    spinner.center = CGPointMake(130.5, 65.5);
    spinner.color = [UIColor blackColor];
    [spinner startAnimating];
    [alert.view addSubview:spinner];
    [self presentViewController:alert animated:NO completion:nil];




回答3:


Swift 3.0/4.1

To show the progress dialog:

let alertController = UIAlertController(title: nil, message: "Please wait\n\n", preferredStyle: .alert)

let spinnerIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)

spinnerIndicator.center = CGPoint(x: 135.0, y: 65.5)
spinnerIndicator.color = UIColor.black
spinnerIndicator.startAnimating()

alertController.view.addSubview(spinnerIndicator)
self.present(alertController, animated: false, completion: nil)

To dismiss the progress dialog:

alertController.dismiss(animated: true, completion: nil);



回答4:


Swift 2.0:

 override func viewDidAppear(animated: Bool)
 {

  let alertController = UIAlertController(title: nil, message: "Please wait\n\n", preferredStyle: UIAlertControllerStyle.Alert)

  let spinnerIndicator: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)

  spinnerIndicator.center = CGPointMake(135.0, 65.5)
  spinnerIndicator.color = UIColor.blackColor()
  spinnerIndicator.startAnimating()

  alertController.view.addSubview(spinnerIndicator)
  self.presentViewController(alertController, animated: false, completion: nil)

}

After some point, we need to hide the alert.

alertController.dismissViewControllerAnimated(true, completion: nil)



回答5:


If you only want to display title and activity indicator and you feel adventurous, you can hack AlertController's view hierarchy. The code below works on 8.2. However this normally should not be in production.

As it's been noted in comments below, using this code in your app may get you rejected, here is the excerpt from documentation:

The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.

@implementation AlertControllerWithActivityIndicator

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    UIView *scrollView = [self findViewByClassPrefix:@"_UIAlertControllerShadowedScrollView" inView:self.view];
    UIView *containerView = [scrollView.subviews firstObject];
    UILabel *titleLabel = containerView.subviews.firstObject;

    if(!titleLabel) {
        return;
    }

    if(!self.indicatorView) {
        self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        self.indicatorView.translatesAutoresizingMaskIntoConstraints = NO;
        [containerView addSubview:self.indicatorView];
        NSDictionary *views = @{ @"text": titleLabel, @"indicator": self.indicatorView };

        NSArray *constraints = [scrollView constraintsAffectingLayoutForAxis:UILayoutConstraintAxisVertical];
        for(NSLayoutConstraint *constraint in constraints) {
            if(constraint.firstItem == containerView && constraint.secondItem == titleLabel && constraint.firstAttribute == NSLayoutAttributeBottom) {
                constraint.active = NO;
            }
        }

        [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[text]-[indicator]-24-|" options:0 metrics:nil views:views]];
        [containerView addConstraint:[NSLayoutConstraint constraintWithItem:self.indicatorView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

        [self.indicatorView startAnimating];
    }
}

- (UIView *)findViewByClassPrefix:(NSString *)prefix inView:(UIView *)view {
    for(UIView *subview in view.subviews) {
        if([NSStringFromClass(subview.class) hasPrefix:prefix]) {
            return subview;
        }

        UIView *child = [self findViewByClassPrefix:prefix inView:subview];
        if(child) {
            return child;
        }
    }
    return nil;
}

@end

Produces something like that:




回答6:


Same as @pheedsta but changed to work with Swift 4

import UIKit

class ActivityViewController: UIViewController {

    private let activityView = ActivityView()

    init(message: String) {
        super.init(nibName: nil, bundle: nil)
        modalTransitionStyle = .crossDissolve
        modalPresentationStyle = .overFullScreen
        activityView.messageLabel.text = message
        view = activityView
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

private class ActivityView: UIView {

    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
    let boundingBoxView = UIView(frame: CGRect.zero)
    let messageLabel = UILabel(frame: CGRect.zero)

    init() {
        super.init(frame: CGRect.zero)

        backgroundColor = UIColor(white: 0.0, alpha: 0.5)

        boundingBoxView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        boundingBoxView.layer.cornerRadius = 12.0

        activityIndicatorView.startAnimating()

        messageLabel.font = UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)
        messageLabel.textColor = UIColor.white
        messageLabel.textAlignment = .center
        messageLabel.shadowColor = UIColor.black
        messageLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
        messageLabel.numberOfLines = 0

        addSubview(boundingBoxView)
        addSubview(activityIndicatorView)
        addSubview(messageLabel)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        boundingBoxView.frame.size.width = 160.0
        boundingBoxView.frame.size.height = 160.0
        boundingBoxView.frame.origin.x = ceil((bounds.width / 2.0) - (boundingBoxView.frame.width / 2.0))
        boundingBoxView.frame.origin.y = ceil((bounds.height / 2.0) - (boundingBoxView.frame.height / 2.0))

        activityIndicatorView.frame.origin.x = ceil((bounds.width / 2.0) - (activityIndicatorView.frame.width / 2.0))
        activityIndicatorView.frame.origin.y = ceil((bounds.height / 2.0) - (activityIndicatorView.frame.height / 2.0))

        let messageLabelSize = messageLabel.sizeThatFits(CGSize(width: 160.0 - 20.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
        messageLabel.frame.size.width = messageLabelSize.width
        messageLabel.frame.size.height = messageLabelSize.height
        messageLabel.frame.origin.x = ceil((bounds.width / 2.0) - (messageLabel.frame.width / 2.0))
        messageLabel.frame.origin.y = ceil(activityIndicatorView.frame.origin.y + activityIndicatorView.frame.size.height + ((boundingBoxView.frame.height - activityIndicatorView.frame.height) / 4.0) - (messageLabel.frame.height / 2.0))
    }
}

Usage:

// Initiate somewhere
let activitiyViewController = ActivityViewController(message: "Loading...")
// To start/show
present(activitiyViewController, animated: true, completion: nil)
// To stop/dissmiss
activitiyViewController.dismiss(animated: true)



回答7:


It seems to be impossible to do it plain old way using only basic API. I have decided to use DTAlertView which allows such modifications. It works as I wanted.

https://github.com/Darktt/DTAlertView




回答8:


Swift 3.1 version of @darksinge code

import UIKit

class ActivityViewController: UIViewController {

    private let activityView = ActivityView()

    init(message: String) {
        super.init(nibName: nil, bundle: nil)
        modalTransitionStyle = .crossDissolve
        modalPresentationStyle = .overFullScreen
        activityView.messageLabel.text = message
        view = activityView
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

private class ActivityView: UIView {

    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
    let boundingBoxView = UIView(frame: CGRect.zero)
    let messageLabel = UILabel(frame: CGRect.zero)

    init() {
        super.init(frame: CGRect.zero)

        backgroundColor = UIColor(white: 0.0, alpha: 0.5)

        boundingBoxView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        boundingBoxView.layer.cornerRadius = 12.0

        activityIndicatorView.startAnimating()

        messageLabel.font = UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)
        messageLabel.textColor = UIColor.white
        messageLabel.textAlignment = .center
        messageLabel.shadowColor = UIColor.black
        messageLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
        messageLabel.numberOfLines = 0

        addSubview(boundingBoxView)
        addSubview(activityIndicatorView)
        addSubview(messageLabel)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        boundingBoxView.frame.size.width = 160.0
        boundingBoxView.frame.size.height = 160.0
        boundingBoxView.frame.origin.x = ceil((bounds.width / 2.0) - (boundingBoxView.frame.width / 2.0))
        boundingBoxView.frame.origin.y = ceil((bounds.height / 2.0) - (boundingBoxView.frame.height / 2.0))

        activityIndicatorView.frame.origin.x = ceil((bounds.width / 2.0) - (activityIndicatorView.frame.width / 2.0))
        activityIndicatorView.frame.origin.y = ceil((bounds.height / 2.0) - (activityIndicatorView.frame.height / 2.0))

        let messageLabelSize = messageLabel.sizeThatFits(CGSize(width:160.0 - 20.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
        messageLabel.frame.size.width = messageLabelSize.width
        messageLabel.frame.size.height = messageLabelSize.height
        messageLabel.frame.origin.x = ceil((bounds.width / 2.0) - (messageLabel.frame.width / 2.0))
        messageLabel.frame.origin.y = ceil(activityIndicatorView.frame.origin.y + activityIndicatorView.frame.size.height + ((boundingBoxView.frame.height - activityIndicatorView.frame.height) / 4.0) - (messageLabel.frame.height / 2.0))
    }
}


来源:https://stackoverflow.com/questions/25652101/please-wait-dialog-in-ios8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!