Reputation: 1744
I currently have a UIAlertController
being displayed on the screen. The view of the alert should only display 2 elements, a title and a UIActivityIndicatorView
in the center of the alert. Below is the function that displays the alert and its elements.
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
}
However, the activity indicator doesn't display in the center of the view, in fact it displays in the bottom right of the screen, far off of the view. What is the reason for this?
EDIT: I understand that I can hardcode numbers for the indicator's position, but I want the alert to work on multiple devices with multiple screen sizes and orientations.
Upvotes: 27
Views: 46276
Reputation: 1954
If you want a ActivityIndicatorView only alert then try this.
func presentLoader() {
let alert = UIAlertController(title: nil, message: "", preferredStyle: .alert)
let activityIndicator = UIActivityIndicatorView(style: .large)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
activityIndicator.isUserInteractionEnabled = false
activityIndicator.color = .blue
activityIndicator.startAnimating()
alert.view.addSubview(activityIndicator)
NSLayoutConstraint.activate([
alert.view.heightAnchor.constraint(equalToConstant: 95),
alert.view.widthAnchor.constraint(equalToConstant: 95),
activityIndicator.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor),
activityIndicator.centerYAnchor.constraint(equalTo: alert.view.centerYAnchor)
])
present(alert, animated: true)
}
Result:
Upvotes: 1
Reputation: 12645
fully tested ...
extension UIViewController {
func verySimpleSpinner() -> UIAlertController {
let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
let spinner = UIActivityIndicatorView(style: .medium)
spinner.startAnimating()
alert.view.addSubview(spinner)
spinner.bindEdgesToSuperview()
present(alert, animated: true, completion: nil)
return alert
}
}
It's impossible to write iOS apps unless you have a simple .bindEdgesToSuperview()
call -
extension UIView {
func bindEdgesToSuperview() {
guard let s = superview else { preconditionFailure("flop") }
translatesAutoresizingMaskIntoConstraints = false
leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: s.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
}
}
If you do also want text, use the excellent code from @magnuskahr. Modernized:
fully tested ...
extension UIView {
func verySimpleSpinner() -> UIAlertController {
let alert = UIAlertController(title: "", message: "Connecting...", preferredStyle: .alert)
let spinner = UIActivityIndicatorView(style: .medium)
alert.view.addSubview(spinner)
alert.view.heightAnchor.constraint(equalToConstant: 95).isActive = true
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor, constant: 0).isActive = true
spinner.bottomAnchor.constraint(equalTo: alert.view.bottomAnchor, constant: -20).isActive = true
spinner.startAnimating()
present(alert, animated: true, completion: nil)
return alert
}
}
In any view controller:
let spinny = verySimpleSpinner()
when the connection/etc has finished:
spinny.dismiss(animated: true)
Upvotes: 1
Reputation: 16032
I have to implement NSLayoutConstraint
s to put the UIActivityIndicatorView
on the center of the UIAlertController
For 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)
Result:
Upvotes: 6
Reputation: 25246
All the other answers are off :) See documentation:
Important
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
The problem is not the UIAlertController. This is a very simple UI, a stackview or two depending if you want the UIActivityIndicatorView left to the title label or under the title. The presentation animation is what we want.
The code below is based on the WWDC session A Look Inside Presentation Controllers.
Recreate Presentation Controller:
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
)
}
}
Animated Transitioning:
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
}
}
Sample UIViewController
subclass, season to taste with 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
}
}
Credits for swift version: @riciloma
Recreate Presentation Controller:
@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
Animated Transitioning:
@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
Sample UIViewController
subclass, season to taste with 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: Make UIAlertController presentation controller and transitioning delegate public API to enable reuse.
Upvotes: 25
Reputation: 1320
Swift 5.0 solution
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)
Upvotes: 10
Reputation: 2076
How about this way for Swift 3 and higher:
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
}
Upvotes: -1
Reputation: 325
Converted @petesalt's answer to 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)
Upvotes: -1
Reputation: 6377
Be sure to set the frame property when you're creating a view.
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
}
To @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)
Upvotes: 39
Reputation: 1091
I had the same problem and using frame positioning didn't work for me. Yimin Lin's answer was very close for me, but I just wanted to present an alternative using constraints in non-visual format:
//...
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))
//...
Upvotes: -2
Reputation: 19
Apple does not encourage directly subclassing UIAlertController so I made a class that displays UIAlertController with centered UIActivityIndicator and handles the cancel condition with a class protocol.
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)
})
}
}
I recommend using lazy var to initialize the class.
lazy var busyAlertController: BusyAlert = {
let busyAlert = BusyAlert(title: "Lengthy Task", message: "Please wait...", presentingViewController: self)
busyAlert.delegate = self
return busyAlert
}()
Here is a link to sample code: https://github.com/cgilleeny/BusyAlertExample.git
Upvotes: 1
Reputation: 2446
In swift:
activityIndicator.center = self.view.center
If you have a tool bar or a navController you might want to shift the point but otherwise, center is center...
If you still have issues, perhaps this tutorial would help. If you are trying to center it in a table view controller, this answer might help.
Upvotes: 0
Reputation: 477
I converted the answer to Objective C, if anyone is interested:
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];
Cheers
Upvotes: 29
Reputation: 5542
For those like me who prefer UIActivityIndicatorView
aligned at the left of the UIAlertController.title
, this is my solution in Swift working for all devices:
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);
However, to align the UIActivityIndicatorView
in the view center you can change as follows:
activityIndicator.center = CGPoint(x: (alert.view.bounds.width)/2, y: (alert.view.bounds.height)/2)
activityIndicator.autoresizingMask = .FlexibleLeftMargin | .FlexibleRightMargin | .FlexibleTopMargin | .FlexibleBottomMargin
Upvotes: 1
Reputation: 6302
Try this:
activityView.center = CGPointMake(self.view.bounds.size.width/2.0, self.view.bounds.size.height / 2.0)
Also you will need to check for landscape mode and reverse width and height.
if(landscapeMode)activityView.center = CGPointMake(self.view.bounds.size.height/2.0, self.view.bounds.size.width / 2.0)
Maybe you can get the alert view position?
alert.view.frame.origin.x
alert.view.frame.origin.y
and use that to place your activity view dynamically ie with the variables?
Of course you might also want to get the size divide by 2 and add that so that its centred as well.
alert.view.frame.size.height
alert.view.frame.size.width
Upvotes: -2
Reputation: 9346
Well try this code.
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];
Upvotes: -2