Как отобразить индикатор активности в центре UIAlertController?

У меня сейчас на экране отображается UIAlertController. В представлении предупреждения должны отображаться только 2 элемента, заголовок и UIActivityIndicatorView в центре предупреждения. Ниже представлена ​​функция, отображающая предупреждение и его элементы.

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
    }

Однако индикатор активности не отображается в центре представления, на самом деле он отображается в правом нижнем углу экрана, далеко за пределами представления. Что является причиной этого?

РЕДАКТИРОВАТЬ: Я понимаю, что могу жестко запрограммировать числа для положения индикатора, но я хочу, чтобы предупреждение работало на нескольких устройствах с несколькими размерами экрана и ориентацией.


person Satre    schedule 20.11.2014    source источник
comment
По теме: Как мне поместить UIActivityIndicatorView в UIAlertController ?   -  person Ky Leggiero    schedule 15.07.2015


Ответы (13)


Обязательно установите свойство frame при создании представления.

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)
person ylin0x81    schedule 20.11.2014
comment
Спасибо за вашу помощь. Но у меня проблема из-за того, что ваш активатор находится в центре с таким заголовком предупреждающего сообщения - ›puu.sh/ilFpD/d8e1f98d8f.png. есть ли способ поставить активатор в нижнюю позицию заголовка предупреждения, пожалуйста? спасибо. - person Thiha Aung; 12.06.2015
comment
Привет @ 62Shark, я обновил свой ответ и надеюсь, что это поможет вам. - person ylin0x81; 13.06.2015
comment
Swipesight, это выглядит так, как я хотел, сэр. Спасибо за ваш ответ. - person Thiha Aung; 15.06.2015
comment
Вы также можете помочь мне здесь, если не возражаете. У меня проблема с вызовом моего настраиваемого оповещения из другого контроллера представления. Вот моя ссылка: stackoverflow.com/ questions / 30837238 / - person Thiha Aung; 15.06.2015
comment
Используя решение @ 62Shark, UIActivityIndicatorView выровнено слева от экрана iPad и немного ниже UIAlertController. i.imgur.com/ehy8BdD.png - person Ky Leggiero; 13.07.2015
comment
Я думаю, вы установили еще один индикатор активатора в раскадровке. Когда вы используете это предупреждение, нет необходимости устанавливать еще один индикатор активатора в раскадровке вашего контроллера представления. - person Thiha Aung; 14.07.2015
comment
@ 62Shark Я не использовал раскадровку; Все это делал программно. Вот мой полный вопрос по этому поводу: stackoverflow.com/questions/31408319/ - person Ky Leggiero; 14.07.2015
comment
Если вы добавите несколько \ n новых строк в конец сообщения контроллера предупреждений, он достаточно увеличит его высоту, чтобы счетчик больше не закрывал текст, а появлялся под заголовком и сообщением, как и должно. - person Doug Voss; 28.10.2017
comment
Как это отклонить? - person Faizan Mubasher; 01.02.2018
comment
Это плохой ответ, который вовсе не является доказательством будущего. Делайте то, что предлагает @catlan: stackoverflow.com/a/48730050/543 - person bpapa; 11.02.2018
comment
@FaizanMubasher за увольнение UIAlertController,, вам нужно только написать это: yourAlert.dismiss(animated: true, completion: nil) - person iGhost; 07.09.2019

Я преобразовал ответ в Objective C, если кому интересно:

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

Ваше здоровье

person Dimitris F.    schedule 11.09.2015
comment
Спасибо, мне нравится Swift, но я все еще пишу большинство своих приложений на Objective-C, это очень помогает. - person Supertecnoboff; 17.03.2018
comment
Не могли бы вы объяснить ограничения? - person arniotaki; 20.04.2018
comment
работать отлично. этот код следует закрыть после завершения загрузки данных. [самоотключениеViewControllerAnimated: НЕТ завершения: ноль]; - person jack; 11.08.2018

tl;dr

Все остальные ответы отключены :) См. Документацию:

Важно

Класс UIAlertController предназначен для использования «как есть» и не поддерживает создание подклассов. Иерархия представлений для этого класса является частной и не должна изменяться.

Проблема

