web-dev-qa-db-fra.com

Comment afficher l'indicateur d'activité au centre de UIAlertController?

J'ai actuellement un UIAlertController affiché à l'écran. La vue de l'alerte ne doit afficher que 2 éléments, un titre et un UIActivityIndicatorView au centre de l'alerte. Vous trouverez ci-dessous la fonction qui affiche l'alerte et ses éléments.

func displaySignUpPendingAlert() -> UIAlertController {
        //Create the UIAlertController
        let pending = UIAlertController(title: "Creating New User", message: nil, preferredStyle: .Alert)
        //Create the activity indicator to display in it.
        let indicator = UIActivityIndicatorView(frame: CGRectMake(pending.view.frame.width / 2.0, pending.view.frame.height / 2.0, 20.0, 20.0))
        indicator.center = CGPointMake(pending.view.frame.width / 2.0, pending.view.frame.height / 2.0)
        //Add the activity indicator to the alert's view
        pending.view.addSubview(indicator)
        //Start animating
        indicator.startAnimating()

        self.presentViewController(pending, animated: true, completion: nil)
        return pending
    }

Cependant, l'indicateur d'activité ne s'affiche pas au centre de la vue, en fait il s'affiche en bas à droite de l'écran, loin de la vue. Quelle est la raison pour ça?

EDIT: Je comprends que je peux coder en dur les numéros pour la position de l'indicateur, mais je veux que l'alerte fonctionne sur plusieurs appareils avec plusieurs tailles et orientations d'écran.

21
Satre

Veillez à définir la propriété du cadre lorsque vous créez une vue.

func displaySignUpPendingAlert() -> UIAlertController {
        //create an alert controller
        let pending = UIAlertController(title: "Creating New User", message: nil, preferredStyle: .Alert)

        //create an activity indicator
        let indicator = UIActivityIndicatorView(frame: pending.view.bounds)
        indicator.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        //add the activity indicator as a subview of the alert controller's view
        pending.view.addSubview(indicator)
        indicator.isUserInteractionEnabled = false // required otherwise if there buttons in the UIAlertController you will not be able to press them
        indicator.startAnimating()

        self.presentViewController(pending, animated: true, completion: nil)

        return pending
}

À @ 62Shark:

let pending = UIAlertController(title: "Creating New User", message: nil, preferredStyle: .Alert)

let indicator = UIActivityIndicatorView()
indicator.setTranslatesAutoresizingMaskIntoConstraints(false)
pending.view.addSubview(indicator)

let views = ["pending" : pending.view, "indicator" : indicator]
var constraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[indicator]-(-50)-|", options: nil, metrics: nil, views: views)
constraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|[indicator]|", options: nil, metrics: nil, views: views)
pending.view.addConstraints(constraints)

indicator.userInteractionEnabled = false
indicator.startAnimating()

self.presentViewController(pending, animated: true, completion: nil)
33
ylin0x81

J'ai converti la réponse en objectif C, si quelqu'un est intéressé:

UIAlertController *pending = [UIAlertController alertControllerWithTitle:nil
                                                               message:@"Please wait...\n\n"
                                                        preferredStyle:UIAlertControllerStyleAlert];
UIActivityIndicatorView* indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.color = [UIColor blackColor];
indicator.translatesAutoresizingMaskIntoConstraints=NO;
[pending.view addSubview:indicator];
NSDictionary * views = @{@"pending" : pending.view, @"indicator" : indicator};

NSArray * constraintsVertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[indicator]-(20)-|" options:0 metrics:nil views:views];
NSArray * constraintsHorizontal = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[indicator]|" options:0 metrics:nil views:views];
NSArray * constraints = [constraintsVertical arrayByAddingObjectsFromArray:constraintsHorizontal];
[pending.view addConstraints:constraints];
[indicator setUserInteractionEnabled:NO];
[indicator startAnimating];
[self presentViewController:pending animated:YES completion:nil];

À votre santé

26
Dimitris F.

tl; dr

