Завантаження "накладання" під час запуску довгих завдань в iOS


79

Який приклад для завантаження накладеного файлу у програмі Swift IOS при виконанні довгих завдань. Приклад завантаження даних з віддаленого сервера. Я погуглив, але не знайшов відповіді.

Оновлено:

Дякую за @Sebastian Dressler, це простий спосіб. Я оновив свій код, і він працює круто

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

дозвольте використовувати:

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

2
Ваше прохання практично неможливо виконати. І ви також нічого не отримуєте, отримуючи простий "переклад". Якщо ви шукаєте Google, наприклад, для накладання на Objective-C, ви знайдете кілька записів, як це зробити з iOS / OS X.
Себастіан Дресслер,

Відповіді:


202

Наведені вище відповіді додають режим завантаження, але він не блокує події кліків на екрані, а також не забезпечує накладання для решти екрана. Ви можете досягти цього наступним чином:

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)

Свіфт 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 та новіші

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)

і ви можете приховати це наступним чином:

dismiss(animated: false, completion: nil)

Це буде показано наступним чином: введіть тут опис зображення


4
dismissViewControllerAnimated (хибне, завершення: нуль) не існує в XCode 8.2. Було змінено на звільнення (анімоване: помилкове, завершення:
нульове

2
Більш безпечним способом звільнення буде:internal func dismissAlert() { if let vc = self.presentedViewController, vc is UIAlertController { dismiss(animated: false, completion: nil) } }
Дерек Сойке

Щодо опції Swift 4.0, я отримую таке попередження:Warning: Attempt to present <UIAlertController: 0x7fc4f106e600> on <MyProj.MyViewController: 0x7fc4f3222710> whose view is not in the window hierarchy!
zaitsman

працює нормально, але після звільнення **navigationController?.pushViewController(adminHomeVC, animated: true)**не працює .. (стрімкий 4)
Манівел Гопі

невелика зміна loadingIndicator.styleна loadingIndicator.activityIndicatorViewStyleSwift 4
Nandy Reddy

33

Просто створіть собі накладений вигляд, який ви додаєте до батьківського подання та видаляєте його після завершення завдання, наприклад, щоб додати його:

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

Для видалення:

overlay?.removeFromSuperview()

дякую @ Себастьяну Дресслеру, я просто оновив своє запитання. Не могли б ви перекласти цей код вище на Swift?
Sonrobby

1
Ні, я не думаю. Те, що я подав тут, може бути використано як основний фреймворк. Просто додайте решту самі зверху.
Себастьян Дресслер

1
станом на сьогодні (листопад 2015 р.), схоже, вимагає накладання! .removeFromSuperView () [Примітка, а не знак оклику]
Дерек,

може підтвердити !onoverlay...
zillaofthegods

1
Невелике "v" у "removeFromSuperview ()"
Макс Ліпський

27

Розмиття фону + Індикатор активності, приклад 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()
    }
}

демо


як цим користуватися?
Spurdow,

ах, дякую, я насправді зрозумів, додавши в uiviewcontroller як self.view.addSubview (накладення)
Spurdow

Чудове рішення!
andzaO

Ця відповідь була дуже корисною - якщо комусь, хто працює з Xamarin.iOS, потрібно це зробити, я переписав її на C # тут .
Олівер Далтон,

Ви можете використовувати його в ViewController для показу self.view.showBlurLoader () і для видалення self.view.removeBluerLoader ()
Аніл

8

Для тих, хто запізнився, як я, я зробив деякі зміни в коді @Sonrobby. Як я розумію, @Sonrobby додає активність до накладання кожного showOverlayдзвінка. І деяку частину конфігурації можна передати функції init, дозволяючи лише розміщення showOverlayметоду.

Я також змінюю фон накладання на чорний, оскільки мій додаток переважно білий.

ось код:

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

5

Щоб додати відповіді на відповіді, ви можете зіткнутися з проблемами, якщо намагаєтеся іноді запустити код. Особисто у мене був випадок, коли showOverlay не було викликано належним чином (оскільки я намагався перейти на сцену, а потім негайно викликати цю функцію під час viewDidLoad).

Якщо ви зіткнетеся з проблемою, подібною до моєї, є одне виправлення коду та я рекомендую змінити підхід.

ВИПРАВЛЕННЯ: Розмістіть обидва блоки коду як закриття для виклику dispatch_async, наприклад:

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

ПІДХІД: Під час виклику коду зробіть dispatch_after дзвінок у основну чергу, щоб затримати дзвінок на кілька мілісекунд.

Міркування? Ви просто просите інтерфейс зробити занадто багато під час viewDidLoad.

Якби цей додаток до рішення допоміг, я був би радий.

-Джоел Лонг

PS Solution працював для XCode v6.3.2


5

Використовуйте ATKit.

Посилання: https://aurvan.github.io/atkit-ios-release/index.html

Клас ATProgressOverlay https://aurvan.github.io/atkit-ios-release/helpbook/Classes/ATProgressOverlay.html

Код:

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.

Знімок екрана:

Завантаження знімка екрана Overlay


4

Оновлено відповідь @sonrobby, додано фоновий вигляд та обробку орієнтації за допомогою маски зміни розміру ... це можна використовувати для простих матеріалів

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

якщо ви додасте його у вікно клавіатури, воно також зможе перейти через панелі навігації та вкладок ... щось подібне

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

Чи не безпечніше використовувати bgView.frame = view.bounds? Оскільки view.frame.originможе відрізнятися від (0,0).
Ніколас Міарі,

Мета накладання - охопити поданий до нього вигляд .... йому не потрібно дбати, де саме джерело .... у випадку, якщо вам потрібно охопити весь екран, застосувати його до вікна клавіш. дайте мені знати, якщо ви зіткнулися з проблемою, щоб я міг її
переглянути

Я говорив, що frameвластивість подання завжди знаходиться в системі координат батьківського подання. Якщо ви хочете, щоб накладення повністю охоплювало подання, до якого воно застосовується, ви повинні переконатись, що кадр має розмір, що перевищує цільовий (батьківський) вигляд, а також що він починається з (0,0). в іншому випадку, якщо батьківський вигляд не в нулі НЕ Ot його батьківський вид, і НЕ будуть перекривати.
Ніколас Міарі

Гаразд, дозвольте спробувати кілька зразків ... і відповідно оновити код
anoop4real

Нема проблем. Це повинно працювати більшу частину часу; це лише незначна техніка.
Ніколас Міарі

2

Стрімкий 3.

Я використав код @ Lucho у його відповіді нижче, і я змінив колір фону накладання, щоб очистити, і додав колір спінера.

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

Я створив протокол для подання власного контролера перегляду як накладання. Використання дуже просте:

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

Результат:

Swift OverlayViewController

Вихідний код: https://github.com/agordeev/OverlayViewController

Стаття, пов’язана з цим: https://andreygordeev.com/2017/04/18/overlay-view-controller-protocols-swift/


2

Відповідь @Ajinkya Patil як посилання. Swift 4.0 та новіші

Це рішення розширення, яке можна використовувати на всіх viewController без зіткнень.

Створіть 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)
  }
}

викликайте функцію всередині ViewController де завгодно. ось так:

self.LoadingStart()

і ось як зупинити діалог завантаження.

self.LoadingStop()

1

Стрімкий 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
}

Використовує

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

введіть тут опис зображення

showUniversalLoadingView (істина) введіть тут опис зображення

Зніміть навантажувач

showUniversalLoadingView(false)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.