“Please wait” dialog in iOS8

后端 未结 8 1620
离开以前
离开以前 2020-12-13 08:14

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

相关标签:
8条回答
  • 2020-12-13 08:23

    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)
    
    0 讨论(0)
  • 2020-12-13 08:27

    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.

    0 讨论(0)
  • 2020-12-13 08:29

    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)
    
    0 讨论(0)
  • 2020-12-13 08:31

    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

    0 讨论(0)
  • 2020-12-13 08:32

    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];
    

    enter image description here

    0 讨论(0)
  • 2020-12-13 08:32

    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:

    UIAlertController with activity indicator

    0 讨论(0)
提交回复
热议问题