Переопределение свойства делегата UIScrollView в Swift (например, UICollectionView)

UIScrollView имеет свойство делегата, которое соответствует UIScrollViewDelegate.

protocol UIScrollViewDelegate : NSObjectProtocol {
    //...
}
class UIScrollView : UIView, NSCoding {
    unowned(unsafe) var delegate: UIScrollViewDelegate?
    //...
}

UICollectionView переопределяет это свойство другим типом UICollectionViewDelegate

protocol UICollectionViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
   //...
}

class UICollectionView : UIScrollView {
     unowned(unsafe) var delegate: UICollectionViewDelegate?
   //...
}

Когда я пытаюсь переопределить делегат UIScrollViews своим протоколом следующим образом:

protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
    //...
}

class MyScrollView: UIScrollView {
    unowned(unsafe) var delegate: MyScrollViewDelegate?

}

компилятор дает мне два предупреждения:

  • Свойство «делегат» с типом «MyScrollViewDelegate?» не может переопределить свойство с типом «UIScrollViewDelegate?»
  • «unowned» не может применяться к неклассу «MyScrollViewDelegate?»

Как я могу создать подкласс UIScrollView и переопределить тип свойства делегата (т. Е. Использовать собственный протокол делегата)?


person stringCode    schedule 08.09.2014    source источник


Ответы (6)


Я думаю, что переопределение унаследованного свойства - это то, что возможно в Objective-C, но не (по крайней мере, в настоящее время) в Swift. Я справился с этим, объявив отдельный делегат как вычисляемое свойство правильного типа, которое получает и устанавливает фактический делегат:

@objc protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
    func myHeight() -> CGFloat
    // ...
}

class MyScrollView: UIScrollView {
    var myDelegate: MyScrollViewDelegate? {
        get { return self.delegate as? MyScrollViewDelegate }
        set { self.delegate = newValue }
    }
}

Таким образом, все, что вызывает делегат представления прокрутки, обычно по-прежнему работает, и вы можете вызывать свои конкретные методы делегата на self.myDelegate, например:

if let height = self.myDelegate?.myHeight() {
    // ...
}
person Nate Cook    schedule 08.09.2014
comment
Это не работает на Swift 2.1, есть ли обновления этого подхода? - person Zigii Wong; 27.01.2016

Сделать можно так:

protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
    func someNewFunction()
}

class CustomScrollView: UIScrollView {

    weak var myDelegate: ExtendedScrollViewDelegate?
    override weak var delegate: UIScrollViewDelegate? {
        didSet {
            myDelegate = delegate as? ExtendedScrollViewDelegate
        }
    }
}

Надеюсь это поможет

person 谢仪伦    schedule 07.09.2016

Лично мой любимый метод - не создавать подклассы scrollviews напрямую, а создавать подкласс UIView, содержащий и действующий в качестве делегата для отдельного scrollview, а затем пересылать сообщения делегата этого scrollview собственному делегату подкласса UIView, где это необходимо. Это также позволяет добавлять настраиваемые элементы управления за пределами области, определяемой просмотром прокрутки. Это может показаться немного неэлегантным по сравнению с прямым подклассом, но, по крайней мере, позволяет избежать неприятных взломов.

person Ash    schedule 28.09.2014
comment
не могли бы вы быть более наглядным или опубликовать код? Это выглядит интересно ... - person Juan Boero; 17.11.2015

Вот решение для изменения типа переопределяющих свойств в Swift. Это особенно полезно, когда вам нужно расширить протоколы делегатов.

@objc protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
     func someNewFunction()
}

class CustomScrollView: UIScrollView {

    weak var delegateInterceptor: ExtendedScrollViewDelegate?
    override var delegate: UIScrollViewDelegate! {
        didSet {
            if let newValue = delegate {
                let castedDelegate = unsafeBitCast(delegate, ExtendedScrollViewDelegate.self)
                delegateInterceptor = castedDelegate
            }
            else {
                delegateInterceptor = nil
            }
        }
    }
}

Это работает так же, как и в Swift версии 1.2. Я надеюсь, что это помогает.

person rober    schedule 27.05.2015

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

func setDelegate(delegate:UITableViewDelegate?){
     self.delegateInterceptor = delegate;
}

swift компилирует свойство в метод, как это делает Objective-c.

person thilong    schedule 17.09.2014
comment
У меня это не сработало. У меня чистое приложение Swift. После добавления этого метода установки к моему подклассу UITextView выполнение myTextView.delegate = self из делегата (моего контроллера представления) не вызвало добавленный мной метод установки. Я не пробовал метод получения. - person ma11hew28; 19.08.2017

Рассмотрим следующую ситуацию:

class BaseProp {}

class Base {
    var prop: BaseProp
}

Затем, если вы сделаете это:

class DerivedProp: BaseProp {}

class Derived: Base {
    override var prop: DerivedProp
}

Тогда if нарушит принципы создания подклассов (а именно, принцип подстановки Лискова). По сути, вы ограничиваете область действия «var prop» от более широкого типа «BaseProp» до более узкого типа «DerivedProp». Тогда возможен такой код, который не имеет смысла:

class UnrelatedProp: BaseProp {}

let derived = Derived()
let base = derived as Base
base.prop = UnrelatedProp()

Обратите внимание, что мы назначаем экземпляр UnrelatedProp свойству, что не имеет смысла для экземпляра Derived, с которым мы на самом деле работаем. ObjectiveC допускает такую ​​двусмысленность, а Swift - нет.

person Igor Vasilev    schedule 03.10.2018