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
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:
Voir this answer pour un exemple d'implémentation qui recrée l'animation UIAlertController
à l'aide de UIViewControllerAnimatedTransitioning
.
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];
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);
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)
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:
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)
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.
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))
}
}