I have been making search bars without navigation on the imageView.
The search bar height is fixed but i want to change the search bar height.
so i tried
let frame = CGRect(x: 0, y: 0, width: 100, height: 44)
searchbar.frame = frame
and
searchbar.heightAnchor.constraint(equalToConstant: 200).isActive = true
but they don't work.
I'm using this code
searchBar.isTranslucent = true
searchBar.searchBarStyle = .minimal
so like this
please help me change the search bar textfield height.
fileprivate func customizeSearchField(){
UISearchBar.appearance().setSearchFieldBackgroundImage(UIImage(), for: .normal)
searchField.backgroundColor = .white
if let searchTextField = searchField.value(forKey: "searchField") as? UITextField {
searchTextField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
searchTextField.heightAnchor.constraint(equalToConstant: 50),
searchTextField.leadingAnchor.constraint(equalTo: searchField.leadingAnchor, constant: 10),
searchTextField.trailingAnchor.constraint(equalTo: searchField.trailingAnchor, constant: -10),
searchTextField.centerYAnchor.constraint(equalTo: searchField.centerYAnchor, constant: 0)
])
searchTextField.clipsToBounds = true
searchTextField.font = GenericUtility.getOpenSansRegularFontSize(14)
searchTextField.layer.cornerRadius = 4.0
searchTextField.layer.borderWidth = 1.0
searchTextField.layer.borderColor = AppColor.primaryLightGray.cgColor
}
}
try this!
for myView in searchBars.subviews {
for mySubView in myView.subviews {
if let textField = mySubView as? UITextField {
var bounds: CGRect
bounds = textField.frame
bounds.size.height = 40 //(set your height)
textField.bounds = bounds
textField.borderStyle = UITextBorderStyle.RoundedRect
}
}
}
Try this:
searchBar.frame.size.height = 44
if you want to use with interface builder:
class MediaSearchBar: UISearchBar {
override func layoutSubviews() {
}
}
and setup it in viewDidLoad:
func setupSearchBar() {
searchBar.delegate = self
for myView in searchBar.subviews {
for mySubView in myView.subviews {
if let textField = mySubView as? UITextField {
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
textField.heightAnchor.constraint(equalTo: myView.heightAnchor,
multiplier: 1.0, constant: -20.0),
textField.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 10.0),
textField.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: -10.0),
textField.centerYAnchor.constraint(equalTo: myView.centerYAnchor, constant: 0.0)
])
textField.clipsToBounds = true
}
}
}
}
I'm trying to add views in UIStackView programmatically.
For now My code is:
UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 100, 100)];
UIView *view2 = [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];
[self.stack1 addArrangedSubview:view1];
[self.stack1 addArrangedSubview:view2];
When i deploy the app, there is only 1 view and it is with black colour.(view1 get the parameters for view2 too)
Stack views use intrinsic content size, so use layout constraints to define the dimensions of the views.
There is an easy way to add constraints quickly (example):
[view1.heightAnchor constraintEqualToConstant:100].active = true;
Complete Code:
- (void) setup {
//View 1
UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor blueColor];
[view1.heightAnchor constraintEqualToConstant:100].active = true;
[view1.widthAnchor constraintEqualToConstant:120].active = true;
//View 2
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor greenColor];
[view2.heightAnchor constraintEqualToConstant:100].active = true;
[view2.widthAnchor constraintEqualToConstant:70].active = true;
//View 3
UIView *view3 = [[UIView alloc] init];
view3.backgroundColor = [UIColor magentaColor];
[view3.heightAnchor constraintEqualToConstant:100].active = true;
[view3.widthAnchor constraintEqualToConstant:180].active = true;
//Stack View
UIStackView *stackView = [[UIStackView alloc] init];
stackView.axis = UILayoutConstraintAxisVertical;
stackView.distribution = UIStackViewDistributionEqualSpacing;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.spacing = 30;
[stackView addArrangedSubview:view1];
[stackView addArrangedSubview:view2];
[stackView addArrangedSubview:view3];
stackView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:stackView];
//Layout for Stack View
[stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = true;
[stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = true;
}
Note: This was tested on iOS 9
Swift 5.0
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi World"
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.vertical
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
Based on #user1046037 answer.
In Swift 4.2
let redView = UIView()
redView.backgroundColor = .red
let blueView = UIView()
blueView.backgroundColor = .blue
let stackView = UIStackView(arrangedSubviews: [redView, blueView])
stackView.axis = .vertical
stackView.distribution = .fillEqually
view.addSubview(stackView)
// stackView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
// autolayout constraint
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor),
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.heightAnchor.constraint(equalToConstant: 200)
])
UIStackView uses constraints internally to position its arranged subviews. Exactly what constraints are created depends on how the stack view itself is configured. By default, a stack view will create constraints that lay out its arranged subviews in a horizontal line, pinning the leading and trailing views to its own leading and trailing edges. So your code would produce a layout that looks like this:
|[view1][view2]|
The space that is allocated to each subview is determined by a number of factors including the subview's intrinsic content size and it's compression resistance and content hugging priorities. By default, UIView instances don't define an intrinsic content size. This is something that is generally provided by a subclass, such as UILabel or UIButton.
Since the content compression resistance and content hugging priorities of two new UIView instances will be the same, and neither view provides an intrinsic content size, the layout engine must make its best guess as to what size should be allocated to each view. In your case, it is assigning the first view 100% of the available space, and nothing to the second view.
If you modify your code to use UILabel instances instead, you will get better results:
UILabel *label1 = [UILabel new];
label1.text = #"Label 1";
label1.backgroundColor = [UIColor blueColor];
UILabel *label2 = [UILabel new];
label2.text = #"Label 2";
label2.backgroundColor = [UIColor greenColor];
[self.stack1 addArrangedSubview:label1];
[self.stack1 addArrangedSubview:label2];
Note that it is not necessary to explictly create any constraints yourself. This is the main benefit of using UIStackView - it hides the (often ugly) details of constraint management from the developer.
You have to set you distribution type.
In your code, juste add:
self.stack1.distribution = UIStackViewDistributionFillEqually;
Or you can set the distribution directly in your interface builder.
For example:
Hope that helps ;)
Lapinou.
Following two lines fixed my issue
view.heightAnchor.constraintEqualToConstant(50).active = true;
view.widthAnchor.constraintEqualToConstant(350).active = true;
Swift version -
var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
self.view.addSubview(DynamicView)
DynamicView.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView.widthAnchor.constraintEqualToConstant(350).active = true;
var DynamicView2=UIView(frame: CGRectMake(100, 310, 100, 100))
DynamicView2.backgroundColor=UIColor.greenColor()
DynamicView2.layer.cornerRadius=25
DynamicView2.layer.borderWidth=2
self.view.addSubview(DynamicView2)
DynamicView2.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView2.widthAnchor.constraintEqualToConstant(350).active = true;
var DynamicView3:UIView=UIView(frame: CGRectMake(10, 420, 355, 100))
DynamicView3.backgroundColor=UIColor.greenColor()
DynamicView3.layer.cornerRadius=25
DynamicView3.layer.borderWidth=2
self.view.addSubview(DynamicView3)
let yourLabel:UILabel = UILabel(frame: CGRectMake(110, 10, 200, 20))
yourLabel.textColor = UIColor.whiteColor()
//yourLabel.backgroundColor = UIColor.blackColor()
yourLabel.text = "mylabel text"
DynamicView3.addSubview(yourLabel)
DynamicView3.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView3.widthAnchor.constraintEqualToConstant(350).active = true;
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Vertical
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 30
stackView.addArrangedSubview(DynamicView)
stackView.addArrangedSubview(DynamicView2)
stackView.addArrangedSubview(DynamicView3)
stackView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true
For the accepted answer when you try to hide any view inside stack view, the constraint works not correct.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x618000086e50 UIView:0x7fc11c4051c0.height == 120 (active)>",
"<NSLayoutConstraint:0x610000084fb0 'UISV-hiding' UIView:0x7fc11c4051c0.height == 0 (active)>"
)
Reason is when hide the view in stackView it will set the height to 0 to animate it.
Solution change the constraint priority as below.
import UIKit
class ViewController: UIViewController {
let stackView = UIStackView()
let a = UIView()
let b = UIView()
override func viewDidLoad() {
super.viewDidLoad()
a.backgroundColor = UIColor.red
a.widthAnchor.constraint(equalToConstant: 200).isActive = true
let aHeight = a.heightAnchor.constraint(equalToConstant: 120)
aHeight.isActive = true
aHeight.priority = 999
let bHeight = b.heightAnchor.constraint(equalToConstant: 120)
bHeight.isActive = true
bHeight.priority = 999
b.backgroundColor = UIColor.green
b.widthAnchor.constraint(equalToConstant: 200).isActive = true
view.addSubview(stackView)
stackView.backgroundColor = UIColor.blue
stackView.addArrangedSubview(a)
stackView.addArrangedSubview(b)
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Just add a button in xib file or storyboard and add connect this action.
#IBAction func test(_ sender: Any) {
a.isHidden = !a.isHidden
}
}
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blueColor()
imageView.heightAnchor.constraintEqualToConstant(120.0).active = true
imageView.widthAnchor.constraintEqualToConstant(120.0).active = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.greenColor()
textLabel.widthAnchor.constraintEqualToConstant(self.view.frame.width).active = true
textLabel.heightAnchor.constraintEqualToConstant(20.0).active = true
textLabel.text = "Hi World"
textLabel.textAlignment = .Center
//Third View
let thirdView = UIImageView()
thirdView.backgroundColor = UIColor.magentaColor()
thirdView.heightAnchor.constraintEqualToConstant(120.0).active = true
thirdView.widthAnchor.constraintEqualToConstant(120.0).active = true
thirdView.image = UIImage(named: "buttonFollowMagenta")
//Stack View
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Vertical
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.addArrangedSubview(thirdView)
stackView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true
Improved on the answer by #Oleg Popov
Swift 5 version of Oleg Popov's answer, which is based on user1046037's answer
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi World"
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.vertical
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
In my case the thing that was messing with I would expect was that I was missing this line:
stackView.translatesAutoresizingMaskIntoConstraints = false;
After that no need to set constraints to my arranged subviews whatsoever, the stackview is taking care of that.
If you have many UIViews to add, you can use the following extension where you pass an array of UIViews where it will add them into the UIStackView by order
extension UIStackView {
func addArrangedSubviews(_ subviews: [UIView]) {
subviews.forEach{ self.addArrangedSubview($0) }
}
}
If you have want to add UIView at specific position, use this
yourStackView.insertArrangedSubview(yourView, at: index)
I just came across very similar problem. Just like mentioned before the stack view's dimensions depend one intrinsic content size of the arranged subviews. Here is my solution in Swift 2.x and following view structure:
view - UIView
customView - CustomView:UIView
stackView - UISTackView
arranged subviews - custom UIView subclasses
//: [Previous](#previous)
import Foundation
import UIKit
import XCPlayground
/**Container for stack view*/
class CustomView:UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init(){
super.init(frame: CGRectZero)
}
}
/**Custom Subclass*/
class CustomDrawing:UIView{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
// self.backgroundColor = UIColor.clearColor()
print("setup \(frame)")
}
override func drawRect(rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(ctx, 0, 0)
CGContextAddLineToPoint(ctx, CGRectGetWidth(bounds), CGRectGetHeight(bounds))
CGContextStrokePath(ctx)
print("DrawRect")
}
}
//: [Next](#next)
let stackView = UIStackView()
stackView.distribution = .FillProportionally
stackView.alignment = .Center
stackView.axis = .Horizontal
stackView.spacing = 10
//container view
let view = UIView(frame: CGRectMake(0,0,320,640))
view.backgroundColor = UIColor.darkGrayColor()
//now custom view
let customView = CustomView()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.widthAnchor.constraintEqualToConstant(220).active = true
customView.heightAnchor.constraintEqualToConstant(60).active = true
customView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
customView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
customView.backgroundColor = UIColor.lightGrayColor()
//add a stack view
customView.addSubview(stackView)
stackView.centerXAnchor.constraintEqualToAnchor(customView.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(customView.centerYAnchor).active = true
stackView.translatesAutoresizingMaskIntoConstraints = false
let c1 = CustomDrawing()
c1.translatesAutoresizingMaskIntoConstraints = false
c1.backgroundColor = UIColor.redColor()
c1.widthAnchor.constraintEqualToConstant(30).active = true
c1.heightAnchor.constraintEqualToConstant(30).active = true
let c2 = CustomDrawing()
c2.translatesAutoresizingMaskIntoConstraints = false
c2.backgroundColor = UIColor.blueColor()
c2.widthAnchor.constraintEqualToConstant(30).active = true
c2.heightAnchor.constraintEqualToConstant(30).active = true
stackView.addArrangedSubview(c1)
stackView.addArrangedSubview(c2)
XCPlaygroundPage.currentPage.liveView = view
Instead of coding all the constrains you could use a subclass that handles .intrinsicContentSize of UIView class in a simpler way.
This solution improves also Interface Builder a little in a way to support with "intrinsicWidth" and "intrinsicHeight" of views. While you could extend UIView's and have those properties available on all UIViews in IB its cleaner to subclass.
// IntrinsicView.h
#import UIKit
IB_DESIGNABLE
#interface IntrinsicView : UIView
-(instancetype)initWithFrame:(CGRect)rect;
#property IBInspectable CGSize intrinsic;
#end
// IntrinsicView.m
#import "IntrinsicView.h"
#implementation IntrinsicView {
CGSize _intrinsic;
}
- (instancetype)initWithFrame:(CGRect)frame {
_intrinsic = frame.size;
if ( !(self = [super initWithFrame:frame]) ) return nil;
// your stuff here..
return self;
}
-(CGSize)intrinsicContentSize {
return _intrinsic;
}
-(void)prepareForInterfaceBuilder {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, _intrinsic.width,_intrinsic.height);
}
#end
Which means you can just allocate those IntrinsicView's and the self.frame.size is taken as intrinsicContentSize. That way it does not disturb the normal layout and you dont need to set constraint relations that don't even apply in full with UIStackViews
#import "IntrinsicView.h"
- (void)viewDidLoad {
[super viewDidLoad];
UIStackView *column = [[UIStackView alloc] initWithFrame:self.view.frame];
column.spacing = 2;
column.alignment = UIStackViewAlignmentFill;
column.axis = UILayoutConstraintAxisVertical; //Up-Down
column.distribution = UIStackViewDistributionFillEqually;
for (int row=0; row<5; row++) {
//..frame:(CGRect) defines here proportions and
//relation to axis of StackView
IntrinsicView *intrinsicView = [[IntrinsicView alloc] initWithFrame:CGRectMake(0, 0, 30.0, 30.0)];
[column addArrangedSubview:intrinsicView];
}
[self.view addSubview:column];
}
now you can go crazy with UIStackView's
or in swift + encoding, decoding, IB support, Objective-C support
#IBDesignable #objc class IntrinsicView : UIView {
#IBInspectable var intrinsic : CGSize
#objc override init(frame: CGRect) {
intrinsic = frame.size
super.init(frame: frame)
}
required init?(coder: NSCoder) {
intrinsic = coder.decodeCGSize(forKey: "intrinsic")
super.init(coder: coder)
}
override func encode(with coder: NSCoder) {
coder.encode(intrinsic, forKey: "intrinsic")
super.encode(with: coder)
}
override var intrinsicContentSize: CGSize {
return intrinsic
}
override func prepareForInterfaceBuilder() {
frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: intrinsic.width, height: intrinsic.height)
}
}
It is really not recommended to set a height constraint... If you can, never, never, never set a height! You need to check all the constraints of the views inside your UIStackView and be sure that there is constraints for bottom, top, leading and trailing. Someone said to me: it is like a guy pushing on walls. If he don't push on 4 sides, one of the wall will fall on him.
func configureHorizontalView(){
containerView.addSubview(horizontalStackView)
_ = horizontalStackView.anchor(top: secondCurrencyTextField.bottomAnchor,
left: containerView.leftAnchor,
bottom: nil,
right: containerView.rightAnchor,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 30,
widthConstant: 0,
heightConstant: 65)
}
func configureFirstDropDownlabel(){
//add a view to stackView with addArrangedSubview()
horizontalStackView.addArrangedSubview(firstDropDownlabel)
_ = firstDropDownlabel.anchor(top: horizontalStackView.bottomAnchor,
left: horizontalStackView.leftAnchor,
bottom: nil, right: nil,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 0,
widthConstant: 0,
heightConstant: 0)
firstDropDownlabel.widthAnchor.constraint(equalToConstant: 130).isActive = true
firstDropDownlabel.heightAnchor.constraint(equalToConstant: 65).isActive = true
}
Try below code:
UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 50, 50)];
UIView *view2 = [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];
NSArray *subView = [NSArray arrayWithObjects:view1,view2, nil];
[self.stack1 initWithArrangedSubviews:subView];
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.
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)
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
tl;dr
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.
Problem
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.
Swift
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
Objective-C
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
Screen Recording
Bug Reporter
rdar://37433306: Make UIAlertController presentation controller and transitioning delegate public API to enable reuse.
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)
I have to implement NSLayoutConstraints 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:
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
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
It's this simple.
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 want text
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
}
}
How to use
In any view controller:
let spinny = verySimpleSpinner()
when the connection/etc has finished:
spinny.dismiss(animated: true)
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.
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:
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)
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
}
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];
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
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))
//...