Monday, September 30, 2013

The invisible property of UIViewController

Popular wisdom says "things happen for a reason". However experience has proved me that when it comes to programmers, that is not always true. Things can just happen for no reason at all. That's why we require feedback, here is one and I assure you is in good spirit. So let's me write about something I found when building an app for iOS using Xamarin/Mono.

The invisible property.

Ranging from windows forms (desktop or Windows Mobile) to WPF, Silverlight/XAML on the web or windows phone. ASP.NET Web Forms or Razor, MVC or classic. One thing I can say for sure: any visual element has a way to check for its visual status.

Call it "Visible" in windows forms, "Visibility" in Silverlight/WPF. There is always an intuitive, simple and trivial way to know if something - that might be visible - it is visible at a particular moment or not. You can't imagine my surprise when I realized that the UIViewController in iOS does not provide such mechanism.

Of course it would not seem necessary because the  UIViewController is not a visible component itself, it is a controller that manages views (root view and subviews) and acts on behalf of them when the user interacts with them. However, in reality we require too often to know if the given "screen" is still visible or not. Examples? asynchronous  callbacks...

Let me add that I am not using the APIs directly with Objective-C, instead I am using Mono to write my app in C#. I checked however, that this is not a deficiency of Mono, it is "by design" or at least it seems so.

So lets explore the alternatives. In order for you to know if a UIViewController (actually its main view) is visible. You can do this:
if (this.IsViewLoaded && this.View.Window != null)
    //is visible
   else {
    //not visible

We check first if the current view is already loaded, then we check whether  the parent window is null or not. The reason for the check is to prevent the system from loading the view when asking for its status. And of course that "loading-when-you-ask" is something that is worth an extra discussion but let's skip it today.

But wait, there is more...

The invocation above should be done from the main thread, because we are touching visual components. And we could clean this up a bit and use an extension method. Like this:
public static bool IsVisible(this UIViewController view){
   bool value = false;
   view.InvokeOnMainThread (() => {
    value = view.IsViewLoaded && view.View.Window != null;});
   return value;
It's easy, it's not clean, but it works. Now, bad things happen when developers are trying to work around the underlying APIs because something is missing. It might be difficult to accomplish a task, and actually it might be difficult to do it the right way. The code above needed a check in order to prevent the view from being loaded, easy to miss. 

An alternative

If you are using a UINavigationController it might be easier just to check for the VisibleViewController property, this will include views presented in modal mode.

The dirty (don't do it) way

Now the terrible part of workarounds, is when they become something like this:

//use a field to hold the value
bool isVisible = false;

public override void ViewDidAppear (bool animated)
  base.ViewDidAppear (animated);
  isVisible = true; //and change it when the view appears

public override void ViewDidDisappear (bool animated)
  base.ViewDidDisappear (animated);
  isVisible = false; //or disappears 

I have seen this implementation several times, please don't do it this way, so many things wrong...that I won't enter into details here. Just don't do it. Stick with the other options, until they provide us with a simple property for that.

On the next entry will talk about the virtual keyboard on iOS...!

No comments: