Индикатор активности отображения внутри UIButton

Я хотел бы изменить содержимое UIButton на ActivityIndicator после его нажатия.

Я знаю, что кнопки имеют imageView и titleLabel, но я не знаю, как указать индикатор активности в любом из них.

Вот как я создаю индикаторы активности:

let aiView = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
aiView.startAnimating()
aiView.center = CGPointMake(0,0)
aiView.hidesWhenStopped = false

Ответ 1

Код найден здесь:

https://www.snip2code.com/Snippet/777050/iOS---Swift---UIButton-subclass-for-show/

import UIKit

class LoadingButton: UIButton {
var originalButtonText: String?
var activityIndicator: UIActivityIndicatorView!

func showLoading() {
    originalButtonText = self.titleLabel?.text
    self.setTitle("", for: .normal)

    if (activityIndicator == nil) {
        activityIndicator = createActivityIndicator()
    }

    showSpinning()
}

func hideLoading() {
    self.setTitle(originalButtonText, for: .normal)
    activityIndicator.stopAnimating()
}

private func createActivityIndicator() -> UIActivityIndicatorView {
    let activityIndicator = UIActivityIndicatorView()
    activityIndicator.hidesWhenStopped = true
    activityIndicator.color = .lightGray
    return activityIndicator
}

private func showSpinning() {
    activityIndicator.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(activityIndicator)
    centerActivityIndicatorInButton()
    activityIndicator.startAnimating()
}

private func centerActivityIndicatorInButton() {
    let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
    self.addConstraint(xCenterConstraint)

    let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
    self.addConstraint(yCenterConstraint)
}
}

Ответ 2

@Boris: Это не должно быть в расширении.

Здесь он находится в быстрой версии 3/4, с улучшенным кодом: отключает кнопку, работает с изображениями и названиями.

class LoadingButton: UIButton {

    struct ButtonState {
        var state: UIControlState
        var title: String?
        var image: UIImage?
    }

    private (set) var buttonStates: [ButtonState] = []
    private lazy var activityIndicator: UIActivityIndicatorView = {
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.hidesWhenStopped = true
        activityIndicator.color = self.titleColor(for: .normal)
        self.addSubview(activityIndicator)
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
        let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
        self.addConstraints([xCenterConstraint, yCenterConstraint])
        return activityIndicator
    }()

    func showLoading() {
        activityIndicator.startAnimating()
        var buttonStates: [ButtonState] = []
        for state in [UIControlState.disabled] {
            let buttonState = ButtonState(state: state, title: title(for: state), image: image(for: state))
            buttonStates.append(buttonState)
            setTitle("", for: state)
            setImage(UIImage(), for: state)
        }
        self.buttonStates = buttonStates
        isEnabled = false
    }

    func hideLoading() {
        activityIndicator.stopAnimating()
        for buttonState in buttonStates {
            setTitle(buttonState.title, for: buttonState.state)
            setImage(buttonState.image, for: buttonState.state)
        }
        isEnabled = true
    }

}

Ответ 3

Swift 4.0 с небольшой модификацией

class LoadingUIButton: UIButton {

    @IBInspectable var indicatorColor : UIColor = .lightGray

    var originalButtonText: String?
    var activityIndicator: UIActivityIndicatorView!

    func showLoading() {
        originalButtonText = self.titleLabel?.text
        self.setTitle("", for: .normal)

        if (activityIndicator == nil) {
            activityIndicator = createActivityIndicator()
        }

        showSpinning()
    }

    func hideLoading() {
        DispatchQueue.main.async(execute: {
            self.setTitle(self.originalButtonText, for: .normal)
            self.activityIndicator.stopAnimating()
        })
    }

    private func createActivityIndicator() -> UIActivityIndicatorView {
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.hidesWhenStopped = true
        activityIndicator.color = indicatorColor
        return activityIndicator
    }

    private func showSpinning() {
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(activityIndicator)
        centerActivityIndicatorInButton()
        activityIndicator.startAnimating()
    }

    private func centerActivityIndicatorInButton() {
        let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
        self.addConstraint(xCenterConstraint)

        let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
        self.addConstraint(yCenterConstraint)
    }

}

Ответ 4

я написал библиотеку в swift, в ней есть 7 различных стилей и анимаций для отображения индикатора в uibutton, здесь это

https://github.com/farshadjahanmanesh/loady

  enter image description here

Ответ 5

Swift 4:

