web-dev-qa-db-fra.com

Remplacement de la propriété de délégué de UIScrollView dans Swift (comme le fait UICollectionView)

UIScrollView a une propriété delegate conforme à UIScrollViewDelegate.

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

UICollectionView substitue cette propriété avec un type différent UICollectionViewDelegate

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

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

Lorsque j'essaie de remplacer le délégué UIScrollViews avec mon protocole comme suit:

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

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

}

le compilateur me donne deux avertissements: 

  • Propriété 'delegate' avec le type 'MyScrollViewDelegate?' ne peut pas remplacer une propriété avec le type 'UIScrollViewDelegate?'
  • 'non propriétaire' ne peut pas être appliqué au type non-classe 'MyScrollViewDelegate?'

Comment puis-je sous-classer UIScrollView et remplacer le type de propriété de délégué (c'est-à-dire utiliser un protocole de délégué personnalisé)?

35
stringCode

Je pense que redéfinir une propriété héritée est une chose possible dans Objective-C mais pas (du moins actuellement) dans Swift. La façon dont j'ai géré cela est de déclarer un délégué distinct en tant que propriété calculée du type correct qui obtient et définit le délégué actual:

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

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

De cette façon, tout ce qui appelle le délégué de la vue de défilement fonctionne toujours normalement et vous pouvez appeler vos méthodes de délégué particulières sur self.myDelegate, comme ceci:

if let height = self.myDelegate?.myHeight() {
    // ...
}
18
Nate Cook

Voici une solution pour changer le type des propriétés prioritaires dans Swift. Cela est particulièrement utile lorsque vous devez étendre les protocoles des délégués.

@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
            }
        }
    }
}

Cela fonctionne comme testé avec Swift version 1.2. J'espère que ça aide.

3
prodos

Vous pouvez faire comme ça:

protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
    func someNewFunction()
}

class CustomScrollView: UIScrollView {

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

J'espère que cela t'aides 

3
谢仪伦

Ma méthode préférée personnellement ne consiste pas à sous-classer directement les vues de défilement, mais à créer une sous-classe UIView contenant et agissant en tant que délégué pour une vue de défilement distincte, puis à transférer les messages de délégué de cette vue à son propre délégué, le cas échéant. Cela permet également l'ajout de contrôles personnalisés en dehors de la zone définie par la vue de défilement. Cela peut sembler un peu inélégant par rapport à une sous-classe directe, mais cela évite au moins les piratages désagréables.

2
Ash

Considérez la situation suivante:

class BaseProp {}

class Base {
    var prop: BaseProp
}

Alors si tu fais ça:

class DerivedProp: BaseProp {}

class Derived: Base {
    override var prop: DerivedProp
}

Alors, si casserait les principes de sous-classes (à savoir le principe de substitution de Liskov). En gros, vous limitez la portée de "var prop" d'un type "BaseProp" plus large à un type "DerivedProp" plus étroit. Alors ce genre de code serait possible, ce qui n’a aucun sens:

class UnrelatedProp: BaseProp {}

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

Notez que nous affectons une instance de UnrelatedProp à la propriété, ce qui n’a aucun sens pour l’instance dérivée avec laquelle nous opérons réellement. ObjectiveC permet ce type d'ambiguïté, mais pas Swift.

1
Igor Vasilev

Vous pouvez remplacer la méthode get et set par la fonction declare comme:

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

Swift compilez la propriété à la méthode comme le fait Objective-c.

0
thilong