Toutes les autres réponses sont désactivées :) Voir la documentation:

Important

La classe UIAlertController est destinée à être utilisée en l'état et ne prend pas en charge le sous-classement. La hiérarchie de vue pour cette classe est privée et ne doit pas être modifiée .

Problème

Le problème n'est pas le UIAlertController. Il s'agit d'une interface utilisateur très simple, d'une vue de pile ou de deux, selon que vous souhaitez laisser UIActivityIndicatorView à l'étiquette de titre ou sous le titre. L'animation de présentation est ce que nous voulons.

Le code ci-dessous est basé sur la session WWDC A Look Inside Presentation Controllers .

Recréer le contrôleur de présentation:

@interface LOActivityAlertControllerPresentationController : UIPresentationController
@end

@interface LOActivityAlertControllerPresentationController ()
@property (nonatomic) UIView *dimmerView;
@end

@implementation LOActivityAlertControllerPresentationController

- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController
{
    self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
    if (self)
    {
        _dimmerView = [[UIView alloc] init];
        _dimmerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        _dimmerView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];


        UIView *presentedView = [self presentedView];
        presentedView.layer.cornerRadius = 8.0;

        UIInterpolatingMotionEffect *centerXMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
        centerXMotionEffect.minimumRelativeValue = @(-10.0);
        centerXMotionEffect.maximumRelativeValue = @(10.0);

        UIInterpolatingMotionEffect *centerYMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
        centerYMotionEffect.minimumRelativeValue = @(-10.0);
        centerYMotionEffect.maximumRelativeValue = @(10.0);

        UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
        group.motionEffects = [NSArray arrayWithObjects:centerXMotionEffect, centerYMotionEffect, nil];

        [presentedView addMotionEffect:group];
    }
    return self;

}

- (CGRect)frameOfPresentedViewInContainerView
{
    UIView *containerView = [self containerView];
    UIView *presentedView = [self presentedView];

    CGSize size = [presentedView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
    CGRect frame = CGRectZero;
    frame.Origin = CGPointMake(CGRectGetMidX([containerView frame]) - (size.width / 2.0),
                               CGRectGetMidY([containerView frame]) - (size.height / 2.0));
    frame.size = size;

    return frame;
}

- (void)presentationTransitionWillBegin
{
    UIViewController *presentingViewController = [self presentingViewController];
    UIView *containerView = [self containerView];
    UIView *presentedView = [self presentedView];
    UIView *dimmerView = [self dimmerView];

    dimmerView.alpha = 0.0;
    dimmerView.frame = [containerView bounds];
    [containerView insertSubview:dimmerView atIndex:0];

    presentedView.center = [containerView center];

    [[presentingViewController transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        dimmerView.alpha = 1.0;

    } completion:NULL];
}

- (void)containerViewWillLayoutSubviews
{
    [super containerViewWillLayoutSubviews];

    UIView *containerView = [self containerView];
    UIView *presentedView = [self presentedView];
    UIView *dimmerView = [self dimmerView];

    dimmerView.frame = [containerView bounds];
    presentedView.frame = [self frameOfPresentedViewInContainerView];
}

- (void)dismissalTransitionWillBegin
{
    UIViewController *presentingViewController = [self presentingViewController];
    UIView *dimmerView = [self dimmerView];

    [[presentingViewController transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        dimmerView.alpha = 0.0;

    } completion:NULL];
}


@end

Transition animée:

@interface LOActivityAlertControllerAnimatedTransitioning : NSObject <UIViewControllerAnimatedTransitioning>

@property (getter=isPresentation) BOOL presentation;

@end

@implementation LOActivityAlertControllerAnimatedTransitioning

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext
{
    UIView *containerView = [transitionContext containerView];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    if (_presentation)
    {
        [containerView addSubview:toView];
        toView.transform = CGAffineTransformMakeScale(1.6, 1.6);
        toView.alpha = 0.0;
        [UIView animateWithDuration:0.2 animations:^{

            toView.alpha = 1.0;
            toView.transform = CGAffineTransformIdentity;

        } completion:^(BOOL finished) {

            [transitionContext completeTransition:YES];

        }];
    }
    else
    {
        [UIView animateWithDuration:0.2 animations:^{

            fromView.alpha = 0.0;

        } completion:^(BOOL finished) {

            [fromView removeFromSuperview];
            [transitionContext completeTransition:YES];

        }];
    }
}

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.2;
}

