Laden eines "Overlays" beim Ausführen langer Aufgaben in iOS

77

Was ist ein Beispiel für das Laden von Overlays in Swift IOS-Anwendungen, wenn Sie lange Aufgaben ausführen? Beispiel zum Laden von Daten vom Remote-Server. Ich habe gegoogelt, aber keine Antwort gefunden.

Aktualisiert:

Danke für @Sebastian Dressler, das ist ein einfacher Weg. Ich habe meinen Code aktualisiert und er läuft cool

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()
    }
}

Lassen Sie mit:

LoadingOverlay.shared.showOverlay(self.view)
//To to long tasks
LoadingOverlay.shared.hideOverlayView()
Sonrobby
quelle
2
Ihre Anfrage ist fast unmöglich zu erfüllen. Und Sie gewinnen auch nichts, wenn Sie eine einfache "Übersetzung" erhalten. Wenn Sie z. B. mit Objective-C überlagern, finden Sie einige Einträge für iOS / OS X.
Sebastian Dressler

Antworten:

200

Die obigen Antworten fügen eine Ladeansicht hinzu, blockieren jedoch keine Klickereignisse auf dem Bildschirm und bieten keine Überlagerung für den Rest des Bildschirms. Sie können dies wie folgt erreichen:

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 und neuer

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)

und Sie können es wie folgt verstecken:

dismiss(animated: false, completion: nil)

Es wird wie folgt gezeigt: Geben Sie hier die Bildbeschreibung ein

Ajinkya Patil
quelle
4
entlassenViewControllerAnimated (false, Vervollständigung: null) ist in XCode 8.2 nicht vorhanden. Es wurde geändert, um zu entlassen (animiert: falsch, Abschluss: null)
Olanrewaju O. Joseph
1
Ein sicherer Weg zu entlassen wäre:internal func dismissAlert() { if let vc = self.presentedViewController, vc is UIAlertController { dismiss(animated: false, completion: nil) } }
Derek Soike
Für Swift 4.0 Option erhalte ich folgende Warnung:Warning: Attempt to present <UIAlertController: 0x7fc4f106e600> on <MyProj.MyViewController: 0x7fc4f3222710> whose view is not in the window hierarchy!
Zaitsman
funktioniert gut, aber nach Entlassung **navigationController?.pushViewController(adminHomeVC, animated: true)**funktioniert nicht .. (schnell 4)
Manivel Gopi
kleine Änderung loadingIndicator.stylean loadingIndicator.activityIndicatorViewStylein Swift 4
Nandy Reddy
33

Erstellen Sie einfach eine Überlagerungsansicht, die Sie Ihrer übergeordneten Ansicht hinzufügen, und entfernen Sie sie, sobald Ihre Aufgabe erledigt ist, z. B. um sie hinzuzufügen:

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!)

Zum Entfernen:

overlay?.removeFromSuperview()
Sebastian Dressler
quelle
danke @Sebastian Dressler, ich aktualisiere gerade meine Frage. Könnten Sie diesen Code oben in Swift übersetzen?
Sonrobby
1
Nein, das glaube ich nicht. Was ich hier bereitgestellt habe, kann als Grundgerüst verwendet werden. Fügen Sie den Rest einfach selbst hinzu.
Sebastian Dressler
1
Ab heute (November 2015) scheint eine Überlagerung erforderlich zu sein! .removeFromSuperView () [Ausrufezeichen statt Fragezeichen beachten]
Derek
kann die !onoverlay...
zillaofthegods
1
Kleines "v" in der "removeFromSuperview ()"
Max Lipsky
26

Hintergrund verwischen + Aktivitätsanzeige, Beispiel Swift 5

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

