The Problem
I love protocols and extensions as much as the next Swift developer, but when dealing with UIKit, sometimes you just need to subclass to share functionality or implement a reusable peice of UI. Recently I ran into this when subclassing UIScrollView
— to implement the behavior I wanted in my subclass, I had to become the scroll view delegate to receive updates on scrollViewDidScroll(_:)
. This meant conforming to UIScrollViewDelegate
and setting self.delegate = self
— so far no issues. But what if someone uses your subclass and needs to receive their own updates for scrollViewDidScroll(_:)
? It turns out there’s a neat trick that will let you set a delegate for yourself while preserving the UIKit API — i.e., someone else can set your delegate
property without messing with your own delegate.
The Solution
Below is an example of how to preserve the superclass API while using it for your own needs as well. It’s a UIScrollView
subclass that listens to its own scrollViewDidScroll(_:)
while allowing another class to be its delegate.
import UIKit
class LayeredScrollView: UIScrollView {
// 1. Use a private var to store the delegate you're publicly exposing
private weak var _delegate: UIScrollViewDelegate?
// 2. Override the super `delegate` property, getting and setting your private backing var
override var delegate: UIScrollViewDelegate? {
get {
return _delegate
}
set {
_delegate = newValue
}
}
override init(frame: CGRect) {
super.init(frame: frame)
// 3. Set the delegate of super to `self` — super maintains the old implementation
super.delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension LayeredScrollView: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 4. Implement whatever you need! (Hopefully more than a print 😝)
print(scrollView.contentOffset)
// 5. Call your own delegate's version of the method — it's an optional method, hence the ?
delegate?.scrollViewDidScroll?(scrollView)
}
// 5. Continued... read on
}
By overriding the property like this and using super
’s version for ourself, we can still have another class set themselves as our delegate, while having our own implementation called by super
.
The Downside
Point 5. says to “read on” and that’s because there’s a caveat, and some missing code in my sample. Since you’re the superclass’s delegate, you need to implement every delegate method and forward those to your own delegate. This can result in a lot of boilerplate, but isn’t hard to implement — just copy and paste from the header file 😄. If you’re implementing a subclass like this for a framework, it’s well worth the effort to preserve the existing UIScrollView
API. Even interally, you never know when someone might set the delegate
property and spend hours (or minutes if you’re quite intelligent) wondering why they broke your class. All in all, it’s a neat trick when you need to subclass.