Проблема не в UIAlertController. Это очень простой пользовательский интерфейс, один или два вида стека в зависимости от того, хотите ли вы, чтобы UIActivityIndicatorView оставался на метке заголовка или под заголовком. Анимация презентации - это то, что нам нужно.

Приведенный ниже код основан на контроллерах презентаций изнутри сеанса WWDC.

Быстрый

Воссоздать контроллер презентации:

class LOActivityAlertControllerPresentationController: UIPresentationController {
    
    var dimmerView: UIView!
    
    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        self.dimmerView = UIView()
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        dimmerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        dimmerView.backgroundColor = UIColor.init(white: 0, alpha: 0.4)
        
        guard let presentedView = self.presentedView else { return }
        presentedView.layer.cornerRadius = 8.0
        
        let centerXMotionEffect: UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
        centerXMotionEffect.minimumRelativeValue = -10.0
        centerXMotionEffect.maximumRelativeValue = 10.0
        
        let centerYMotionEffect: UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
        centerYMotionEffect.minimumRelativeValue = -10.0
        centerYMotionEffect.maximumRelativeValue = 10.0
        
        let group: UIMotionEffectGroup = UIMotionEffectGroup()
        group.motionEffects = [centerXMotionEffect, centerYMotionEffect]
        
        presentedView.addMotionEffect(group)
    }
    
    override var frameOfPresentedViewInContainerView: CGRect {
        guard let containerView = self.containerView, let presentedView = self.presentedView else { return .zero }
        
        let size = presentedView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        var frame = CGRect.zero
        
        frame.origin = CGPoint(x: containerView.frame.midX - (size.width / 2.0), y: containerView.frame.midY - (size.height / 2.0))
        
        frame.size = size
        
        return frame
    }
    
    override func presentationTransitionWillBegin() {
        guard let containerView: UIView = self.containerView, let presentedView: UIView = self.presentedView, let dimmerView = self.dimmerView else { return }
        let presentingViewController: UIViewController = self.presentingViewController
        
        dimmerView.alpha = 0.0
        dimmerView.frame = containerView.bounds
        containerView.insertSubview(dimmerView, at: 0)
        
        presentedView.center = containerView.center
        
        guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
        
        transitionCoordinator.animate(
            alongsideTransition: { _ in
                dimmerView.alpha = 1.0
            },
            completion: nil
        )
    }
    
    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        
        guard let containerView: UIView = self.containerView, let presentedView: UIView = self.presentedView, let dimmerView = self.dimmerView else { return }
        
        dimmerView.frame = containerView.bounds
        presentedView.frame = self.frameOfPresentedViewInContainerView
    }
    
    override func dismissalTransitionWillBegin() {
        guard let dimmerView = self.dimmerView, let transitionCoordinator = self.presentingViewController.transitionCoordinator else { return }
        
        transitionCoordinator.animate(
            alongsideTransition: { _ in
                dimmerView.alpha = 0.0
            },
            completion: nil
        )
    }

}

Анимированный переход:

class LOActivityAlertControllerAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
    
    var presentation: Bool
    
    init(presentation: Bool) {
        self.presentation = presentation
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView
        guard let fromView = transitionContext.view(forKey: .from), let toView = transitionContext.view(forKey: .to) else { return }
        if self.presentation {
            containerView.addSubview(toView)
            toView.transform = CGAffineTransform(scaleX: 1.6, y: 1.6)
            toView.alpha = 0.0
            UIView.animate(
                withDuration: 0.2,
                animations: {
                    toView.alpha = 1.0
                    toView.transform = .identity
                },
                completion: { finished in
                    transitionContext.completeTransition(true)
                }
            )
        } else {
            UIView.animate(
                withDuration: 0.2,
                animations: {
                    fromView.alpha = 0.0
                },
                completion: { finished in
                    fromView.removeFromSuperview()
                    transitionContext.completeTransition(true)
                }
            )
        }
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.2
    }
    
}

Пример подкласса UIViewController, приправить по вкусу XIB:

class LOActivityAlertController: UIViewController, UIViewControllerTransitioningDelegate {
    
    var activityIndicatorView: UIActivityIndicatorView!
    var titleLabel: UILabel!
    var messageLabel: UILabel!
    
    var alertTitle: String
    var alertMessage: String
    
