Place a UIActivityIndicator inside a UIButton

15,712

Solution 1

I think the concept you are missing is that a view's frame (and its center) are both in relation to its superview. Based on your screenshot I would guess that your textfields and buttons are all in a view that is acting as a container. So your button's frame & center are in relation to that container view and not to the view controller's view as a whole. You assign the same frame & center to the activity indicator, but then you add the indicator as a subview of the main view and not the container view. So you now have two views (the button and the indicator) with the same frame, but that frame is in relation to two different superviews.

The easiest change would just be to add your indicator to the container view you are using. But I would suggest adding the indicator as a subview of the button, then just do a little math to tweak its position.

UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
CGFloat halfButtonHeight = btnLogin.bounds.size.height / 2;
CGFloat buttonWidth = btnLogin.bounds.size.width;
indicator.center = CGPointMake(buttonWidth - halfButtonHeight , halfButtonHeight);
[btnLogin addSubview:indicator];
[indicator startAnimating];

As a side note: Your setting of the center of a view just after the frame of the view is redundant. Also, the last view added as a subview is automatically the front subview.

Solution 2

Based on @Musa almatri answer, I create extension:

extension UIButton {
func loadingIndicator(show show: Bool) {
    let tag = 9876
    if show {
        let indicator = UIActivityIndicatorView()
        let buttonHeight = self.bounds.size.height
        let buttonWidth = self.bounds.size.width
        indicator.center = CGPointMake(buttonWidth/2, buttonHeight/2)
        indicator.tag = tag
        self.addSubview(indicator)
        indicator.startAnimating()
    } else {
        if let indicator = self.viewWithTag(tag) as? UIActivityIndicatorView {
            indicator.stopAnimating()
            indicator.removeFromSuperview()
        }
    }
}}

then you can use it like this:

yourButton.loadingIndicator(show: true) //hide -> show: false

Solution 3

Here is a Swift 3 version, without using tags.

import UIKit

extension UIButton {
    func loadingIndicator(show: Bool) {
        if show {
            let indicator = UIActivityIndicatorView()
            let buttonHeight = self.bounds.size.height
            let buttonWidth = self.bounds.size.width
            indicator.center = CGPoint(x: buttonWidth/2, y: buttonHeight/2)
            self.addSubview(indicator)
            indicator.startAnimating()
        } else {
            for view in self.subviews {
                if let indicator = view as? UIActivityIndicatorView {
                    indicator.stopAnimating()
                    indicator.removeFromSuperview()
                }
            }
        }
    }
}

Solution 4

Swift Solution:

var indicator = UIActivityIndicatorView()
var halfButtonHeight = SOME_BUTTON.bounds.size.height / 2;
var buttonWidth = SOME_BUTTON.bounds.size.width;
indicator.center = CGPointMake(buttonWidth - halfButtonHeight , halfButtonHeight);
SOME_BUTTON.addSubview(indicator)
indicator.startAnimating()

And to make it in the center of the button

indicator.center = CGPointMake(buttonWidth/2, halfButtonHeight);

Or Use great library

https://github.com/souzainf3/RNLoadingButton-Swift

Solution 5

Little upgrade: (adding button on superview)

extension UIButton {
    func loadingIndicator(_ show: Bool) {
        let indicatorTag = 808404
        if show {
            isEnabled = false
            alpha = 0
            let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
            indicator.center = center
            indicator.tag = indicatorTag
            superview?.addSubview(indicator)
            indicator.startAnimating()
        } else {
            isEnabled = true
            alpha = 1.0
            if let indicator = superview?.viewWithTag(indicatorTag) as? UIActivityIndicatorView {
                indicator.stopAnimating()
                indicator.removeFromSuperview()
            }
        }
    }
}
Share:
15,712

Related videos on Youtube

Matthys Du Toit
Author by

Matthys Du Toit

I'm a developer working primarily on MS-centric projects doing a lot of Front-end, Web Service & DB coding, but every now and again I get to do some iOS and there is no better place to discuss iOS dev than stack overflow.

Updated on August 03, 2022

Comments

  • Matthys Du Toit
    Matthys Du Toit almost 2 years

    I know I'm missing something stupid, but anyway, here is my code:

    UIActivityIndicatorView indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    indicator.hidesWhenStopped = YES;
    indicator.frame = btnLogin.frame;
    indicator.center = btnLogin.center;
    [self.view addSubview:indicator];
    [indicator bringSubviewToFront:indicator];
    

    Here is the end result:

    Screenshot of misplaced UIActivityIndicator

    http://img542.imageshack.us/img542/8172/uiactivity.png

    Thank you in advance!

  • dbreaux
    dbreaux over 7 years
    Note this puts the indicator in the center of the button. Accepted answer with CGPointMake(buttonWidth - halfButtonHeight , halfButtonHeight) puts it on the right side (in a simple, clever manner, IMO).
  • Anurag
    Anurag over 6 years
    Why is this better than one with tags? Could you please elaborate?
  • Mike Mikina
    Mike Mikina about 6 years
    Using tags adds additional complexity and can lead to errors, but I'm not saying that tags are bad. This solution is just simpler and looks for UIActivityIndicatorView itself.
  • Anurag
    Anurag about 6 years
    Thanks for the explanation.:)
  • meaning-matters
    meaning-matters over 5 years
    buttonWidth - halfButtonHeight: subtracting a height from a width, seems wrong. Then, the first indicator.center assignment is useless as there's a second. Finally, what is x?
  • meaning-matters
    meaning-matters over 5 years
    Why not indicator.center = self.center?
  • meaning-matters
    meaning-matters over 5 years
    The show function argument label is redundant because it's the same as the parameter name. You could just write: func loadingIndicator(show: Bool).