Dan Abramov
Dan Abramov

Reputation: 268245

How to proxy UIKit delegates in MonoTouch?

I need to chain into a UIScrollView's delegate (it's not in my code and I have no control over it) and handle some of its methods, while passing all events to the previous delegate.

My naïve implementation was this proxy class:

public class ScrollViewProxyDelegate : UIScrollViewDelegate
{
    private UIScrollViewDelegate _realDelegate;

    public ScrollViewProxyDelegate (UIScrollViewDelegate realDelegate)
    {
        _realDelegate = realDelegate;
    }

    public override void DecelerationStarted (UIScrollView scrollView)
    {
        _realDelegate.DecelerationStarted (scrollView);
    }

    public override void DecelerationEnded (UIScrollView scrollView)
    {
        _realDelegate.DecelerationEnded (scrollView);
    }

    // ...
 }

This didn't work out because scrollView.Delegate I passed to ScrollViewProxyDelegate constructor turned out to be null because this particular delegate was in a different class tree. The property I'm after is WeakDelegate, but it's an NSObject.

I read this and I'm still confused. I guess I could just call Objective C selectors on this NSObject in each method but could there be a less verbose way?

Update 1

I just tried proxying PerformSelector and RespondsToSelector but it caused unrecognized selector crashes.

Update 2

Okay, apparently NSProxy is what people use for this. Investigating.

Update 3

Ouch, no NSProxy in MonoTouch.

Update 4

I ended up using Key Value Observing to watch my view's contentOffset. I should've thought about it earlier! Still, I'm curious how to implement a proxy if I ever need it.

What's the easiest way to hook into a view's delegate?

Upvotes: 0

Views: 483

Answers (1)

Greg Munn
Greg Munn

Reputation: 526

I can't quite follow your need for a proxy if the delegate that you're trying to proxy is null, or did you mean that the delegate that you needed to proxy was the scroll view's WeakDelegate?

A WeakDelegate is essentially any NSObject, and any methods that you need to implement must be 'exported' in order for the delegate's owner to be able to invoke them.

[Export("scrollViewDidEndDecelerating:")]
public void MyDecelerationEndedMethod(UIScrollView scrollView)
{
  ...
}

You can add methods like this onto any NSObject, a view controller for example. If you needed to use a WeakDelegate as the subject of your proxy you'd have to query it to see it if responds to the selector and then perform the selector - which I think is what you were meaning.

However, I don't think you'd be able to write a generic proxy that could handle any delegate because although the proxy gets RespondsToSelector called, it doesn't get PerformSelector called - the selector is sent directly to the proxy and not via PerformSelector. You'd have to write a proxy that implements exactly the same methods as the delegate you want to proxy.

The best I could come up with is something like the following where you'd have to implement each method that the delegate implements.

public class TestProxy : NSObject
{
    private NSObject realDelegate;

    public TestProxy(NSObject realDelegate)
    {
        this.realDelegate = realDelegate;
    }

    public override bool RespondsToSelector(MonoTouch.ObjCRuntime.Selector sel)
    {
        Console.WriteLine("Query : " + sel.Name);
        return this.realDelegate.RespondsToSelector(sel);
    }

    [Export("tableView:didSelectRowAtIndexPath:")]
    public void RowSelected(UITableView tableView, NSIndexPath indexPath)
    {
        // invoke method on realDelegate either by casting to the correct type or by using
        // reflection to find the method that matches the export and pass this method's arguments.
        // which way you implement depends on your needs and what you know about the delegate being
        // proxied - casting would be much faster than reflection.
    }
}

Because you're implementing all the methods that the delegate implements, the RespondsToSelector in redundant and not required.

Upvotes: 3

Related Questions