web-dev-qa-db-fra.com

Chargement d'une "superposition" lors de l'exécution de tâches longues dans iOS

Quel est l’exemple de chargement de superposition dans Swift IOS application lorsque vous effectuez une tâche longue. Exemple de chargement de données à partir d’un serveur distant. J’ai cherché sur Google mais n’ai trouvé aucune réponse.

Mis à jour:

Merci pour @Sebastian Dressler, c’est un moyen simple. J'ai mis à jour mon code et il fonctionne bien

public class LoadingOverlay{

var overlayView = UIView()
var activityIndicator = UIActivityIndicatorView()

class var shared: LoadingOverlay {
    struct Static {
        static let instance: LoadingOverlay = LoadingOverlay()
    }
    return Static.instance
}

    public func showOverlay(view: UIView) {

        overlayView.frame = CGRectMake(0, 0, 80, 80)
        overlayView.center = view.center
        overlayView.backgroundColor = UIColor(hex: 0x444444, alpha: 0.7)
        overlayView.clipsToBounds = true
        overlayView.layer.cornerRadius = 10

        activityIndicator.frame = CGRectMake(0, 0, 40, 40)
        activityIndicator.activityIndicatorViewStyle = .WhiteLarge
        activityIndicator.center = CGPointMake(overlayView.bounds.width / 2, overlayView.bounds.height / 2)

        overlayView.addSubview(activityIndicator)
        view.addSubview(overlayView)

        activityIndicator.startAnimating()
    }

    public func hideOverlayView() {
        activityIndicator.stopAnimating()
        overlayView.removeFromSuperview()
    }
}

laisser utiliser:

LoadingOverlay.shared.showOverlay(self.view)
//To to long tasks
LoadingOverlay.shared.hideOverlayView()
60
Sonrobby

Les réponses ci-dessus ajoutent une vue de chargement, mais elles ne bloquent pas les événements de clic à l'écran et ne fournissent pas de superposition pour le reste de l'écran. Vous pouvez y arriver comme suit:

let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .Alert)

alert.view.tintColor = UIColor.blackColor()
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50)) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();

alert.view.addSubview(loadingIndicator)
presentViewController(alert, animated: true, completion: nil)

Swift 3.0

let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)

let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();

alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)

Swift 4.0 et plus récent

let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)

let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();

alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)

et vous pouvez le cacher comme suit:

dismiss(animated: false, completion: nil)

Il sera montré comme suit: enter image description here

153
Ajinkya Patil

Créez vous-même une vue de superposition que vous ajoutez à votre vue parent et supprimez-la une fois la tâche terminée, par exemple. pour l'ajouter:

var overlay : UIView? // This should be a class variable

[ ... ]

overlay = UIView(frame: view.frame)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8

view.addSubview(overlay!)

Pour le retrait:

overlay?.removeFromSuperview()
32
Sebastian Dressler

Arrière-plan flou + Indicateur d'activité, Swift 5 exemple

extension UIView {
    func showBlurLoader() {
        let blurLoader = BlurLoader(frame: frame)
        self.addSubview(blurLoader)
    }

    func removeBluerLoader() {
        if let blurLoader = subviews.first(where: { $0 is BlurLoader }) {
            blurLoader.removeFromSuperview()
        }
    }
}


class BlurLoader: UIView {

    var blurEffectView: UIVisualEffectView?

    override init(frame: CGRect) {
        let blurEffect = UIBlurEffect(style: .dark)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.frame = frame
        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.blurEffectView = blurEffectView
        super.init(frame: frame)
        addSubview(blurEffectView)
        addLoader()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func addLoader() {
        guard let blurEffectView = blurEffectView else { return }
        let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
        activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
        blurEffectView.contentView.addSubview(activityIndicator)
        activityIndicator.center = blurEffectView.contentView.center
        activityIndicator.startAnimating()
    }
}

demo

22
Maor

Pour les retardataires comme moi, j'ai apporté des modifications au code @Srobby. Si j'ai bien compris, @Sonrobby ajoute l'activité à la superposition à chaque appel showOverlay. Et une partie de la configuration peut être passée à la fonction init, en ne laissant que le placement sur la méthode showOverlay.

Je change également l'arrière-plan de la superposition en noir, car mon application est principalement blanche.

voici le code:

public class LoadingOverlay{

    var overlayView : UIView!
    var activityIndicator : UIActivityIndicatorView!

    class var shared: LoadingOverlay {
        struct Static {
            static let instance: LoadingOverlay = LoadingOverlay()
        }
        return Static.instance
    }

