alert ios8 uiactivityindicatorview uialertcontroller

alert - Diálogo "Espere por favor" en iOS8



uiactivityindicatorview uialertcontroller (7)

Solía ​​tener un cuadro de diálogo "Por favor espere" en mi aplicación durante mucho tiempo. Era bastante simple usar UIActivityIndicatorView y agregarlo a UIAlertView .

Sin embargo iOS8 introdujo UIAlertController . ¿Es posible agregarle algo para tener un efecto similar? ¿Hay alguna otra forma de hacerlo con iOS8?

He buscado en muchos sitios y aún no tengo idea de cómo se puede hacer con la nueva API.

Agradecería cualquier respuesta: enlaces a libs, tutoriales, etc., que podrían ser útiles.

Saludos,

Mateusz


En lugar de usar un UIAlertController, puede usar un UIViewController personalizado que se presenta de manera modal. Esto es lo que uso en 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)) } }

Lo usas así:

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

Y se verá así:


Parece imposible hacerlo de la manera antigua usando solo API básica. Decidí usar DTAlertView que permite tales modificaciones. Funciona como yo quería.

https://github.com/Darktt/DTAlertView


Si solo desea mostrar el título y el indicador de actividad y se siente aventurero, puede piratear la jerarquía de vistas de AlertController. El siguiente código funciona en 8.2. Sin embargo, esto normalmente no debería estar en producción.

Como se señaló en los comentarios a continuación, el uso de este código en su aplicación puede hacer que lo rechacen, aquí está el extracto de la documentación:

La clase UIAlertController está diseñada para ser usada tal como está y no es compatible con la creación de subclases. La jerarquía de vista para esta clase es privada y no debe modificarse.

@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

Produce algo así:


Versión Swift 3.1 del código @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)) } }


Prueba esto, hice un truco ...

El código siguiente funciona para mí en 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 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) }

Después de algún punto, necesitamos ocultar la alerta.

alertController.dismissViewControllerAnimated(true, completion: nil)


Swift 3.0

Para mostrar el diálogo de progreso:

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)

Para descartar el diálogo de progreso:

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