Maor
quelle
Wie benutzt man das?
Spurdow
ah danke, ich habe es tatsächlich bekommen und im uiviewcontroller als self.view.addSubview (Overlay)
hinzugefügt
Tolle Lösung!
andzaOs
Diese Antwort war sehr hilfreich - wenn jemand anderes Arbeiten mit Xamarin.iOS dies tun , muss ich es in C # geschrieben haben hier .
Oliver Dalton
Sie können es im viewController verwenden, um self.view.showBlurLoader () anzuzeigen und self.view.removeBluerLoader () zu entfernen
Anil
8

Für alle, die spät dran sind wie ich, habe ich einige Änderungen am @ Sonrobby-Code vorgenommen. Soweit ich weiß, fügt @Sonrobby die Aktivität bei jedem showOverlayAnruf dem Overlay hinzu . Ein Teil der Konfiguration kann an die Init-Funktion übergeben werden, wobei nur die Platzierung auf der showOverlayMethode berücksichtigt wird.

Ich ändere auch den Hintergrund des Overlays in Schwarz, da meine App meistens weiß ist.

Hier ist der 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()
    }
}
Lucho
quelle
5

Um die gegebenen Antworten zu ergänzen, können Probleme auftreten, wenn Sie manchmal versuchen, den Code auszuführen. Persönlich gab es eine Gelegenheit, in der showOverlay nicht richtig aufgerufen wurde (weil ich versuchte, in eine Szene überzugehen und diese Funktion dann sofort während viewDidLoad aufzurufen).

Wenn Sie auf ein ähnliches Problem wie ich stoßen, gibt es eine Korrektur für den Code und eine Änderung des Ansatzes, die ich empfehle.

UPDATE: Platzieren Sie beide Codeblöcke als Abschluss eines dispatch_async-Aufrufs wie folgt:

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

ANSATZ: Wenn Sie Ihren Code aufrufen, führen Sie einen Aufruf von dispatch_after in der Hauptwarteschlange durch , um den Anruf um einige Millisekunden zu verzögern.

Die Begründung? Sie fordern die Benutzeroberfläche einfach auf, während viewDidLoad zu viel zu tun.

Wenn dieser Anhang zur Lösung helfen würde, wäre ich froh.

-Joel Long

PS Solution funktionierte für XCode v6.3.2

Joel Long
quelle
5

Verwenden Sie ATKit.

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

ATProgressOverlay-Klasse 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.

Bildschirmfoto:

Laden von OverlayScreenshot

Rupendra
quelle
4

Die @ sonrobby-Antwort wurde aktualisiert, eine Hintergrundansicht und eine Orientierungsbehandlung über die Größenänderungsmaske wurden hinzugefügt. Dies kann für einfache Dinge verwendet werden

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()
    }
}

Wenn Sie es zum Tastenfenster hinzufügen, kann es auch über Ihre Navigations- und Registerkartenleisten gehen ... so etwas

LoadingOverlay.shared.showOverlay(view: UIApplication.shared.keyWindow!)
anoop4real
quelle
Ist es nicht sicherer zu benutzen bgView.frame = view.bounds? Da view.frame.originkönnte sich von (0,0) unterscheiden.
Nicolas Miari
Das Ziel von Overlay ist es, die Ansicht abzudecken, die ihm zur Verfügung gestellt wird. Es muss sich nicht darum kümmern, wo sich der Ursprung befindet. Falls Sie den gesamten Bildschirm abdecken müssen, wenden Sie ihn auf das Tastenfenster an. Lassen Sie mich wissen, wenn Sie auf ein Problem gestoßen sind, damit ich es mir noch einmal ansehen kann
anoop4real
Ich habe gesagt, dass sich die frameEigenschaft einer Ansicht immer im Koordinatensystem der übergeordneten Ansicht befindet. Wenn die Überlagerung die Ansicht, auf die sie angewendet wird, vollständig abdecken soll, müssen Sie sicherstellen, dass der Rahmen so groß ist wie die Zielansicht (übergeordnete Ansicht), aber auch bei (0,0) beginnt. ansonsten, wenn die übergeordnete Ansicht nicht am Ursprung ist ot seine übergeordnete Ansicht, so werden sich überlagern.
Nicolas Miari
Ok, lass mich ein paar Beispiele ausprobieren ... und werde dementsprechend den Code aktualisieren
anoop4real
Kein Problem. Es sollte die meiste Zeit funktionieren; Es ist nur eine kleine technische Angelegenheit.
Nicolas Miari
2

