【问题标题】:"Please wait" dialog in iOS8iOS8 中的“请稍候”对话框
【发布时间】:2014-09-03 19:19:18
【问题描述】:

我曾经在我的应用程序中有一个“请稍候”对话框很长时间。使用UIActivityIndicatorView 并将其添加到UIAlertView 非常简单。

然而 iOS8 引入了UIAlertController。是否可以添加任何东西以产生类似的效果?有没有其他方法可以用 iOS8 做这样的事情?

我搜索了很多网站,但仍然不知道如何使用新 API 来完成。

我会很感激任何答案 - 库、教程等的链接,这可能会有所帮助。

问候,

马特乌斯

【问题讨论】:

标签: alert ios8 uiactivityindicatorview uialertcontroller


【解决方案1】:

您可以使用模态显示的自定义 UIViewController,而不是使用 UIAlertController。这是我在 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)

它看起来像这样:

演示控制器和动画过渡

请参阅此answer,了解使用UIViewControllerAnimatedTransitioning 重新创建UIAlertController 动画的示例实现。

【讨论】:

  • 其他解决方案快速简单,但不是很健壮或面向未来:您不能拥有message 微调器,您必须手动定位微调器(可以break in the future),用额外的换行符修改字符串等。这是最优雅的。
  • 加载一段时间后如何停止加载?
  • 如果让视图 = 查看为? ActivityView { view.activityIndi​​catorView.stopAnimating }
  • activitiyViewController.dismiss(animated: true) 将关闭进度视图控制器
  • 此代码对于更新的 Swift 版本已过期
【解决方案2】:

试试这个我做了一些技巧......

以下代码在 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];

【讨论】:

  • 已检查,实际上它可以工作 ;-) 经过一些改进后,它可以接受,所以回答被接受 :) 谢谢帮助。
  • 唯一的问题是你必须手动定位子视图。不理想,但它可以工作,并且看不到其他方式
  • @Jageen 我实现了这个,但我似乎无法让警报视图退出。有什么方法可以实现吗?
  • 您可以向 uialertcontroller 添加一个按钮,或遵循@Jageen 的建议并保留该文件的引用。我都做到了,而且都很完美。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    过了一段时间,我们需要隐藏警报。

    alertController.dismissViewControllerAnimated(true, completion: nil)
    

    【讨论】:

      【解决方案5】:

      如果您只想显示标题和活动指示器并且您喜欢冒险,您可以破解 AlertController 的视图层次结构。下面的代码适用于 8.2。然而,这通常不应该在生产中。

      正如下面的 cmets 所述,在您的应用中使用此代码可能会被拒绝,以下是文档摘录:

      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
      

      产生类似的东西:

      【讨论】:

      • 请注意_UIAlertControllerShadowedScrollView 是私密的,可能会导致应用被拒绝!
      【解决方案6】:

      与@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)
      

      【讨论】:

        【解决方案7】:

        仅使用基本 API 似乎是不可能的。我决定使用允许此类修改的 DTAlertView。它按我的意愿工作。

        https://github.com/Darktt/DTAlertView

        【讨论】:

        • 他们是如何实现DTAlertView的?没有 API!
        【解决方案8】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-08-16
          • 1970-01-01
          • 2012-07-29
          • 1970-01-01
          • 2012-09-12
          • 1970-01-01
          • 2011-08-10
          • 1970-01-01
          相关资源
          最近更新 更多