3

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?

Community
  • 1
  • 1
Tyson
  • 14,726
  • 6
  • 31
  • 43

1 Answers1

4

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).

Community
  • 1
  • 1
poupou
  • 43,413
  • 6
  • 77
  • 174
  • I can't seem to get your first solution to work - I have MT 6.0.8 installed, and there is no static `RespondsToSelector` method of `UINavigationBar` or any of its base classes. Unless you meant to use the instance version? While this is possible, it means constructing a dummy UINavigationBar, something I want to avoid. As for the version checks, yeah thats my current workaround. However I would rather check for features than for versions, a lesson learned from web development years ago. I guess I will go with the manual interop code you posted for now, thanks! Feature request? – Tyson Jan 26 '13 at 23:24
  • You're right it's an instance method, I'll update my answer. Also feel free to open a bug report (enhancements) for anything that's missing and would help you develop faster/better. We could expose something that does not requires an instance to be created, – poupou Jan 27 '13 at 01:50