Пожалуйста, подождите диалоговое окно в iOS8

Раньше в моем приложении долгое время появлялось диалоговое окно "Подождите". Это было довольно просто, используя UIActivityIndicatorView и добавляя его к UIAlertView.

Однако iOS8 представила UIAlertController. Можно ли к нему что-нибудь добавить, чтобы получить аналогичный эффект? Есть ли другой способ сделать это с iOS8?

Я перерыл множество сайтов и до сих пор не знаю, как это можно сделать с помощью нового API.

Буду признателен за любые ответы - ссылки на библиотеки, учебные пособия и т. д., которые могут быть полезны.

С уважением,

Матеуш


person matrejek    schedule 03.09.2014    source источник
comment
Ответ здесь stackoverflow.com/questions/27033466/   -  person rukiman    schedule 24.02.2015


Ответы (8)


Вместо использования UIAlertController вы можете использовать настраиваемый UIViewController, который представлен модально. Вот что я использую в 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))
    }
}

Вы используете это так:

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

И это будет выглядеть так:

ActivityViewController

Контроллер презентации и анимированный переход

См. этот ответ для примера реализации, воссоздающей анимацию UIAlertController с использованием UIViewControllerAnimatedTransitioning.

person pheedsta    schedule 06.11.2015
comment
Другие решения просты и быстры, но не очень надежны и не рассчитаны на будущее: у вас не может быть счетчика message и, вам нужно вручную позиционировать счетчик (может сломаться в будущем), взломать строки с помощью дополнительные символы новой строки и т. д. Это самое элегантное. - person Nicolas Miari; 18.01.2016
comment
как остановить загрузку через некоторое время загрузки? - person Mahmudul Haque Khan; 04.02.2016
comment
если разрешить просмотр = просмотр как? ActivityView { view.activityIndicatorView.stopAnimating } - person pheedsta; 05.02.2016
comment
activitiyViewController.dismiss(animated: true) закроет контроллер представления прогресса - person Alessandro Muzzi; 05.05.2017
comment
Этот код устарел для более поздних версий Swift. - person CSchwarz; 12.08.2019

Попробуйте, я проделал кое-какую хитрость...

Ниже код работает для меня в 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];

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

person Jageen    schedule 04.09.2014
comment
Проверено, и на самом деле это работает ;-) После некоторых улучшений это может быть приемлемо, поэтому ответ принят :) Спасибо за помощь. - person matrejek; 05.09.2014
comment
Единственная проблема заключается в том, что вам нужно вручную позиционировать подвид. Не идеально, но работает, и другого выхода нет. - person StuStirling; 15.11.2014
comment
@Jageen Я реализовал это, но не могу заставить окно предупреждения выйти. Есть ли способ реализовать это? - person user1871869; 26.04.2015
comment
вы можете добавить кнопку в uialertcontroller или последовать совету @Jageen и сохранить ссылку на этот файл. Я сделал оба, и оба работают идеально. - person Septronic; 09.10.2015

Свифт 3.0/4.1

Чтобы отобразить диалоговое окно прогресса:

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)

Чтобы закрыть диалоговое окно прогресса:

alertController.dismiss(animated: true, completion: nil);
person Mahmoud Fayez    schedule 06.01.2017

Свифт 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)

}

Через какой-то момент нам нужно скрыть оповещение.

alertController.dismissViewControllerAnimated(true, completion: nil)
person A.G    schedule 02.03.2016

Если вы хотите отображать только заголовок и индикатор активности и чувствуете себя авантюрно, вы можете взломать иерархию представлений AlertController. Код ниже работает на 8.2. Однако обычно этого не должно быть в производстве.

Как было отмечено в комментариях ниже, использование этого кода в вашем приложении может привести к отклонению, вот выдержка из документации:

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

@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

Выдает что-то вроде этого:

UIAlertController с индикатором активности

person Ben Affleck    schedule 30.03.2015
comment
Обратите внимание, что _UIAlertControllerShadowedScrollView является частным и может привести к отклонению приложения! - person Simon; 20.06.2016

То же, что и @pheedsta, но изменено для работы со 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))
    }
}

Использование:

// Initiate somewhere
let activitiyViewController = ActivityViewController(message: "Loading...")
// To start/show
present(activitiyViewController, animated: true, completion: nil)
// To stop/dissmiss
activitiyViewController.dismiss(animated: true)
person Doskii    schedule 10.04.2018

Кажется, невозможно сделать это по-старому, используя только базовый API. Я решил использовать DTAlertView, который позволяет такие модификации. Он работает так, как я хотел.

https://github.com/Darktt/DTAlertView

person matrejek    schedule 04.09.2014
comment
как они реализовали DTAlertView? без API! - person Sazzad Hissain Khan; 12.01.2017

Swift 3.1 версия кода @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))
    }
}
person pawisoon    schedule 04.08.2017