Search code examples
c#iosxamarin.iosuiappearance

Where is the MonoTouch instancesRespondToSelector implementation?


The various UIAppearance proxy instances do not respond to selectors (as they are a proxy of their relevant types, not an actual instance of it), as discussed in this question and answer.

This makes it impossible to test for new iOS 6 features of the Appearance API. e.g. this appearance change will never execute as the code within the if check always returns false, even on iOS 6, because the instance it is checking is not a real instance but an appearance proxy.

if ( UINavigationBar.Appearance.RespondsToSelector( new Selector("setShadowImage:")))
    UINavigationBar.Appearance.ShadowImage = new UIImage();

The answer linked says to use the instancesRespondToSelector method. However I can't find it anywhere in the MT APIs. Am I just blind, or is there a different way to achieve this in MT?


Solution

  • There are few differences between both respondsToSelector: and instancesRespondToSelector:, here's a good description, except that the later is a static method.

    The answer, from your link, uses instancesRespondToSelector: on the real type, not its Appearance proxy. You can get the same result using RespondsToSelector that is already available in MonoTouch.

    if (new UINavigationBar ().RespondsToSelector( new Selector("setShadowImage:")))
        UINavigationBar.Appearance.ShadowImage = new UIImage();
    

    IOW it assume that if setShadowImage: is available then you can access it's proxy. That's not true for features that existed before UIAppearance was available (code might work but the result won't match your expectations).

    is there a different way to achieve this in MT?

    In many cases you can enable/disable several features by doing a single version check like this:

    if (UIDevice.CurrentDevice.CheckSystemVersion (6,0)) {
        // enable iOS6 new features
    } else {
        // fallback to iOS 5.x supported features
    }
    

    Right now instancesRespondToSelector: is not part of the public API that MonoTouch provide (it would need to be bound in every type, at least in done using generated bindings). However it's not hard to implement if you want it. You can use this code:

    IntPtr responds_handle = Selector.GetHandle ("instancesRespondToSelector:");
    IntPtr appearance_handle = new UINavigationBar ().ClassHandle; // *not* Handle
    IntPtr setShadowImage_handle = Selector.GetHandle ("setShadowImage:");
    bool result = Messaging.bool_objc_msgSend_IntPtr (appearance_handle, responds_handle, setShadowImage_handle);
    

    And you can turn it into a method if you need it in several places. Keep in mind that it will return the same answer as RespondsToSelector (for your specific question).