Swift 3.

Ich habe @ Luchos Code in seiner Antwort unten verwendet und die Überlagerungshintergrundfarbe in klar geändert und eine Spinnerfarbe hinzugefügt.

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()
    }
}
Markhorrocks
quelle
2

Ich habe ein Protokoll erstellt, um Ihren eigenen View Controller als Overlay darzustellen. Die Verwendung ist sehr einfach:

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

Ergebnis:

Schneller OverlayViewController

Quellcode: https://github.com/agordeev/OverlayViewController

In Verbindung stehender Artikel: https://andreygordeev.com/2017/04/18/overlay-view-controller-protocols-swift/

Andrey Gordeev
quelle
2

@ Ajinkya Patil Antwort als Referenz. Swift 4.0 und neuer

Dies ist eine Erweiterungslösung, die auf allen viewControllern ohne Konflikte verwendet werden kann.

Erstellen Sie einen LoadingDialog + ViewContoller.swift

import UIKit

struct ProgressDialog {
    static var alert = UIAlertController()
    static var progressView = UIProgressView()
    static var progressPoint : Float = 0{
        didSet{
            if(progressPoint == 1){
                ProgressDialog.alert.dismiss(animated: true, completion: nil)
            }
        }
    }
}
extension UIViewController{
   func LoadingStart(){
        ProgressDialog.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();

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

  func LoadingStop(){
    ProgressDialog.alert.dismiss(animated: true, completion: nil)
  }
}

Rufen Sie die Funktion in ViewController an einer beliebigen Stelle auf. wie so:

self.LoadingStart()

und hier erfahren Sie, wie Sie den Ladedialog stoppen.

self.LoadingStop()
Muhammad Asyraf
quelle
1

Swift 5

class func showUniversalLoadingView(_ show: Bool, loadingText : String = "") {
    let existingView = UIApplication.shared.windows[0].viewWithTag(1200)
    if show {
        if existingView != nil {
            return
        }
        let loadingView = self.makeLoadingView(withFrame: UIScreen.main.bounds, loadingText: loadingText)
        loadingView?.tag = 1200
        UIApplication.shared.windows[0].addSubview(loadingView!)
    } else {
        existingView?.removeFromSuperview()
    }

}



 class func makeLoadingView(withFrame frame: CGRect, loadingText text: String?) -> UIView? {
    let loadingView = UIView(frame: frame)
    loadingView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
    let activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    //activityIndicator.backgroundColor = UIColor(red:0.16, green:0.17, blue:0.21, alpha:1)
    activityIndicator.layer.cornerRadius = 6
    activityIndicator.center = loadingView.center
    activityIndicator.hidesWhenStopped = true
    activityIndicator.style = .white
    activityIndicator.startAnimating()
    activityIndicator.tag = 100 // 100 for example

    loadingView.addSubview(activityIndicator)
    if !text!.isEmpty {
        let lbl = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
        let cpoint = CGPoint(x: activityIndicator.frame.origin.x + activityIndicator.frame.size.width / 2, y: activityIndicator.frame.origin.y + 80)
        lbl.center = cpoint
        lbl.textColor = UIColor.white
        lbl.textAlignment = .center
        lbl.text = text
        lbl.tag = 1234
        loadingView.addSubview(lbl)
    }
    return loadingView
}

Verwendet

showUniversalLoadingView(true, loadingText: "Downloading Data.......")

Geben Sie hier die Bildbeschreibung ein

showUniversalLoadingView (true) Geben Sie hier die Bildbeschreibung ein

Lader entfernen

showUniversalLoadingView(false)
Shourob Datta
quelle