@end

Exemple de sous-classe UIViewController, assaisonner au goût avec XIB:

@interface LOActivityAlertController : UIViewController <UIViewControllerTransitioningDelegate>

@property (nonatomic, strong) IBOutlet UIActivityIndicatorView *activityIndicatorView;
@property (nonatomic, strong) IBOutlet UILabel *titleLabel;

@end

@implementation LOActivityAlertController

@dynamic title;

+ (instancetype)alertControllerWithTitle:(NSString *)title
{
    LOActivityAlertController *alert = [LOActivityAlertController new];
    alert.title = title;
    return alert;
}

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        self.transitioningDelegate = self;
        self.modalPresentationStyle = UIModalPresentationCustom;
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.titleLabel.text = self.title;
}

#pragma mark Properties

- (void)setTitle:(NSString *)title
{
    [super setTitle:title];

    self.titleLabel.text = title;
}

#pragma mark UIViewControllerTransitioningDelegate

- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented
                                                      presentingViewController:(UIViewController *)presenting
                                                          sourceViewController:(UIViewController *)source
{
    LOActivityAlertControllerPresentationController *myPresentation = nil;
    myPresentation = [[LOActivityAlertControllerPresentationController alloc]
                      initWithPresentedViewController:presented presentingViewController:presenting];

    return myPresentation;
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
{
    LOActivityAlertControllerAnimatedTransitioning *transitioning = [LOActivityAlertControllerAnimatedTransitioning new];
    transitioning.presentation = YES;
    return transitioning;
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    LOActivityAlertControllerAnimatedTransitioning *transitioning = [LOActivityAlertControllerAnimatedTransitioning new];
    return transitioning;
}

@end

Enregistrement d'écran

enter image description here

Reporter un bogue

rdar: // 37433306 : Rendre le contrôleur de présentation UIAlertController et l'API publique déléguée de transition pour permettre la réutilisation.

16
catlan

solution Swift 5.

let alert = UIAlertController(title: "Sender ...", message: nil, preferredStyle: .alert)
let activityIndicator = UIActivityIndicatorView(style: .gray)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
activityIndicator.isUserInteractionEnabled = false
activityIndicator.startAnimating()

alert.view.addSubview(activityIndicator)
alert.view.heightAnchor.constraint(equalToConstant: 95).isActive = true

activityIndicator.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor, constant: 0).isActive = true
activityIndicator.bottomAnchor.constraint(equalTo: alert.view.bottomAnchor, constant: -20).isActive = true

present(alert, animated: true)
3
magnuskahr

Apple n'encourage pas directement la sous-classe UIAlertController, j'ai donc créé une classe qui affiche UIAlertController avec UIActivityIndicator centré et gère la condition d'annulation avec un protocole de classe.

import Foundation
import UIKit

protocol BusyAlertDelegate {
    func didCancelBusyAlert()
}


class BusyAlert {

   var busyAlertController: UIAlertController?
   var presentingViewController: UIViewController?
   var activityIndicator: UIActivityIndicatorView?
   var delegate:BusyAlertDelegate?

