【问题标题】:Swift show and hide activity indicator on func with dataTask使用 dataTask 在 func 上快速显示和隐藏活动指示器
【发布时间】:2018-01-08 16:24:01
【问题描述】:

我有一个调用并等待服务器响应的函数。我想在请求之前显示活动指示器,并在对同一功能的回答之后隐藏它。为什么?避免代码重复。我将在其他操作中调用服务器。

它现在的工作方式是:手动调用登录按钮上的开始和停止动画。

为了测试指标,我在服务器上添加了 sleep。

问题:如何显示/隐藏每次调用服务器的指示器?

登录视图上的按钮:

@IBAction func btnLog2(_ sender: Any) {

    DispatchQueue.main.async(execute: {
        customActivityIndicatory(self.view, startAnimate: true)
    })

    let user = User(email: login_email.text!, password : login_password.text!)

    var jsonData = Data()
    let jsonEncoder = JSONEncoder()
    do {
        jsonData = try jsonEncoder.encode(user)
    }
    catch {
    }

    makeRequestPost(endpoint: "http://blog.local:4711/api/login",
                    requestType: "POST",
                    requestBody: jsonData,
                    completionHandler: { (response : ApiContainer<Login>?, error : Error?) in
                        if let error = error {
                            print("error calling POST on /todos")
                            print(error)
                            return
                        }

                        DispatchQueue.main.async(execute: {
                            customActivityIndicatory(self.view, startAnimate: false)
                        })

                        let a = (response?.result[0])!
                        let b = (response?.meta)!

                        if(b.sucess == "yes") {
                            DAKeychain.shared["email"] = (user.email) // Store
                            DAKeychain.shared["token"] = (a.token) // Store
                            DispatchQueue.main.async(execute: {
                                let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "tabBarClients") as UIViewController

                                self.present(viewController, animated: false, completion: nil)
                            })
                        }
                        else
                        {
                            DispatchQueue.main.async(execute: {
                                let myAlert = UIAlertController(title: "Error", message: "Invalid E-mail or Password", preferredStyle: .alert)
                                let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
                                myAlert.addAction(okAction)

                                self.present(myAlert, animated: true, completion: nil)
                            })
                        }
    } )


}

请求函数

func makeRequestPost<T>(endpoint: String,
                    requestType: String = "GET",
                    requestBody: Data,
                    completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) {

guard let url = URL(string: endpoint) else {
    print("Error: cannot create URL")
    let error = BackendError.urlError(reason: "Could not create URL")
    completionHandler(nil, error)
    return
}

var urlRequest = URLRequest(url: url)
let session = URLSession.shared
urlRequest.httpMethod = "POST"
urlRequest.httpBody = requestBody
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

let task = session.dataTask(with: urlRequest, completionHandler: {
    (data, response, error) in
    guard let responseData = data else {
        print("Error: did not receive data")
        completionHandler(nil, error)
        return
    }
    guard error == nil else {
        completionHandler(nil, error!)
        return
    }

    do {
        let response = try JSONDecoder().decode(ApiContainer<T>.self, from: responseData)
        completionHandler(response, nil)
    }
    catch {
        print("error trying to convert data to JSON")
        print(error)
        completionHandler(nil, error)
    }

})
task.resume()

}

帮助文件的功能:

func makeRequestPost<T>(endpoint: String,
                    requestType: String = "GET",
                    requestBody: Data,
                    completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) {

guard let url = URL(string: endpoint) else {
    print("Error: cannot create URL")
    let error = BackendError.urlError(reason: "Could not create URL")
    completionHandler(nil, error)
    return
}

var urlRequest = URLRequest(url: url)
let session = URLSession.shared
urlRequest.httpMethod = "POST"
urlRequest.httpBody = requestBody
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

let task = session.dataTask(with: urlRequest, completionHandler: {
    (data, response, error) in
    guard let responseData = data else {
        print("Error: did not receive data")
        completionHandler(nil, error)
        return
    }
    guard error == nil else {
        completionHandler(nil, error!)
        return
    }

    do {
        let response = try JSONDecoder().decode(ApiContainer<T>.self, from: responseData)
        completionHandler(response, nil)
    }
    catch {
        print("error trying to convert data to JSON")
        print(error)
        completionHandler(nil, error)
    }

})
task.resume()

}

