Протокол соответствия стандарту ios swift
Я пытаюсь научиться быстро и хочу использовать протоколно-ориентированный подход программирования. То, чего я хочу достичь, просто, но я не мог найти никакого пути.
допустим, у меня есть Outlet, который является текстовым полем. Я хочу, чтобы текстовое поле соответствовало протоколу, подобному протоколу ValidatesName. Есть ли способ сделать это? Я не хочу делать новый класс, который подкласс UITextField и соответствует протоколу. Я хочу использовать для этого конкретного свойства.
@IBOutlet weak var nameTextField:UITextField!<Conforms ValidatesName>
@IBOutlet weak var emailTextField:UITextField!<Conforms ValidatesEmail>
@IBOutlet weak var passwordTextField:UITextField!<Conforms ValidatesPassword>
Спасибо
2 ответа
Ваша проблема заключается в том, что хотя вы можете добавить соответствие протокола через расширение, расширение применяется к классу, а не к экземпляру этого класса. Это означает, что вы можете сказать что-то вроде:
extension UITextField: ValidatesName {...}
Но это заставит все экземпляры UITextField соответствовать ValidatesName
Точно так же вы могли бы также сказать
extension UITextField: ValidatesEmail{...}
Но теперь все экземпляры UITextField будут соответствовать ValidatesName
а также ValidatesEmail
,
Имея отдельный Validates...
протоколы не кажутся подходящими в любом случае. Основная подпись вашего протокола - что-то вроде var isValid: Bool
; это не меняется между именем и электронной почтой. Что меняется, так это логика проверки, и это должно где-то жить. Это, в сочетании с тем, что вам нужны подклассы для работы с Интерфейсным Разработчиком, предполагает, что единый протокол 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")
}
}
К сожалению, есть несколько ограничений, препятствующих этому:
IBOutlets должен ссылаться на классы, которые наследуются от NSObject, возможно, для включения архивирования / декодирования, поэтому вы не можете использовать только протокол для типа IBOutlet
В Swift нет способа объявить тип переменной как комбинацию конкретного соответствия типа и протокола, как вы делаете в своем примере.
В Objective-C вы можете объявить конкретное соответствие протокола типа +, но протоколы IBOutlets все равно игнорируют протоколы, возможно потому, что соответствие протокола проверяется во время выполнения, и Xcode / Interface Builder не обязательно знает, будет ли объект в конечном итоге соответствовать протоколу.