web-dev-qa-db-fra.com

"Veuillez patienter" dans iOS8

J'avais l'habitude d'avoir un "S'il vous plaît attendre" dialogue dans mon application depuis longtemps. C'était assez simple d'utiliser UIActivityIndicatorView et de l'ajouter à UIAlertView

Cependant, iOS8 a introduit UIAlertController. Est-il possible d’ajouter quoi que ce soit pour avoir un effet similaire? Y at-il une autre façon de faire une telle chose avec iOS8?

J'ai parcouru de nombreux sites et je ne sais toujours pas comment cela peut être fait avec la nouvelle API.

J'apprécierais toutes les réponses - liens vers des bibliothèques, des tutoriels, etc., qui pourraient être utiles. 

Cordialement,

Mateusz

15
karmel

Au lieu d'utiliser un UIAlertController, vous pouvez utiliser un UIViewController personnalisé présenté sous forme modale. Voici ce que j'utilise dans 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))
    }
}

Vous l'utilisez comme ceci:

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

Et cela ressemblera à ceci:

 ActivityViewController

Contrôleur de présentation et transition animée

Voir this answer pour un exemple d'implémentation qui recrée l'animation UIAlertController à l'aide de UIViewControllerAnimatedTransitioning

21
pheedsta

Essayez ceci j'ai fait un truc ...

Le code ci-dessous fonctionne pour moi sur 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

19
Jageen

Swift 3.0/4.1

Pour afficher la boîte de dialogue de progression:

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)

Pour fermer le dialogue de progression:

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

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)

}

Au bout d'un moment, il faut cacher l'alerte. 

alertController.dismissViewControllerAnimated(true, completion: nil)
6
A.G

Si vous souhaitez uniquement afficher le titre et l'indicateur d'activité et que vous vous sentez aventureux, vous pouvez pirater la hiérarchie des vues d'AlertController. Le code ci-dessous fonctionne sur 8.2. Cependant, cela ne devrait normalement pas être en production.

Comme il a été noté dans les commentaires ci-dessous, l'utilisation de ce code dans votre application peut vous faire rejeter, voici l'extrait de la documentation:

La classe UIAlertController est destinée à être utilisée telle quelle et ne prend pas en charge les sous-classes. La hiérarchie des vues pour cette classe est privée et ne doit pas être modifiée.

@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

Produit quelque chose comme ça:

UIAlertController with activity indicator

1
highmaintenance

Identique à @pheedsta mais changé pour fonctionner avec 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)
1
Doskii

Il semble impossible de le faire simplement à l'ancienne en utilisant uniquement des API de base. J'ai décidé d'utiliser DTAlertView qui permet de telles modifications. Cela fonctionne comme je voulais. 

https://github.com/Darktt/DTAlertView

0
karmel

Version Swift 3.1 du code @darksinge

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))
    }
}
0
pawisoon