Overriding delegate property of UIScrollView in Swift (like UICollectionView does)

14,095

Solution 1

I think overriding an inherited property is something that's possible in Objective-C but not (at least currently) in Swift. The way I've handled this is to declare a separate delegate as a computed property of the correct type that gets and sets the actual delegate:

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

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

This way anything that calls the scroll view delegate normally still works, and you can call your particular delegate methods on self.myDelegate, like this:

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

Solution 2

You can do like this:

protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
    func someNewFunction()
}

class CustomScrollView: UIScrollView {

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

Hope this helps

Solution 3

My favoured method personally is not to subclass scrollviews directly but to make a UIView subclass containing and acting as delegate for a separate scrollview, then forward that scrollview's delegate messages on to the UIView subclass's own delegate where necessary. This also allows for the adding of custom controls outside of the area defined by the scroll view. It may seem a little inelegant compared to a direct subclass, but it does at least avoid unpleasant hacks.

Solution 4

Here is a solution for changing the type of the overriding properties in Swift. It is especially useful when you need to extend protocols of delegates.

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

This works as tested with Swift version 1.2. I hope it helps.

Share:
14,095
stringCode
Author by

stringCode

indie iOS developer in heart of London's silicon roundaboutwww.stringcode.co.uk

Updated on July 04, 2022

Comments

  • stringCode
    stringCode almost 2 years

    UIScrollView has a delegate property which conforms to UIScrollViewDelegate

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

    UICollectionView overrides this property with a different type UICollectionViewDelegate

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

    When I try to override UIScrollViews delegate with my protocol like so:

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

    the compiler gives me two warnings:

    • Property 'delegate' with type 'MyScrollViewDelegate?' cannot override a property with type 'UIScrollViewDelegate?'
    • 'unowned' cannot be applied to non-class type 'MyScrollViewDelegate?'

    How can I subclass UIScrollView and override type of delegate property (i.e. use a custom delegate protocol) ?

  • Juan Boero
    Juan Boero over 8 years
    could you please be more descriptive or post some code?, this looks interesting...
  • Zigii Wong
    Zigii Wong over 8 years
    This doesn't work on swift 2.1, any update to this approach?
  • ma11hew28
    ma11hew28 over 6 years
    This did not work for me. I have a pure Swift app. After adding this setter method to my subclass of UITextView, doing myTextView.delegate = self from the delegate (my view controller) didn't call the setter method I added. I didn't try the getter method.