编辑:

我创建了一个 CustomActivityIndi​​cator 类,但没有显示加载器。开始和停止打印在控制台上。

import Foundation
import UIKit
class CustomActivityIndicator {

var viewContainer = UIView()
var startAnimate:Bool? = true
var mainContainer = UIView()
var activityIndicatorView = UIActivityIndicatorView()
let viewBackgroundLoading = UIView()

init(viewContainer: UIView, startAnimate: Bool?) {
    self.viewContainer = viewContainer
    self.startAnimate = startAnimate
    setup()
}

func setup() {

    let mainContainer: UIView = UIView(frame: viewContainer.frame)
    mainContainer.center = viewContainer.center
    mainContainer.backgroundColor = UIColor.lightGray
    mainContainer.alpha = 0.5
    mainContainer.tag = 789456123
    mainContainer.isUserInteractionEnabled = true

    let viewBackgroundLoading: UIView = UIView(frame: CGRect(x:0,y: 0,width: 80,height: 80))
    viewBackgroundLoading.center = viewContainer.center
    viewBackgroundLoading.backgroundColor = UIColor.black
    viewBackgroundLoading.alpha = 0.5
    viewBackgroundLoading.clipsToBounds = true
    viewBackgroundLoading.layer.cornerRadius = 15

    let activityIndicatorView: UIActivityIndicatorView = UIActivityIndicatorView()
    activityIndicatorView.frame = CGRect(x:0.0,y: 0.0,width: 40.0, height: 40.0)
    activityIndicatorView.activityIndicatorViewStyle =
        UIActivityIndicatorViewStyle.whiteLarge
    activityIndicatorView.center = CGPoint(x: viewBackgroundLoading.frame.size.width / 2, y: viewBackgroundLoading.frame.size.height / 2)
}

func start(){
    DispatchQueue.main.async(execute: {
        print("start")
        self.viewBackgroundLoading.addSubview(self.activityIndicatorView)
        self.mainContainer.addSubview(self.viewBackgroundLoading)
        self.viewContainer.addSubview(self.mainContainer)
        self.activityIndicatorView.startAnimating()
    })
}

func stop() {
    DispatchQueue.main.async(execute: {
        print("stop")
        for subview in self.viewContainer.subviews {
            if subview.tag == 789456123{
                subview.removeFromSuperview()
            }
        }
    })
}

}

内部函数 makeRequestPost

