Подход к проектированию для создания подкласса UITextField с поведением по умолчанию в методах делегата

Я создал подклассы от класса UITextField, поэтому я могу предоставить некоторые встроенные функции для своего приложения. Я изменил внешний вид, чтобы обеспечить только подчеркивание границы для дизайна UX. Кроме того, я хочу использовать этот элемент управления в ситуациях, когда есть средство выбора (список выбора, средство выбора даты / времени). В этих случаях я хочу предотвратить редактирование, НО мне все равно нужно реагировать на события касания. Для этого я добавил проверяемые свойства, чтобы управлять им из IB.

Я могу легко предотвратить появление меню копирования / вставки, выполнив следующие действия:

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if isFirstResponder && disableEditing {
        DispatchQueue.main.async(execute: {
            (sender as? UIMenuController)?.setMenuVisible(false, animated: false)
        })
        return false
    }

    return super.canPerformAction(action, withSender: sender)
}

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

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return false
}

Вопрос в том, как обеспечить такое поведение по умолчанию в подклассе? Вы можете сделать:

self.delegate = self

Однако это приводит ко всевозможным недостаткам, так что это не лучшее решение.

Другое решение - реализовать базовый подкласс UIViewController (MyBaseViewController), но позже это приведет к запутанному и монолитному коду.

Было бы лучше, если бы существовал чистый способ обеспечить такое поведение по умолчанию в инкапсулированном виде.

Очевидно, есть несколько других способов исправить это (например, написать тот же код в 10 контроллерах представления). По сути, кажется, что должен быть способ повторно использовать код делегата при создании подклассов таких элементов управления.

У кого-нибудь есть идеи ??


person Shawn    schedule 26.10.2017    source источник
comment
А как насчет добавления свойства к подклассу, например hasSelectedOption. Вы должны установить для него значение true / false в зависимости от других действий с элементом управления (например, выбрать параметр из раскрывающегося списка), а затем запустить другой вход в свой подкласс в зависимости от значения этого свойства. Я правильно понимаю ваш вопрос?   -  person Alex    schedule 26.10.2017
comment
@Alex не совсем. Я знаю, как управлять логикой на основе свойства. Я делаю это уже для управления визуальными аспектами элемента управления (подчеркивание границы, цвета и т. Д.). Однако проблема в том, как конкретно обрабатывать логику делегирования. Ответ от Алекса - это именно то, с чем я борюсь.   -  person Shawn    schedule 26.10.2017


Ответы (1)


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

Сделать это можно двумя способами. Это самый простой.

class CustomTextField: UITextField, UITextFieldDelegate
{
    var externalDelegate: UITextFieldDelegate?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        delegate = self
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        externalDelegate?.textFieldDidBeginEditing?(textField)
    }
}

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

override var delegate: UITextFieldDelegate? {
    didSet {
        if delegate === self {
            return
        }            
        externalDelegate = delegate
        delegate = oldValue
    }
}

Плюсы

  • Хорошо работает при настройке из кода и из раскадровки.
  • Это прозрачно с точки зрения клиентского кода.

Минусы

Недостатком этого подхода является то, что вам нужно реализовать каждый метод из протокола UITextFieldDelegate в своем подклассе UITextField, чтобы полностью поддерживать делегирование. К счастью, их всего 8, и вряд ли вам понадобятся все, поэтому вы можете сузить его до необходимого подмножества.

person Kamil Szostakowski    schedule 26.10.2017
comment
Хорошее предложение. Я наткнулся на делегата многоадресной рассылки и подумал, что это может быть подходящим вариантом. Однако мне особенно нравится ваш подход, который делает его прозрачным с точки зрения клиентского кода. Спасибо, что нашли время дать ответ. - person Shawn; 26.10.2017
comment
Очень хороший ответ (во время моих полудневных поисков) по этой теме. - person inexcii; 24.06.2020
comment
Только одно небольшое внимание, чтобы напомнить, во-первых, что если внешний объект (например, UIViewController) непреднамеренно захватит делегата (например, customTextField.delegate = self), CustomTextField больше не получит вызов делегата. - person inexcii; 24.06.2020