Swift: создание подкласса UITextView или UICollectionView и правильная инициализация

#ios #uiview #uitextview #swift

#iOS #uiview #uitextview #swift

Вопрос:

Проблема с подклассом UITextView (и UICollectionView) заключается в том, что назначенный конструктор является «initWithFrame». Но в реальной жизни, когда он загружается из раскадровки, будет вызван initWithCoder .

 class BorderedTextView: UITextView {
    //will be called
    init(coder: NSCoder?){
       //constant values here is not an option
       super.init(frame: CGRectMake(0,0,100,100), textContainer: nil)
    }
    //will not be called
    init(frame: CGRect, textContainer: NSTextContainer!) {
        super.init(frame: frame, textContainer: textContainer)
    }
}
  

В результате я не могу вызвать какой-либо код настройки пользовательского интерфейса при инициализации и предоставить любое значение инициализации для переменных Swift, кроме значений по умолчанию.

Я полагаю, что эту проблему можно временно решить, извлекая размер кадра из «coder», но я не нашел для этого ключа.

Есть идеи лучше, чем жестко прописанные значения фреймов?

Комментарии:

1. Я ожидаю, что super.init(coder: coder) это сработает — и для подклассов UIView или UILabel это работает (по крайней мере, компилятор не жалуется). Но для подклассов UITextView он завершается ошибкой с сообщением об ошибке «Необходимо вызвать назначенный инициализатор …». Ошибка Swift?

2. Возможным обходным путем может быть выполнение инициализации awakeFromNib вместо этого.

3. Вы правы, похоже, это работает для UIView (я обновил вопрос). Но у меня такая же проблема с UICollectionView, так что проблема не только в UITextView.

4. Разница, по-видимому, заключается в том, что и UITextView, и UICollectionView имеют свой «собственный» назначенный инициализатор (в отличие, например, от UILabel). Я все еще думаю, что это ошибка Swift (и о ней следует сообщить Apple), потому что тот же шаблон работает в Objective-C.

5. Отправил его в Apple. Спасибо за awakeFromNib обходной путь, он идеально соответствует моим текущим требованиям.

Ответ №1:

(Из моих комментариев выше:)Это похоже на ошибку Swift. initWithCoder: вызывается, когда представление (или контроллер представления) создается из раскадровки или файла Nib, и переопределение этого метода работает в Objective-C:

 - (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        // Initialization code
    }
    return self;
}
  

Но эквивалентный код Swift

 class BorderedTextView: UITextView {
    init(coder: NSCoder!) {
        super.init(coder: coder)
    }
}
  

сбой с сообщением об ошибке «необходимо вызвать назначенный инициализатор суперкласса ‘UITextView'».

Эта проблема возникает со всеми подклассами UIView , которые имеют свой собственный назначенный инициализатор (например UITextView , UICollectionView ). С другой стороны, проблема не возникает с подклассами UILabel , у которых нет назначенного инициализатора. Язык Swift очень строг в отношении вызова назначенного инициализатора суперклассов, но должен быть способ переопределения initWithCoder: для всех пользовательских UIView подклассов, поэтому я считаю это ошибкой Swift.

В качестве обходного пути вы можете выполнить пользовательскую инициализацию в

 override func awakeFromNib() {
    super.awakeFromNib()
    // ...
}
  

Обновление для Swift 1.2: это, по-видимому, было исправлено. Параметр
изменен, он больше не является неявно развернутым необязательным. Итак, это
компилируется и работает должным образом (протестировано с Xcode 6.4):

 class BorderedTextView: UITextView {
    required init(coder: NSCoder) {
        super.init(coder: coder)

        // ...
    }
}
  

Обновление для Swift 2 (Xcode 7): init(coder:) теперь является отказоустойчивым
инициализатором:

 class BorderedTextView: UITextView {
    required init?(coder: NSCoder) {
        super.init(coder: coder)

        // ...
    }
}
  

Ответ №2:

Более полный ответ для Swift 3:

 class ValidatedTextField:UITextField, UITextFieldDelegate
{
    required override init(frame: CGRect)
    {
        super.init(frame: frame)
        //custom code
        self.delegate = self
    }

    required init?(coder: NSCoder)
    {
        super.init(coder: coder)
        //custom code
        self.delegate = self
    }
  

Комментарии:

1. OP запрашивает UITextView, а не UITextField.