    init(title: String, message: String) {
        self.alertTitle = title
        self.alertMessage = message
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("Not implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.transitioningDelegate = self
        self.modalPresentationStyle = .custom
        self.titleLabel = UILabel()
        self.messageLabel = UILabel()
        self.titleLabel.text = self.alertTitle
        self.messageLabel.text = self.alertMessage
        
        self.activityIndicatorView = UIActivityIndicatorView(style: .medium)
        
        let currentFrame = self.view.frame
        let alertFrame = CGRect(x: 0, y: 0, width: currentFrame.width / 2.0, height: currentFrame.height / 2.0)
        
        let stackView = UIStackView(frame: alertFrame)
        stackView.backgroundColor = .gray
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.distribution = .fillProportionally
        stackView.addArrangedSubview(self.titleLabel)
        stackView.addArrangedSubview(self.messageLabel)
        stackView.addArrangedSubview(self.activityIndicatorView)
        
        self.activityIndicatorView.startAnimating()
        
        self.view.addSubview(stackView)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        
    }
    
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let presentationController = LOActivityAlertControllerPresentationController(presentedViewController: presented, presenting: presenting)
        return presentationController
    }
    
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let transitioning = LOActivityAlertControllerAnimatedTransitioning(presentation: true)
        return transitioning
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let transitioning = LOActivityAlertControllerAnimatedTransitioning(presentation: false)
        return transitioning
    }
}

Кредиты для быстрой версии: @riciloma

Цель-C

Воссоздать контроллер презентации:

@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

Анимированный переход:

@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

Пример подкласса UIViewController, приправить по вкусу 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

Запись экрана

введите описание изображения здесь

Сообщение об ошибках

rdar: // 37433306: сделайте контроллер представления UIAlertController и общедоступный API делегата перехода, чтобы разрешить повторное использование.

person catlan    schedule 11.02.2018
comment
Это единственно правильный ответ. Не взламывайте иерархии частных представлений. Забавно, что SO теперь плохой ответ, помеченный как принятый, а затем 500 меня тоже клонируют в разных версиях Swift и Objective-C, но только @catlan иллюстрирует, как хаки будут ломаться в будущих версиях UIKit . - person bpapa; 11.02.2018
comment
@bpapa более забавным является тот факт, что вы хвалите ответ на другом языке программирования, чем спрашивает OP. - person J. Doe; 14.10.2018
comment
Я перевел этот код на Swift здесь - person riciloma; 18.10.2020
comment
Код Swift неверен, и все, что вы увидите, - это нечеткое изображение. Операторы guard должны быть разделены, и каждый должен находиться внутри isPresentation is else. fromView вид всегда будет nil, когда isPresentation, и наоборот. - person Raphael; 08.03.2021

Мне нужно реализовать NSLayoutConstraints, чтобы поместить UIActivityIndicatorView в центр UIAlertController

Для 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)

Результат:

введите описание изображения здесь

person pableiros    schedule 13.11.2016

Решение Swift 5.0

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)
person magnuskahr    schedule 29.05.2019

Для тех, кто, как я, предпочитает UIActivityIndicatorView, выровненный слева от UIAlertController.title, это мое решение в Swift, работающее на всех устройствах:

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

Однако, чтобы выровнять UIActivityIndicatorView в центре просмотра, вы можете изменить следующее:

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

Apple не поощряет создание подклассов UIAlertController напрямую, поэтому я создал класс, который отображает UIAlertController с центрированным UIActivityIndicator и обрабатывает условие отмены с помощью протокола класса.

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

}

Я рекомендую использовать lazy var для инициализации класса.

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

Вот ссылка на образец кода: https://github.com/cgilleeny/BusyAlertExample.git

person Caroline Gilleeny    schedule 12.10.2015

Быстро:

activityIndicator.center = self.view.center

Если у вас есть панель инструментов или navController, вы можете сместить точку, но в противном случае центр будет центром ...

Если у вас все еще есть проблемы, возможно, вам поможет это руководство. Если вы пытаетесь центрировать его в контроллере табличного представления, это ответ может помочь.

person zevij    schedule 27.09.2015

Преобразованный ответ @petesalt на Swift 3:

