2016-06-28 4 views
1

Я хочу украсить UIViewController с возможностью настройки его интерфейса, когда метод setInteractionEnabled вызывается из другого класса (например, Network State Manager). Все изменения (если они есть) должны быть указаны в конкретном контроллере путем переопределения onInteractionChanged. Вот мой код:Быстрое закрытие в расширении протокола

import Foundation 

typealias InteractionClosure = ((enabled: Bool) -> Void) 

protocol Interaction: class { 

    var onInteractionChanged: InteractionClosure? { get set } 

    func setInteractionEnabled(enabled: Bool) 

} 

extension Interaction where Self: UIViewController { 

    // Default: Do nothing 
    // Throws: - Extensions may not contain stored properties 
    var onInteractionChanged: InteractionClosure? = nil 

    func setInteractionEnabled(enabled: Bool) { 
     onInteractionChanged?(enabled: enabled) 
    } 

} 

extension UIViewController : Interaction {} 

Как добавить реализацию по умолчанию для onInteractionChanged?

+1

Ошибка говорит об этом - расширения не могут содержать сохраненные свойства. Вы должны сделать это вычисляемым свойством, которое просто возвращает 'nil' при доступе. Хотя в протоколе необходимо обеспечить установленную способность свойства '{get set}'? В противном случае вам придется иметь пустой сеттер для реализации свойства по умолчанию. Я хотел бы сделать свойство '{get}' и дать соответствующим типам выбор того, хотят ли они просто переопределить его с помощью рассчитанного свойства (только для получения) или использовать сохраненное свойство (gettable и устанавливаемый). – Hamish

ответ

4

Отвечая на мой собственный вопрос является то, что я обычно не делаю, но вот мое решение:

typealias InteractionClosure = (enabled: Bool) -> Void 

protocol Interaction: class { 

    func addOnInteractionChanged(closure: InteractionClosure) 
    func setInteractionEnabled(enabled: Bool) 

} 

extension Interaction where Self: UIViewController { 

    func addOnInteractionChanged(closure: InteractionClosure) { 
     onInteractionChanged = closure 
    } 

    func setInteractionEnabled(enabled: Bool) { 
     onInteractionChanged?(enabled: enabled) 
    } 

    // MARK: - Private 

    private var onInteractionChanged: InteractionClosure? { 
     get { 
      let wrapper = 
       objc_getAssociatedObject(self, &icAssociationKey) as? ClosureWrapper 
      return wrapper?.closure 
     } 
     set(newValue) { 
      objc_setAssociatedObject(self, 
            &icAssociationKey, 
            ClosureWrapper(newValue), 
            .OBJC_ASSOCIATION_RETAIN) 
     } 
    } 

} 

extension UIViewController : Interaction {} 

// Helpers 

private var icAssociationKey: UInt8 = 0 

private class ClosureWrapper { 
    var closure: InteractionClosure? 

    init(_ closure: InteractionClosure?) { 
     self.closure = closure 
    } 
} 

класса Клиент:

class LoginViewController: UIViewController { 

    // MARK: - Lifecycle 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.setup() 
    } 

    // MARK: - Private 

    private func setup() { 
     // ... 

     addOnInteractionChanged { [unowned self] (enabled) in    
      self.signInButton.enabled = enabled 
      self.activityIndicatorView.hidden = !enabled 
     } 
    } 

} 

В менеджер классе:

visibleViewController?.setInteractionEnabled(true) 
+0

Решение работает как шарм. Но я запутался в 'icAssociationKey'. Зачем нужен 'icAssociationKey'. Что, если я удалю его? –

0

Если вы хотите, чтобы собственность имела только { get } способность, вы можете использовать:

protocol TestProtocol { 
    var testClosure: ((_ parameter: Bool) -> Void)? { get } 
} 

extension TestProtocol { 
    var testClosure: ((_ parameter: Bool) -> Void)? { 
     return { parameter in 
      print(parameter) 
     } 
    } 
} 
Смежные вопросы