如何在视图控制器之外以编程方式创建视图

How to create view programmatically outside a view controller

目前我正在尝试创建一个将由管理器调用的 UIProgressView class:

 func addProgressBar() {

        let rect = CGRect(x: 10, y: 70, width: 250, height: 0)
        let progressView = UIProgressView(frame: rect)
        progressView.progress = 0.0
        progressView.tintColor = UIColor.blue
        self.view.addSubview(progressView)
    }

出现的问题是:

 self.view.addSubview(progressView)

因为函数不在 viewController 中,我得到错误:

Value of type 'NetworkHelper' has no member 'view'

知道如何在 viewcontroller 之外添加 progressView 吗?

好吧,你必须有一个视图来放置它。要么你抓住顶部 viewcontroller 并将它放在上面,要么你调用你的 NetworkHelper class 并传递一个它应该的视图把进度条放在.

我用这个方法抢顶viewcontroller

func topViewController() -> UIViewController? {
    guard var topVC = UIApplication.shared.keyWindow?.rootViewController else { return nil }
    while let presentedViewController = topVC.presentedViewController {
        topVC = presentedViewController
    }

    return topVC
}

您可以获得最顶层的视图控制器,并将子视图添加到它的视图中属性。

iPhone -- How to find topmost view controller

您可以使用 "callbacks",或者使用委托设计模式

https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html

或Swift的闭包(Objective-C块)

您可以从函数中 return 一个 "closure",这个闭包会创建进度。用于 view/viewController

 func addProgressBar() -> (UIView) -> () {

    return { view in
        let rect = CGRect(x: 10, y: 70, width: 250, height: 0)
        let progressView = UIProgressView(frame: rect)
        progressView.progress = 0.0
        progressView.tintColor = UIColor.blue
        view.addSubview(progressView)
    }
}

然后使用这个 return 值,例如在视图控制器中:

      (networkHelper.addProgressBar())(self.view)



如果你想在视图控制器上制作进度视图,并在那里管理它。

class NetworkHelper {

    ...

    func doAnythingWithNetwork(withProgress: @escaping (NSProgress) -> (),
                               completion: @escaping (Data?, Error?) -> ()) {
        ...
        // When you get any progress (like in Alamofire for example).
        // Pass it to the callback
        withProgress(progress)
        ...
    }

}

然后在你的 viewController:

let networker = NetworkHelper()

...

@IBAction func buttonClicked(_ sender: Any?) {
    networker.doAnythingWithNetwork(withProgress: { [weak self] progress in
        // You can use your viewController here (note: use 'self?', not
        //     'self', here self is "weak", only to avoid memory leaks).
        // Note: Any update of the view must be in DispatchQueue.main
    }, completion: { [weak self] data, error in
        // Your usual completion handler.
    })
}

但推荐的解决方案是使您的 NetworkHelper 只是一个模型,仅用于处理网络请求和数据。 SOLID 原则有一个 "Single Responsibility Principle",即设计每个对象只有一个责任。

视图(或 viewcontroller)应处理 addProgressBar,NetworkHelper 应通过委托或 closure/block 将此进度传递给视图所在的 viewcontroller已更新。

好吧,您可能自己猜到了,您需要一个 view,然后将 progressBar 放入其中。我认为 addProgressBar 方法的调用者应该知道它最适合的地方,所以我建议使用 progressBarUIViewController 类型的参数,这将是目标它负责进行网络调用,因此是将进度条放入的目标:

func addProgressBar(targetViewController: UIViewController) {
    // moreover, are you sure here that the height of the progressBar should be 0?
    let rect = CGRect(x: 10, y: 70, width: 250, height: 0)
    let progressView = UIProgressView(frame: rect)
    progressView.progress = 0.0
    progressView.tintColor = UIColor.blue
    targetViewController.view.addSubview(progressView)
}