-1

Currently I'm trying to create a UIProgressView that will be called with a manager 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)
    }

This issue that's arising is the line:

 self.view.addSubview(progressView)

because the function isn't in a viewController I get the error:

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

any idea how to add the progressView outside of a viewcontroller?

SwiftyJD
  • 5,257
  • 7
  • 41
  • 92

4 Answers4

1

Well, you probably guessed it yourself, you need a view to which you would put the progressBar. I think the caller of the addProgressBar method should know the best where it would fit the best, so I would recommend using progressBar with an argument of a UIViewController type, which would be the target which is responsible for making a network call and thus is a target to put the progress bar into:

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)
}
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
0

Well you have to have a view to place it in. Either you grab the top viewcontroller and place it on that or you call your NetworkHelper class and pass it a view that it should place the progress bar in.

I use this method to grab the top viewcontroller

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

    return topVC
}
Frizzo
  • 432
  • 5
  • 10
  • When I try and add the subview: let topView = Utility.getTopViewController topView.addSubview(progressView), there is an error "Value of type '(Utility) -> () -> UIViewController?' has no member 'addSubview'" – SwiftyJD Feb 13 '18 at 22:09
  • That's because you're adding it to the viewcontroller. topView.view.addSubview(progressView) is what you want. :) – Frizzo Feb 14 '18 at 08:31
0

You could get the topmost view controller, and add the subview to its view property.

iPhone -- How to find topmost view controller

mSabu
  • 31
  • 4
0

You can use "callbacks", either using Delegation design pattern

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

Or Swift's closures (Objective-C blocks)

You can return a "closure" from the function, this closure creates the progress. To be used on the 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)
    }
}

Then use this return value, in the view controller for example:

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



The is if you want to make the progress view on the view controller, and manage it there.

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

}

Then in your 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.
    })
}

But the recommended solution is to make your NetworkHelper just a model, just to handle web requests and data. SOLID principles has a "Single Responsibility Principle", i.e. Design each object to have only one single responsibility.

The view (or viewcontroller) should handle the addProgressBar, and the NetworkHelper should pass this progress via a delegate or closure/block to the viewcontroller where the view is updated.

user9335240
  • 1,739
  • 1
  • 7
  • 14