Key Value Observing (KVO) on iOS for Xamarin

For developers coming to Xamarin from the native iOS world, this is probably old news. But for those of us coming from a different direction, read on for a handy way to work with native iOS controls.

The iOS (and macOS) runtimes have a built-in property notification system, somewhat analogous to INotifyPropertyChanged in .NET, Key Value Observing. While Xamarin has some nice documentation on using the Key-Value Coding system for macOS, there is little about using it on iOS, or how to use it to hook into platform events.  There is the basic API documentation, for instance on AddObserver, which we’ll be using.  Note that there is some misleading information on the page, which we’ll explain below.  That being said, AddObserver is a nice, easy-to-use .NET-feeling wrapper around the underlying functionality.

I’ve found KVO programming to be an important tool for developing custom renderers for Xamarin.Forms, where I want to extend the functionality of an existing renderer rather than completely write a new one.

Let’s say you want to be notified when the scroll position in a UIScrollView changes. Using Visual Studio for Mac, right-click on a reference to the ContentOffset property on a UIScrollView object, and click on Go To Declaration.

This will bring up the metadata about the property:

The important things to note:

  • You can only observe properties
  • The property must have an Export attribute on it

The name in the Export property on the getter is what we’ll need in the AddObserver call. It’s important to get this right, including casing.  The runtime and compiler will silently ignore any mistakes (UPDATE: XamRight will now detect incorrect AddObserver calls). The casing is different than the C# wrapper, and this is where the Xamarin docs are incorrect:

The name is not necessarily all lowercase, though in the case of platform properties, it will start with a lowercase letter.

Putting this all together, this is how we’ll register for callbacks on changes:

observer = Control.AddObserver("contentOffset",
           Foundation.NSKeyValueObservingOptions.New, HandleAction);
...
  private void HandleAction(Foundation.NSObservedChange obj)
  {
      ...
  }

This sets it up so our method HandleAction is called on changes to contentOffset (specifically just being given the new values). And when we’re done observing changes to contentOffset, we’ll need to Dispose observer.

You can also write Xamarin.iOS code that generates notifications to allow Key-Value Observers to be watch for changes, just like they can with native controls.  But that’s a story for another day.

Author

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.