   init (title:String, message:String, presentingViewController: UIViewController) {
       busyAlertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
       busyAlertController!.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel Button"), style: UIAlertActionStyle.Cancel, handler:{(alert: UIAlertAction!) in
            delegate?.didCancelBusyAlert()
    }))
        self.presentingViewController = presentingViewController
        activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
        busyAlertController!.view.addSubview(activityIndicator!)
    }

    func display() {
        dispatch_async(dispatch_get_main_queue(), {
               self.presentingViewController!.presentViewController(self.busyAlertController!, animated: true, completion: {
            self.activityIndicator!.translatesAutoresizingMaskIntoConstraints = false
               self.busyAlertController!.view.addConstraint(NSLayoutConstraint(item: self.activityIndicator!, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.busyAlertController!.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0))
            self.busyAlertController!.view.addConstraint(NSLayoutConstraint(item: self.activityIndicator!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.busyAlertController!.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
            self.activityIndicator!.startAnimating()

        })
    })

}

func dismiss() {
    dispatch_async(dispatch_get_main_queue(), {
        self.busyAlertController?.dismissViewControllerAnimated(true, completion: nil)
    })
}

}

Je recommande d'utiliser lazy var pour initialiser la classe.

lazy var busyAlertController: BusyAlert = {
        let busyAlert = BusyAlert(title: "Lengthy Task", message: "Please     wait...", presentingViewController: self)
        busyAlert.delegate = self
        return busyAlert
        }()

Voici un lien vers un exemple de code: https://github.com/cgilleeny/BusyAlertExample.git

1
Caroline Gilleeny

Pour ceux comme moi qui préfèrent UIActivityIndicatorView aligné à gauche du UIAlertController.title, voici ma solution en Swift fonctionnant pour tous les appareils:

let alert = UIAlertController(title: NSLocalizedString("Authenticating...", comment: "Authenticating"), message: nil, preferredStyle: .Alert);
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
activityIndicator.frame = activityIndicator.frame.rectByOffsetting(dx: 8, dy: (alert.view.bounds.height - activityIndicator.frame.height)/2);
activityIndicator.autoresizingMask = .FlexibleRightMargin | .FlexibleTopMargin | .FlexibleBottomMargin
activityIndicator.color = themeManager().currentTheme.navigationBarTintColor;
activityIndicator.startAnimating();
alert.view.addSubview(activityIndicator);
self.presentViewController(progressAlert, animated: true, completion: nil);

Cependant, pour aligner le UIActivityIndicatorView dans le centre de vue, vous pouvez modifier comme suit:

activityIndicator.center = CGPoint(x: (alert.view.bounds.width)/2, y: (alert.view.bounds.height)/2)
activityIndicator.autoresizingMask = .FlexibleLeftMargin | .FlexibleRightMargin | .FlexibleTopMargin | .FlexibleBottomMargin
1
Francesco Vadicamo

Je dois implémenter NSLayoutConstraints pour mettre le UIActivityIndicatorView au centre du UIAlertController

Pour Swift :

let loadingAlertController: UIAlertController = UIAlertController(title: "Loading", message: nil, preferredStyle: .alert)
let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView(style: .gray)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false

loadingAlertController.view.addSubview(activityIndicator)

let xConstraint: NSLayoutConstraint = NSLayoutConstraint(item: activityIndicator, attribute: .centerX, relatedBy: .equal, toItem: loadingAlertController.view, attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint: NSLayoutConstraint = NSLayoutConstraint(item: activityIndicator, attribute: .centerY, relatedBy: .equal, toItem: loadingAlertController.view, attribute: .centerY, multiplier: 1.4, constant: 0)

NSLayoutConstraint.activate([ xConstraint, yConstraint])
activityIndicator.isUserInteractionEnabled = false
activityIndicator.startAnimating()

let height: NSLayoutConstraint = NSLayoutConstraint(item: loadingAlertController.view, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 80)
loadingAlertController.view.addConstraint(height);

self.present(loadingAlertController, animated: true, completion: nil)

Résultat:

enter image description here

0
pableiros

Dans Swift:

activityIndicator.center = self.view.center

Si vous avez une barre d'outils ou un navController, vous voudrez peut-être déplacer le point, mais sinon, le centre est le centre ...

Si vous avez toujours des problèmes, peut-être que le tutoriel this pourrait vous aider. Si vous essayez de le centrer dans un contrôleur de vue de table, la réponse this pourrait aider.

0
goggelj