func makeRequestPost<T>(endpoint: String,
                    requestType: String = "GET",
                    requestBody: Data,
                    activityIndicator: CustomActivityIndicator? = nil,
                    completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) {


activityIndicator?.start() .......

在操作按钮上:

let loading = CustomActivityIndicator(viewContainer: self.view, startAnimate: true)
    makeRequestPost(endpoint: "http://blog.local:4711/api/login",
                    requestType: "POST",
                    requestBody: jsonData,
                    activityIndicator: loading,
                    completionHandler: { (response : ApiContainer<Login>?, error : Error?) in

【问题讨论】:

    标签: ios swift task


    【解决方案1】:

    阅读了一些教程后,我最终回答了自己的问题。

    我做了什么?

    1) 我创建了一个类:ViewControllerUtils

    class ViewControllerUtils {
    
    var container: UIView = UIView()
    var loadingView: UIView = UIView()
    var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
    
    /*
     Show customized activity indicator,
     actually add activity indicator to passing view
    
     @param uiView - add activity indicator to this view
     */
    func showActivityIndicator(uiView: UIView) {
    
        self.container.frame = uiView.frame
        self.container.center = uiView.center
        self.container.backgroundColor = UIColor.white
        self.container.alpha = 0.7
        self.container.tag = 789456123
    
        loadingView.frame = CGRect(x:0, y:0, width:80, height:80)
        loadingView.center = uiView.center
        loadingView.backgroundColor = UIColor.black
        loadingView.clipsToBounds = true
        loadingView.layer.cornerRadius = 10
    
        activityIndicator.frame = CGRect(x:0.0, y:0.0, width:40.0, height:40.0);
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
        activityIndicator.center = CGPoint(x:loadingView.frame.size.width / 2, y:loadingView.frame.size.height / 2);
    
        DispatchQueue.main.async {
            self.loadingView.addSubview(self.activityIndicator)
            self.container.addSubview(self.loadingView)
        uiView.addSubview(self.container)
        self.activityIndicator.startAnimating()
        }
    }
    
    /*
     Hide activity indicator
     Actually remove activity indicator from its super view
    
     @param uiView - remove activity indicator from this view
     */
    func hideActivityIndicator(uiView: UIView) {
        DispatchQueue.main.async {
        if let viewWithTag = uiView.viewWithTag(789456123) {
            viewWithTag.removeFromSuperview()
        }
        else {
            return
        }
        }
    }
    
    /*
     Define UIColor from hex value
    
     @param rgbValue - hex color value
     @param alpha - transparency level
     */
    func UIColorFromHex(rgbValue:UInt32, alpha:Double=1.0)->UIColor {
        let red = CGFloat((rgbValue & 0xFF0000) >> 16)/256.0
        let green = CGFloat((rgbValue & 0xFF00) >> 8)/256.0
        let blue = CGFloat(rgbValue & 0xFF)/256.0
        return UIColor(red:red, green:green, blue:blue, alpha:CGFloat(alpha))
    }
    
    }
    

    2) 在 func makeRequestPost 上,我添加了一个新参数(视图)并调用以启动和停止加载视图

    func makeRequestPost<T>(endpoint: String,
                        requestType: String = "GET",
                        requestBody: Data,
                        view: UIView,
                        completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) {
    
    ViewControllerUtils().showActivityIndicator(uiView: view)
    
    guard let url = URL(string: endpoint) else {
        print("Error: cannot create URL")
        let error = BackendError.urlError(reason: "Could not create URL")
        completionHandler(nil, error)
        return
    }
    
    var urlRequest = URLRequest(url: url)
    let session = URLSession.shared
    urlRequest.httpMethod = "POST"
    urlRequest.httpBody = requestBody
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
    
    let task = session.dataTask(with: urlRequest, completionHandler: {
        (data, response, error) in
    
        ViewControllerUtils().hideActivityIndicator(uiView: view)
        guard let responseData = data else {
            print("Error: did not receive data")
            completionHandler(nil, error)
            return
        }
        guard error == nil else {
            completionHandler(nil, error!)
            return
        }
    
        do {
            let response = try JSONDecoder().decode(ApiContainer<T>.self, from: responseData)
            completionHandler(response, nil)
        }
        catch {
            print("error trying to convert data to JSON")
            print(error)
            completionHandler(nil, error)
        }
    
    })
    
    task.resume()
    
    }
    

    【讨论】:

      【解决方案2】:

      我假设您的 ma​​keRequest 函数位于单独的服务类中,因此基本上您可以在 onRequestComplete 块上添加通用 onReqestStart,并在发布 ma​​keRequest 被调用时执行它们。

      请关注https://cocoapods.org/pods/ResultPromises,也许你会发现它对你有帮助

      编辑:

      声明全局函数 end 而且将它们暴露给整个模块是个坏主意。您最终可能会获得数千个辅助函数,并且项目的复杂性将成倍增加。尽管仍然很少有人保护 程序 方法(当函数以这种方式声明时),但社区在 80 年代后期从它演变而来。所以我强烈建议你声明类来包装所有与网络相关的功能(NetworkClient/Service/Manager 等等)

      回到你的问题:你可以把你的活动指标作为参数:

      func makeRequestPost<T>(endpoint: String,
                          requestType: String = "GET",
                          requestBody: Data,
                          activityIndicator: CustomActivityIndicator?,
                          completionHandler: @escaping (ApiContainer<T>?, Error?) -> ()) 
      

      因此,您可以在函数中启动并完成它,而不是在使用函数时复制/粘贴代码。

      【讨论】:

      • 还不是一门课。我在我的问题中添加了辅助函数。
      • 尽量避免让函数保持全局。在这种特殊情况下,您可以创建 NetwrokService 来发送请求并为其提供 requestDidSent/DidComplete 块,如果您在整个应用程序中使用相同的活动指示器。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 2017-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多