在iOS中运行长任务时加载“叠加”


77

在执行长任务时在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
您的请求几乎无法实现。通过简单的“翻译”,您也一无所获。如果谷歌的如覆盖与Objective-C中,你会发现一些条目怎么办呢WRT的iOS / OS X.
塞巴斯蒂安·德雷斯勒

Answers:


200

上面的答案添加了一个加载视图,但它不会阻止屏幕上的单击事件,也不会为屏幕的其余部分提供覆盖。您可以通过以下方式实现它:

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
在XCode 8.2中不存在dismissViewControllerAnimated(false,complete:nil)。已更改为关闭(动画:错误,完成:无)
Olanrewaju O. Joseph

1
一种更安全的解雇方式是:internal func dismissAlert() { if let vc = self.presentedViewController, vc is UIAlertController { dismiss(animated: false, completion: nil) } }
Derek Soike

对于Swift 4.0选项,我得到以下警告:Warning: Attempt to present <UIAlertController: 0x7fc4f106e600> on <MyProj.MyViewController: 0x7fc4f3222710> whose view is not in the window hierarchy!
zaitsman '19

工作正常,但解雇后**navigationController?.pushViewController(adminHomeVC, animated: true)**无法正常工作。.(swift 4)
Manivel Gopi

小的变化loadingIndicator.style,以loadingIndicator.activityIndicatorViewStyle在夫特4
南迪雷迪

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

感谢@Sebastian Dressler,我只是更新我的问题。您可以将上面的代码翻译成Swift吗?
Sonrobby

1
不,我不这么认为。我在这里提供的内容可以用作基本框架。只需将其余的自己添加到顶部。
塞巴斯蒂安·德莱斯特勒

1
今天(2015年11月)的,似乎还需要重叠.removeFromSuperView()[注惊叹号而不是问号]!
德里克-

可以确认!overlay...
zillaofthegods

1
“ removeFromSuperview()”中的小“ v”
Max Lipsky

26

背景模糊+活动指示器,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(overlay)
Spurdow

很好的解决方案!
andzaOs

这个答案非常有帮助-如果其他使用Xamarin.iOS的人需要这样做,我已经在这里用C#重写了它。
奥利弗·道尔顿

您可以在viewController中使用它来显示self.view.showBlurLoader()并删除self.view.removeBluerLoader()
Anil

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期间立即调用此函数)。

如果您遇到与我的问题类似的问题,则该代码有一个修复程序,我建议您采用一种更改的方法。

FIX:放置两个代码块作为dispatch_async调用的闭包,如下所示:

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

方法:调用代码时,请对主队列执行dispatch_after调用,以将调用延迟几毫秒。

原因在哪里?您只是在要求UI在viewDidLoad期间做太多事情。

如果解决方案的此附录有所帮助,我将很高兴。

-乔·朗

PS解决方案适用于XCode v6.3.2



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)不同。
Nicolas Miari

Overlay的目的是覆盖提供给它的视图。...它不必关心原点的位置。让我知道您是否遇到问题,以便我重新
审视

我的意思是,视图的frame属性始终位于父视图的坐标系中。如果要使叠加层完全覆盖要应用的视图,则必须确保框架与目标(父)视图一样大,而且必须从(0,0)开始。否则,如果父视图是不在原点加时赛它的父视图,也不会覆盖。
Nicolas Miari '18

好的,让我尝试一些示例...然后将更新代码
anoop4real

没问题。它应该在大多数时间都有效;这只是一个小技巧。
Nicolas Miari '18

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(true) 在此处输入图片说明

卸下装载机

showUniversalLoadingView(false)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.