Класс для протокола разговора в Swift - PullRequest
0 голосов
/ 21 января 2019

У меня есть этот тяжелый базовый подкласс класса VC UIViewController, который я пытаюсь преобразовать как vcprotocol.

Это базовый виртуальный канал, который выполняет всю работу как класс бога. Который я хотел бы разбить как vcProtocol.

Я пытаюсь сделать это разделение интересов. Не все ViewControllers должны отображать сообщение с предупреждением или сеть не подключена.

Например, у меня есть displayView, который я создаю в расширении протокола как вычисляемое свойство. Нет предупреждения об ошибке, но индикатор не отображается. Когда я пытаюсь отладить и делать po acticvityIndicator, я получаю следующую ошибку, которая указывает, что ActivityIndicator никогда не выделялся.

error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x5a1012de027).
The process has been returned to the state before expression evaluation.

Фрагмент кода:

protocol vcProtocol {
    var activityIndicator: UIActivityIndicatorView { get }
}

расширение протокола:

extension vcProtocol where Self: UIViewController {

    var activityIndicator: UIActivityIndicatorView {
        let indicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.gray)
        indicator.hidesWhenStopped = true
        indicator.style = .whiteLarge
        indicator.color = .red
        indicator.backgroundColor = UIColor.gray
        indicator.translatesAutoresizingMaskIntoConstraints = false
        return indicator
    }

    func showLoadingIndicator() {
        activityIndicator.startAnimating()
        activityIndicator.isHidden = false
    }

    func hideLoadingIndicator() {
        activityIndicator.stopAnimating()
        activityIndicator.isHidden = true
    }
}

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

Есть мысли о том, как решить эту проблему.

1 Ответ

0 голосов
/ 21 января 2019

Это activityIndicator является вычисляемым свойством, поэтому каждый раз, когда вы ссылаетесь на вычисленное свойство, будет вызываться этот блок get. В итоге, как написано, каждый раз, когда вы ссылаетесь на activityIndicator, вы получаете новый экземпляр UIActivityIndicatorView, что, очевидно, не является вашим намерением.

Рассмотрим ваше showLoadingIndicator:

func showLoadingIndicator() {
    activityIndicator.startAnimating()
    activityIndicator.isHidden = false
}

Первая строка (с startAnimating) вернет новый UIActivityIndicatorView, а вторая строка (с isHidden) вернет еще одну. И ни один из них не будет тем, который вы предположительно добавили в свое подпредставление.

Этот activityIndicator действительно должен быть создан один раз и только один раз. К сожалению, вы не можете определить хранимое свойство в расширении, поэтому есть несколько подходов:

  1. Вы можете позволить UIViewController объявить сохраненное свойство и просто определить методы для его настройки, отображения и скрытия:

    protocol LoadingIndicatorProtocol: class {
        var loadingActivityIndicator: UIActivityIndicatorView? { get set }
    }
    
    extension LoadingIndicatorProtocol where Self: UIViewController {
    
        func addLoadingIndicator() {
            let indicator = UIActivityIndicatorView(style: .gray)
            indicator.hidesWhenStopped = true
            indicator.style = .whiteLarge
            indicator.color = .red
            indicator.backgroundColor = .gray
            indicator.translatesAutoresizingMaskIntoConstraints = false
    
            view.addSubview(indicator)
            // you might want to add the constraints here, too
    
            loadingActivityIndicator = indicator
        }
    
        func showLoadingIndicator() {
            loadingActivityIndicator?.startAnimating()
            loadingActivityIndicator?.isHidden = false
        }
    
        func hideLoadingIndicator() {
            loadingActivityIndicator?.stopAnimating()
            loadingActivityIndicator?.isHidden = true
        }
    }
    

    И тогда подкласс UIViewController просто должен определить свой собственный ivar для activityIndicator, например,

    class ViewController: UIViewController, LoadingIndicatorProtocol {
        var loadingActivityIndicator: UIActivityIndicatorView?
    
        override viewDidLoad() {
            super.viewDidLoad()
    
            addLoadingIndicator()
        }
    
        ...
    }
    
  2. Другой подход заключается в использовании связанных объектов через objc_getAssociatedObject и objc_setAssociatedObject для достижения поведения хранимых свойств:

    protocol LoadingIndicatorProtocol {
        var loadingActivityIndicator: UIActivityIndicatorView { get }
    }
    
    private var associatedObjectKey = 0
    
    extension LoadingIndicatorProtocol {
    
        var loadingActivityIndicator: UIActivityIndicatorView {
            if let indicatorView = objc_getAssociatedObject(self, &associatedObjectKey) as? UIActivityIndicatorView {
                return indicatorView
            }
    
            let indicator = UIActivityIndicatorView(style: .gray)
            indicator.hidesWhenStopped = true
            indicator.style = .whiteLarge
            indicator.color = .red
            indicator.backgroundColor = .gray
            indicator.translatesAutoresizingMaskIntoConstraints = false
            objc_setAssociatedObject(self, &associatedObjectKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    
            return indicator
        }
    
        func showLoadingIndicator() {
            loadingActivityIndicator.startAnimating()
            loadingActivityIndicator.isHidden = false
        }
    
        func hideLoadingIndicator() {
            loadingActivityIndicator.stopAnimating()
            loadingActivityIndicator.isHidden = true
        }
    }
    
...