extension UIButton {
    func loadingIndicator(_ show: Bool) {
        let tag = 808404
        if show {
            self.isEnabled = false
            self.alpha = 0.5
            let indicator = UIActivityIndicatorView()
            let buttonHeight = self.bounds.size.height
            let buttonWidth = self.bounds.size.width
            indicator.center = CGPoint(x: buttonWidth/2, y: buttonHeight/2)
            indicator.tag = tag
            self.addSubview(indicator)
            indicator.startAnimating()
        } else {
            self.isEnabled = true
            self.alpha = 1.0
            if let indicator = self.viewWithTag(tag) as? UIActivityIndicatorView {
                indicator.stopAnimating()
                indicator.removeFromSuperview()
            }
        }
    }
}

Использование: button.loadingIndicator(true/false)

Ответ 6

Обновленный выбранный ответ (steve-rosenberg) для Apple Swift версии 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) Xcode 8.1 (8B62)

import ObjectiveC

private var originalButtonText: String?
private var activityIndicator: UIActivityIndicatorView!

extension UIButton{

    func showLoading() {
        originalButtonText = self.titleLabel?.text
        self.setTitle("", for: .normal)

        if (activityIndicator == nil) {
            activityIndicator = createActivityIndicator()
        }

        showSpinning()
    }

    func hideLoading() {
        self.setTitle(originalButtonText, for: .normal)
        activityIndicator.stopAnimating()
    }

    private func createActivityIndicator() -> UIActivityIndicatorView {
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.hidesWhenStopped = true
        activityIndicator.color = UIColor.lightGray
        return activityIndicator
    }

    func showSpinning() {
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(activityIndicator)
        centerActivityIndicatorInButton()
        activityIndicator.startAnimating()
    }

    private func centerActivityIndicatorInButton() {
        let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
        self.addConstraint(xCenterConstraint)

        let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
        self.addConstraint(yCenterConstraint)
    }
}

Ответ 7

Отлично @Steve,

Может использоваться ниже для реализации вместо строки,

var activityIndicator: UIActivityIndicatorView!

используйте это, так что, если мы настроим индикатор активности перед вызовом showLoading(), приложение не сработает.

private var _activityIndicator:UIActivityIndicatorView! = nil
var activityIndicator: UIActivityIndicatorView{
    set{
        _activityIndicator = newValue

    }
    get{
        if _activityIndicator == nil{
            _activityIndicator = createActivityIndicator()
        }
        return _activityIndicator
    }
}

Ответ 8

Еще одно решение для Swift4 я постарался сделать проще, чем предыдущие решения. Это расширение позволяет изменить размер UIActivityIndicatorView (масштаб), чтобы подогнать его под UIButton, и изменить цвет.

https://gist.github.com/jalopezsuarez/0ecc885b3fd5c555630799a067d66d98

import Foundation
import UIKit

class UIButtonActivity: UIButton {

    @IBInspectable var indicatorColor : UIColor = .lightGray

    private var buttonLabel: String?

    func startAnimating() {
        self.isEnabled = false

        buttonLabel = self.titleLabel?.text
        self.setTitle("", for: .normal)

        let indicator = UIActivityIndicatorView()
        indicator.color = indicatorColor
        indicator.hidesWhenStopped = true

        let buttonHeight = self.bounds.size.height
        let buttonWidth = self.bounds.size.width
        indicator.center = CGPoint(x: buttonWidth/2, y: buttonHeight/2)

        let scale = max(min((self.frame.size.height - 4) / 21, 2.0), 0.0)
        let transform: CGAffineTransform = CGAffineTransform(scaleX: scale, y: scale)
        indicator.transform = transform

        self.addSubview(indicator)
        indicator.startAnimating()
    }

    func stopAnimating() {
        self.isEnabled = true

        if let titleLabel = buttonLabel {
            self.setTitle(titleLabel, for: .normal)
        }

        if let indicator = self.viewWithTag(tag) as? UIActivityIndicatorView {
            indicator.stopAnimating()
            indicator.removeFromSuperview()
        }
    }
}

Ответ 9

Вот мое редактирование с немного меньшим количеством кода.

class Button: UIButton {
    private var originalButtonText: String?

    private lazy var activityIndicator: UIActivityIndicatorView = {
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        activityIndicator.color = .black
        addSubview(activityIndicator)

        NSLayoutConstraint.activate([
            activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor),
            activityIndicator.centerXAnchor.constraint(equalTo: self.centerXAnchor)
        ])

        return activityIndicator
    }()

    func loading(_ isLoading: Bool) {
        isEnabled = !isLoading

        if isLoading {
            originalButtonText = titleLabel?.text
            setTitle("", for: .normal)
            activityIndicator.startAnimating()

        } else {
            setTitle(originalButtonText, for: .normal)
            activityIndicator.stopAnimating()
        }
    }
}