ошибка «Значение типа ‘UIViewController’ не имеет члена …» при перемещении функции внутри расширения

#ios #swift #extension-methods #inout

#iOS #swift #методы расширения #inout

Вопрос:

Мне нужно переместить метод для добавления и удаления представления журнала внутри расширения, чтобы передать его каждому контроллеру. для этого я добавил параметр inout UIVew в исходный метод, где я использовал глобальную переменную для представления. нет, у меня эта ошибка

Значение типа ‘UIViewController’ не имеет члена ‘containerForLoading’

удаление self из self.containerForLoading приведет к ошибке:

Экранирующее закрытие фиксирует параметр ‘inout’ ‘containerForLoading’

внутри анимированного закрытия (см. Комментарий) все неправильно во всем процессе или я заблудился на последнем шаге?

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

это код внутри исходного ViewController

использование этого параметра

 var containerForLoading = UIView()
  

вызывается таким образом, когда это необходимо

 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. self.containerForLoading.alpha вызывается внутри a extension UIViewController {} , поэтому он ожидает, что оно будет свойством UIViewController . Это не так. Вы имели в виду «containerForLoading.alpha», поскольку это предыдущая переменная, которую вы хотите.

2. но я не могу удалить self или я получаю ошибку при экранировании замыкания, которая захватывает параметр ‘inout’, параметр ‘containerForLoading’

3. Можете ли вы показать, на что похож ваш код, прежде чем помещать его в расширение?

4. обновлено старым кодом, в основном то же самое, за исключением ввода нового параметра

Ответ №1:

Вы могли бы создать loadingContainerTag локальную переменную и изменить имя параметра на что-то другое. Затем присвоите параметру сразу после создания представления контейнера:

 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 может inout быть только один непараметр:

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

Но…


Очень странно, что методу, который показывает индикатор загрузки, нужен inout параметр. Оно не должно присваиваться специальному свойству, предоставляемому вызывающим объектом. Он должен просто показывать индикатор загрузки!

Цель вашего containerForLoading свойства заключается в том removeLoading , чтобы вы знали, какое представление удалить. Если вы не сохраните containerForLoading представление где-нибудь в свойстве, вы не будете знать, какое представление удалить, верно? Ну, мы можем использовать tag свойство представления для идентификации представлений, поэтому вы можете просто создать containerForLoading локальную переменную, а позже removeLoading использовать ее тег, чтобы найти ее.

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

Комментарии:

1. спасибо, кажется, это работает! Я хотел бы знать, возможно ли такое же достижение с помощью расширения протокола, есть ли какие-то проблемы с сохраненными свойствами, верно?

2. Да, нет простого способа добавить сохраненные свойства в расширение. Если бы это было так, вы могли бы добавить UIView свойство для хранения загрузочного контейнера.