2016-11-24 14 views
1

Я пытаюсь научиться быстро и хочу использовать подход, ориентированный на протокол. То, что я хочу достичь, прост, но я не мог найти никакого способа.ios swift класс, соответствующий протоколу

позволяет сказать, что у меня есть выход, который является текстовым полем. Я хочу, чтобы это текстовое поле соответствовало протоколу, как протокол ValidatesName. Есть ли способ сделать это? Я не хочу создавать новый класс, который подкласс UITextField и соответствует протоколу. Я хочу использовать это свойство.

@IBOutlet weak var nameTextField:UITextField!<Conforms ValidatesName> 
@IBOutlet weak var emailTextField:UITextField!<Conforms ValidatesEmail> 
@IBOutlet weak var passwordTextField:UITextField!<Conforms ValidatesPassword> 

Благодаря

+0

Но проблема в том, что UITextField класс не соответствует нужного протокола, принятие протокола на основе внедрения O f методы и свойства, определенные в протоколе –

+0

Если я правильно понял, я уже могу определить некоторый метод, подобный этому. Я думал, что могу сделать это для переменной. > – kekkeme

ответ

3

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

extension UITextField: ValidatesName {...} 

Но это сделает все экземпляры UITextField соответствуют ValidatesName

Кроме того, вы могли бы также сказать

extension UITextField: ValidatesEmail{...} 

Но теперь все экземпляры UITextField будут соответствовать ValidatesNameиValidatesEmail.

Имея отдельные протоколы Validates..., в любом случае не похоже на правильный подход. Основная сигнатура вашего протокола - это что-то вроде var isValid: Bool; это не изменяется между именем и электронной почтой. Что такое изменение логики проверки, и это должно где-то жить. Это, в сочетании с тем, что вам нужны подклассы для работы с Interface Builder, предполагает, что один протокол Validatable, который может быть принят вашими различными подклассами, является более разумным подходом.

protocol Validatable { 
    var isValid: Bool { get } 
} 

Теперь вы можете определить подклассы UITextField, которые соответствуют этому протоколу (Вы можете добавить соответствие с помощью расширения для подкласса, если вы хотите, я просто хотел, чтобы сэкономить место здесь)

class NameTextField: UITextField, Validatable { 

    var isValid: Bool { 
     get { 
      guard let text = self.text else { 
       return false 
      } 

      return !text.isEmpty 
     } 
    } 
} 

class EmailTextField: UITextField, Validatable { 
    var isValid: Bool { 
     get { 
      guard let text = self.text else { 
       return false 
      } 

      return text.contains("@") 
     } 
    } 
} 

Теперь вы можете добавить текстовые поля в массив, и есть что-то вроде:

@IBOutlet weak var nameTextField:NameTextField! 
@IBOutlet weak var emailTextField:EmailTextField! 

var validatableFields:[Validatable]! 

override func viewDidLoad() { 
    super.viewDidLoad() 

    self.validatableFields = [nameTextField,emailTextField] 
} 

... 

for field in validateableFields { 
    if !field.isValid() { 
     print("A field isn't valid") 
    } 
} 
+0

Как я могу написать тип массива IBOutletCollection как только протоколы, я попытался, но я получил ошибку. Я получаю ошибку, если пытаюсь реализовать ее как [ProtocolName]. – kekkeme

+0

Вы правы, извините. Я обновил свой ответ. Вам нужно будет построить массив самостоятельно. Это одно из тех мест, где наследие Objective-C ограничивает то, что вы можете сделать в Swift. – Paulw11

+0

Это хороший ответ для использования в качестве массива для предотвращения множества охранников внутри viewcontroller. Единственное, что я изменил, это то, что nameTextField и emailTextField - это виды. Они не ведущие (MVP), они не являются моделью просмотра (MVVM), поэтому я немного изменюсь от нее, но спасибо за идею validatableFields. – kekkeme

1

К сожалению, есть несколько ограничений, препятствующие этому:

  1. IBOutlets должны относиться к классам, которые наследуют от NSObject, вероятно, для того, чтобы архивирования/декодирования, так что вы не можете используйте только протокол для типа IBOutlet

  2. В Swift нет способа объявить тип переменной как сочетание конкретного типа e + протокол, способ, который вы делаете в своем примере

  3. В Objective-C вы можете объявить конкретный тип + соответствие протокола, но протоколы в любом случае игнорируются протоколами IBOutlets, возможно, потому что согласованность протокола проверяется во время выполнения, и это isn Обязательно известно Xcode/Interface Builder во время разработки, будет ли объект в конечном итоге соответствовать протоколу.