let pending = UIAlertController(title: "Saving, please wait...", message: nil, preferredStyle: .alert)

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

let views = ["pending" : pending.view, "indicator" : indicator]

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

indicator.isUserInteractionEnabled = false
indicator.startAnimating()

self.present(pending, animated: true, completion: nil)
person Hajji Daoud    schedule 28.05.2017

Как насчет этого для Swift 3 и выше:

func showActivityIndiactorViewController(title: String) -> UIAlertController {
    let pending = UIAlertController(title: "", message: nil, preferredStyle: .alert)
    let heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: pending.view, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: self.view.frame.height * 0.10)
    pending.view.addConstraint(heightConstraint)

    let label = UILabel()
    label.text = title
    label.textColor = UIColor.black
    label.sizeToFit()

    let space = UIView(frame: CGRect(x: 0, y: 0, width: 8, height: 8))

    let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
    indicator.isUserInteractionEnabled = false
    indicator.startAnimating()

    let width = Int(label.frame.size.width + indicator.frame.size.width + space.frame.size.width)

    let view = UIStackView(arrangedSubviews: [indicator, space, label])
    view.axis = .horizontal
    view.frame = CGRect(x: 20, y: 0, width: width, height: Int(heightConstraint.constant))
    pending.view.addSubview(view)

    let widthConstraint:NSLayoutConstraint = NSLayoutConstraint(item: pending.view, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: view, attribute: NSLayoutAttribute.width, multiplier: 1, constant: CGFloat(width))
    pending.view.addConstraint(widthConstraint)

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

    return pending
}
person blyabtroi    schedule 16.06.2018

Хорошо попробуйте этот код.

UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
                                    message:@"Creating new user\n\n\n"
                             preferredStyle:UIAlertControllerStyleAlert];

UIActivityIndicatorView *loader = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
loader.center = CGPointMake(130.5, 65.5);
loader.color = [UIColor blackColor];
[loader startAnimating];
[alert.view loader];
[self presentViewController:alert animated:NO completion:nil];
person iAnurag    schedule 20.11.2014
comment
Причина, по которой я не хочу этого делать, заключается в том, что здесь используются жестко запрограммированные числа, и я хочу, чтобы он работал для нескольких размеров экрана. - person Satre; 20.11.2014
comment
ну вы можете добавить этот [загрузчик setCenter: view.center]; - person iAnurag; 20.11.2014
comment
Будет ли это компилироваться? Что делает эта строка [загрузчик alert.view]? - person Gruntcakes; 23.06.2017

Попробуй это:

activityView.center = CGPointMake(self.view.bounds.size.width/2.0, self.view.bounds.size.height / 2.0)

Также вам нужно будет проверить альбомный режим и перевернуть ширину и высоту.

if(landscapeMode)activityView.center = CGPointMake(self.view.bounds.size.height/2.0, self.view.bounds.size.width / 2.0)

Может, вы сможете получить позицию просмотра предупреждений?

alert.view.frame.origin.x
alert.view.frame.origin.y

и использовать это для динамического размещения вашего вида деятельности, то есть с переменными?

Конечно, вы также можете захотеть получить размер, разделенный на 2, и добавить его, чтобы он тоже был по центру.

alert.view.frame.size.height
alert.view.frame.size.width
person AMAN77    schedule 20.11.2014
comment
Предупреждение отображается в центре, но индикатор активности находится в нижней части экрана, почти в углу. - person Satre; 20.11.2014
comment
Ой, извините, плохо, я думал, вы имели в виду, что тревожный вид не в центре. Просто отцентрируйте вид активности на главном экране, и он должен быть поверх него. - person AMAN77; 20.11.2014

У меня была та же проблема, и использование позиционирования кадра у меня не сработало. Ответ Иминь Линя был мне очень близок, но я просто хотел представить альтернативу с использованием ограничений в невизуальном формате:

//...
indicator.setTranslatesAutoresizingMaskIntoConstraints(false)
alert.view.addSubview(indicator)

alert.view.addConstraint(NSLayoutConstraint(item: indicator, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: alert.view, attribute: attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0))
alert.view.addConstraint(NSLayoutConstraint(item: indicator, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: alert.view, attribute: attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
//...
person mad_manny    schedule 23.10.2015