【问题标题】:How to display activity indicator in center of UIAlertController?如何在 UIAlertController 的中心显示活动指示器?
【发布时间】:2015-01-17 22:51:24
【问题描述】:

我目前在屏幕上显示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
    }

但是,活动指示器不会显示在视图的中心,实际上它显示在屏幕的右下方,远离视图。这是什么原因?

编辑:我知道我可以对指示器位置的数字进行硬编码,但我希望警报能够在具有多种屏幕尺寸和方向的多台设备上工作。

【问题讨论】:

标签: ios swift uiview uiactivityindicatorview uialertcontroller


【解决方案1】:

我必须实现NSLayoutConstraints 将UIActivityIndicatorView 放在UIAlertController 的中心

对于斯威夫特

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)

结果:

【讨论】:

    【解决方案2】:

    tl;博士

    所有其他答案都已关闭:) 请参阅文档:

    重要

    UIAlertController 类旨在按原样使用,而不是 支持子类化。此类的视图层次结构是私有的,并且 不得修改

    问题

    问题不在于 UIAlertController。这是一个非常简单的 UI,一个或两个堆栈视图,具体取决于您是否希望 UIActivityIndi​​catorView 留在标题标签或标题下方。演示动画是我们想要的。

    以下代码基于 WWDC 会话 A Look Inside Presentation Controllers

    斯威夫特

    重新创建演示控制器:

    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 以启用重用。

    【讨论】:

    • 这是唯一正确的答案。不要破解私人视图层次结构。有趣的是,SO 现在是一个标记为“已接受”的错误答案,然后是 Swift 和 Objective-C 的不同版本中的 500 个“我也是”克隆,但只有 @catlan 说明了这些黑客将如何在 未来 UIKit 的版本。
    • @bpapa 更有趣的是,你用与 OP 要求的不同的编程语言来赞美答案。
    • 我已经用 Swift 翻译了这段代码 here
    • Swift 代码是错误的,您将看到的只是一个暗淡的视图。 guard 语句应该分开,每个语句都应该在 isPresentation is else 内。 fromView 视图总是 nil isPresentation,反之亦然。
    • 感谢您的回答!似乎这是一个好方法❤️但是当前的示例(Swift)对我不起作用。决定用UIActivityIndicatorView创建一个简单的UIViewController,并使用默认的过渡风格。
    【解决方案3】:

    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)
    

    【讨论】:

      【解决方案4】:

      对于 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
      }
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          请务必在创建视图时设置 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)
          

          【讨论】:

          • 感谢您的帮助。但是我遇到了麻烦,您的激活器位于中心,并带有这样的警告消息标题 -> puu.sh/ilFpD/d8e1f98d8f.png。请问有什么方法可以将激活器放在警报标题的下方位置吗?谢谢。
          • 嗨@62Shark,我更新了我的答案,希望这会对你有所帮助。
          • Swipesight,它完全符合我现在想要的样子,先生。谢谢您的回答。
          • 如果您在警报控制器的消息末尾添加几个 \n 换行符,它将扩大它的高度,以使微调器不再遮挡文本,而是显示在标题下方并且应有的消息。
          • 这是一个糟糕的答案,根本无法证明未来。按照@catlan 的建议去做:stackoverflow.com/a/48730050/543
          【解决方案7】:

          我遇到了同样的问题,使用框架定位对我不起作用。 Yimin Lin 的回答对我来说非常接近,但我只是想提出一种使用非可视格式约束的替代方案:

          //...
          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))
          //...
          

          【讨论】:

            【解决方案8】:

            Apple 不鼓励直接将 UIAlertController 子类化,因此我创建了一个显示 UIAlertController 的类,其中 UIActivityIndi​​cator 居中,并使用类协议处理取消条件。

            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 busyAlertController: BusyAlert = {
                    let busyAlert = BusyAlert(title: "Lengthy Task", message: "Please     wait...", presentingViewController: self)
                    busyAlert.delegate = self
                    return busyAlert
                    }()
            

            这里是示例代码的链接:https://github.com/cgilleeny/BusyAlertExample.git

            【讨论】:

              【解决方案9】:

              迅速:

              activityIndicator.center = self.view.center
              

              如果您有工具栏或导航控制器,您可能想要移动点,否则,中心就是中心...

              如果您仍有问题,也许this 教程会有所帮助。如果您尝试将其置于表格视图控制器中,this 答案可能会有所帮助。

              【讨论】:

                【解决方案10】:

                如果有人感兴趣,我将答案转换为 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];
                

                干杯

                【讨论】:

                • 谢谢,我喜欢 Swift,但我仍然在Objective-C 中编写大部分应用程序,这很有帮助。
                • 你能解释一下限制吗?
                • 工作完美。此代码在数据加载完成后关闭。 [self dismissViewControllerAnimated:NO completion:nil];
                【解决方案11】:

                对于像我这样喜欢将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
                

                【讨论】:

                  【解决方案12】:

                  试试这个:

                  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
                  

                  【讨论】:

                  • 警报显示在中心,但活动指示器靠近屏幕底部,几乎在角落。
                  • 哦,对不起,我的错,我以为你的意思是警报视图不在中心。只需将活动视图居中到主屏幕,它就应该在它的顶部。
                  【解决方案13】:

                  试试这段代码。

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

                  【讨论】:

                  • 我不想这样做的原因是它使用了硬编码的数字,我希望它适用于多种屏幕尺寸
                  • 你可以添加这个 [loader setCenter:view.center];
                  • 这会编译吗?这行 [alert.view loader] 在做什么?
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-01-25
                  • 1970-01-01
                  • 2016-08-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多