error "Value of type 'UIViewController' has no member..." when moving a func inside extension

939 Views Asked by At

I need to move a method for adding and removing a logging view inside an Extension, in order to give it to every controller. to do so I added a inout UIVew parameter to original method, where I used a global var for the view. no I have this error

Value of type 'UIViewController' has no member 'containerForLoading'

removing self from self.containerForLoading will give error:

Escaping closure captures 'inout' parameter 'containerForLoading'

inside the animate closure (see the comment) is all wrong the entire process or I am lost at the last step?

extension UIViewController {
        
    func showLoadingView(containerForLoading: inout UIView, uponView: UIView) {
        
           containerForLoading = UIView(frame: uponView.bounds)
           uponView.addSubview(containerForLoading)
           
           containerForLoading.backgroundColor = .white
           containerForLoading.alpha = 0
           UIView.animate(withDuration: 0.24) { self.containerForLoading.alpha = 0.8 } //here the error
           let activivityIndicator = UIActivityIndicatorView()
           containerForLoading.addSubview(activivityIndicator)
           
           activivityIndicator.translatesAutoresizingMaskIntoConstraints = false
           
           
           NSLayoutConstraint.activate([
               
               activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
               activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)
               
           ])
           
           activivityIndicator.startAnimating()
       }
       
    func removeLoading(containerForLoading: inout UiView, uponView: UIView) {
           
           containerForLoading.removeFromSuperview()
           
       }
    
}

this is the code inside the original viewController

using this var

var containerForLoading = UIView()

called this way when needed

self.showLoadingView(uponView: self.view)
extension ViewController {
    
    func showLoadingView(uponView: UIView) {
        containerForLoading = UIView(frame: uponView.bounds)
        uponView.addSubview(containerForLoading)

        containerForLoading.backgroundColor = .white
        containerForLoading.alpha = 0
        UIView.animate(withDuration: 0.24) { self.containerForLoading.alpha = 0.8 }
        let activivityIndicator = UIActivityIndicatorView()
        containerForLoading.addSubview(activivityIndicator)

        activivityIndicator.translatesAutoresizingMaskIntoConstraints = false


        NSLayoutConstraint.activate([

            activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
            activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)

        ])

        activivityIndicator.startAnimating()
    }

    func removeLoading(uponView: UIView) {

        containerForLoading.removeFromSuperview()

    }
    
    
}
1

There are 1 best solutions below

2
On BEST ANSWER

You could make loadingContainerTag a local variable, and change the parameter name to something else. Then assign to the parameter just after you create the container view:

extension UIViewController {
    func showLoadingView(containerForLoadingProperty: inout UIView, uponView: UIView) {
        // local variable!
        let containerForLoading = UIView(frame: uponView.bounds)
        
        // also set property
        containerForLoadingProperty = containerForLoading
        
        uponView.addSubview(containerForLoading)
        containerForLoading.backgroundColor = .white
        containerForLoading.alpha = 0
        
        // no "self."!
        UIView.animate(withDuration: 0.24) { containerForLoading.alpha = 0.8 }
        // ... everything else is the same

removeLoading could just have one non-inout parameter:

func removeLoading(containerForLoadingProperty: UIView) {
    containerForLoadingProperty.removeFromSuperview()
}

But...


It is very weird for a method that shows a loading indicator, to need an inout parameter. It shouldn't assign to a special property that the caller provides. It should just show the loading indicator!

The purpose of your containerForLoading property is so that in removeLoading, you know which view to remove. If you don't store the containerForLoading view somewhere in a property, you wouldn't know which view to remove, right? Well, we can use the tag property of a view to identify views, so you can just make containerForLoading a local variable, and later in removeLoading, use its tag to find it.

extension UIViewController {
    
    static let loadingContainerTag = <a number you like>

    func showLoadingView(uponView: UIView) {
        // local variable!
        let containerForLoading = UIView(frame: uponView.bounds)
        uponView.addSubview(containerForLoading)
        containerForLoading.backgroundColor = .white
        containerForLoading.alpha = 0
        
        // set tag
        containerForLoading.tag = UIViewController.loadingContainerTag
        
        // no "self."!
        UIView.animate(withDuration: 0.24) { containerForLoading.alpha = 0.8 }
        let activivityIndicator = UIActivityIndicatorView()
        containerForLoading.addSubview(activivityIndicator)
        activivityIndicator.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
            activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)
        ])
        activivityIndicator.startAnimating()
    }
    func removeLoading(uponView: UIView) {
        // find view with the tag
        uponView.viewWithTag(UIViewController.loadingContainerTag)?.removeFromSuperview()
    }
}