Doug Smith Doug Smith - 4 months ago 98
Swift Question

In Swift, how do I have a UIScrollView subclass that has an internal and external delegate?

I'm subclassing

UIScrollView
to add some features such as double tap to zoom and an image property for gallery purposes. But in order to do the image part my subclass has to be its own delegate and implement the
viewForZoomingInScrollView
.

But then when someone uses my scroll view subclass, they might like to get delegate notifications as well to see
scrollViewDidScroll
or what have you.

In Swift, how do I get both of these?

Answer

Here is a Swift version of this pattern:

Although forwardInvocation: is disabled in Swift, we can still use forwardingTargetForSelector:

class MyScrollView: UIScrollView {

    class _DelegateProxy: NSObject, UIScrollViewDelegate {
        weak var _userDelegate: UIScrollViewDelegate?

        override func respondsToSelector(aSelector: Selector) -> Bool {
            return super.respondsToSelector(aSelector) || _userDelegate?.respondsToSelector(aSelector) == true
        }

        override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? {
            if _userDelegate?.respondsToSelector(aSelector) == true {
                return _userDelegate
            }
            else {
                return super.forwardingTargetForSelector(aSelector)
            }
        }

        func viewForZoomingInScrollView(scrollView: MyScrollView) -> UIView? {
            return scrollView.viewForZooming()
        }

        // Just a demo. You don't need this.
        func scrollViewDidScroll(scrollView: MyScrollView) {
            scrollView.didScroll()
            _userDelegate?.scrollViewDidScroll?(scrollView)
        }
    }

    private var _delegateProxy =  _DelegateProxy()

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.delegate = _delegateProxy
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        super.delegate = _delegateProxy
    }

    override var delegate:UIScrollViewDelegate? {
        get {
            return _delegateProxy._userDelegate
        }
        set {
            self._delegateProxy._userDelegate = newValue;
            /* It seems, we don't need this anymore.
            super.delegate = nil
            super.delegate = _delegateProxy
            */
        }
    }

    func viewForZooming() -> UIView? {
        println("self viewForZooming")
        return self.subviews.first as? UIView // whatever
    }

    func didScroll() {
        println("self didScroll")
    }
}
Comments