    init(){
        self.overlayView = UIView()
        self.activityIndicator = UIActivityIndicatorView()

        overlayView.frame = CGRectMake(0, 0, 80, 80)
        overlayView.backgroundColor = UIColor(white: 0, alpha: 0.7)
        overlayView.clipsToBounds = true
        overlayView.layer.cornerRadius = 10
        overlayView.layer.zPosition = 1

        activityIndicator.frame = CGRectMake(0, 0, 40, 40)
        activityIndicator.center = CGPointMake(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
        activityIndicator.activityIndicatorViewStyle = .WhiteLarge
        overlayView.addSubview(activityIndicator)
    }

    public func showOverlay(view: UIView) {
        overlayView.center = view.center
        view.addSubview(overlayView)
        activityIndicator.startAnimating()
    }

    public func hideOverlayView() {
        activityIndicator.stopAnimating()
        overlayView.removeFromSuperview()
    }
}
7
Lucho

Pour ajouter aux réponses données, vous pourriez rencontrer des problèmes si vous tentez d’exécuter le code de temps en temps. Personnellement, il y a eu une occasion où showOverlay n'était pas appelé correctement (parce que j'essayais de faire la transition dans une scène, puis d'appeler immédiatement cette fonction pendant viewDidLoad).

Si vous rencontrez un problème similaire au mien, il existe une solution au code et un changement d'approche que je recommande.

CORRECTIF: placez les deux blocs de code en tant que fermetures d'un appel dispatch_async, comme suit:

dispatch_async(dispatch_get_main_queue(),
 { //code });

APPROCHE: Lorsque vous appelez votre code, effectuez un dispatch_after sur la file d'attente principale pour retarder l'appel de quelques millisecondes.

Le raisonnement? Vous demandez simplement à l'interface utilisateur d'en faire trop pendant viewDidLoad.

Si cette annexe à la solution vous a aidé, je serais heureux.

-Joel Long

P.S. La solution a fonctionné pour XCode v6.3.2

5
Joel Long

Mise à jour de la réponse @sonrobby, ajout d'une vue d'arrière-plan et d'une gestion de l'orientation via un masque de redimensionnement ... ceci peut être utilisé pour des choses simples

public class LoadingOverlay{

    var overlayView = UIView()
    var activityIndicator = UIActivityIndicatorView()
    var bgView = UIView()

    class var shared: LoadingOverlay {
        struct Static {
            static let instance: LoadingOverlay = LoadingOverlay()
        }
        return Static.instance
    }

    public func showOverlay(view: UIView) {

        bgView.frame = view.frame
        bgView.backgroundColor = UIColor.gray
        bgView.addSubview(overlayView)
        bgView.autoresizingMask = [.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin,.flexibleHeight, .flexibleWidth]
        overlayView.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
        overlayView.center = view.center
        overlayView.autoresizingMask = [.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin]
        overlayView.backgroundColor = UIColor.black
        overlayView.clipsToBounds = true
        overlayView.layer.cornerRadius = 10

        activityIndicator.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
        activityIndicator.activityIndicatorViewStyle = .whiteLarge
        activityIndicator.center = CGPoint(x: overlayView.bounds.width / 2, y: overlayView.bounds.height / 2)

        overlayView.addSubview(activityIndicator)
        view.addSubview(bgView)
        self.activityIndicator.startAnimating()

    }

    public func hideOverlayView() {
        activityIndicator.stopAnimating()
        bgView.removeFromSuperview()
    }
}

si vous l'ajoutez à Keywindow, il peut alors passer en revue votre barre de navigation et les barres d'onglets également ... quelque chose comme ceci

LoadingOverlay.shared.showOverlay(view: UIApplication.shared.keyWindow!)
4
anoop4real

Utilisez ATKit.

Voir: https://aurvan.github.io/atkit-ios-release/index.html

Classe ATProgressOverlay https://aurvan.github.io/atkit-ios-release/helpbook/Classes/ATProgressOverlay.html

Code:

import ATKit

ATProgressOverlay.sharedInstance.show() // Does not show network activity indicator on status bar.

ATProgressOverlay.sharedInstance.show(isNetworkActivity: true) // Shows network activity indicator on status bar.

Capture d'écran:

Loading OverlayScreenshot

3
Rupendra

J'ai créé un protocole pour présenter votre propre contrôleur de vue en superposition. L'utilisation est très simple:

class ViewController: UIViewController, OverlayHost {
    @IBAction func showOverlayButtonPressed() {
        showOverlay(type: YourOverlayViewController.self, 
            fromStoryboardWithName: "Main")
    }
}

Résultat:

Swift OverlayViewController

Code source: https://github.com/agordeev/OverlayViewController

Article connexe: https://andreygordeev.com/2017/04/18/overlay-view-controller-protocols-Swift/

2
Andrey Gordeev

Rapide 3.

J'ai utilisé le code de @ Lucho dans sa réponse ci-dessous et j'ai changé la couleur d'arrière-plan de la superposition en clair et ajouté une couleur spinner.

public class LoadingOverlay {

    var overlayView : UIView!
    var activityIndicator : UIActivityIndicatorView!

    class var shared: LoadingOverlay {
        struct Static {
            static let instance: LoadingOverlay = LoadingOverlay()
        }
        return Static.instance
    }

    init(){
        self.overlayView = UIView()
        self.activityIndicator = UIActivityIndicatorView()

        overlayView.frame = CGRect(0, 0, 80, 80)
        overlayView.backgroundColor = .clear
        overlayView.clipsToBounds = true
        overlayView.layer.cornerRadius = 10
        overlayView.layer.zPosition = 1

        activityIndicator.frame = CGRect(0, 0, 40, 40)
        activityIndicator.center = CGPoint(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
        activityIndicator.activityIndicatorViewStyle = .whiteLarge
        activityIndicator.color = .gray
        overlayView.addSubview(activityIndicator)
    }

    public func showOverlay(view: UIView) {
        overlayView.center = view.center
        view.addSubview(overlayView)
        activityIndicator.startAnimating()
    }

    public func hideOverlayView() {
        activityIndicator.stopAnimating()
        overlayView.removeFromSuperview()
    }
}
2
markhorrocks