Dynamic Binding

Using Badges in the iMedia Browser

I have just finished implementing a long awaited feature for the iMedia browser called “badges”. You can now annotate your media representations in the browser with badges as shown in the screenshot on the right. Since it is up to the host application to provide specific badges all different kinds of additional information can be communicated to the user through them (e.g. “You have used this media item in the context of this app (n-times)”. A user will also be able to filter the currently displayed image representations with the filters “Show Badged Only” and “Show Unbadged Only”.

As a host application’s programmer you have to exercise the following steps to utilize this functionality.

  1. Implement the following method in your host application’s delegate for the iMedia browser:

    - (CGImageRef) objectViewController:(IMBObjectViewController*) inController badgeForObject:(IMBObject*) inObject

    Given an object and a corresponding controller you will have to provide a badge image back to the browser. A typical badge would be a checkmark or a circled number (denoting things like “Item is being used” or “Item is being used n times”). If you return NULL the object won’t be badged. Most often you will keep a dictionary around holding those objects to be badged keyed by some object identifier that is also known to the respective object inside the browser (-[IMBObject identifier] would be such a key candidate).

    The badge image will be drawn in 3 different views that embed media objects: the icon view (≥ OSX 10.6), the list view and the combo view. Because the list view sports a mere height of 17 pixels per row and since badge images will not be scaled in any way by the browser framework an image size of 16 pixels square is generally a good choice. You find an exemplary implementation of the method in IMBTestAppDelegate.

    To keep your implementation of the method performant (it will get called at least once on any object to be displayed) it is good practice to cache badge images.

  2. To signal changes on badges to the framework send a

    setObjectContainerViewNeedsDisplay:YES

    message to all IMBNodeViewController objects that your app instantiated. If you intended to implement the “Item is being used” type of badge a typical initial badge-changing trigger would be a drag and drop operation of media objects from the browser into a view of your app (see an exemplary implementation on how to handle that in IMBTestTextView. Subsequent possibly badge-changing (user) actions then would be (list considered incomplete):

    • Discard a media object from the app
    • Undo/redo a drag and drop operation

It is definitely the host application’s programmer’s responsibility to attach a notification to the framework to such actions. Note that handling of the actions from above is not excercised by the browser test app.

For the More Curious: Behind the Scenes

Responsibilities for getting badges to work are shared among a couple of objects:

  • An IMBNodeViewController: tells its currently visible object view that it must redraw itself (triggered by the host application)
    setObjectContainerViewNeedsDisplay:

  • An IMBObjectViewController: is the one that asks its delegate for an appropriate badge whenever an object will have to be displayed or when it is to be decided wether the object will be displayed at all (badge related filters). Being the view’s delegate it prepares the view (resp. a view cell) for drawing.
    willDisplayCell:forTableColumn:row:
    objectArrayController:filterObject:
    -[<IMBObjectViewControllerDelegate> objectViewController:badgeForObject:]

  • An IMBObjectArrayController: asks the object controller whether an object passes the badge filter test.
    arrangeObjects:

  • A View: can be either an IMBImageBrowserView, an IMBComboTableView, or an IMBDynamicTableView. It is responsible for actually drawing the badge it got via the controller.
    -[IMBImageBrowserCell layerForType:]
    -[IMBComboTextCell _drawImage:withFrame:]

It is worth mentioning that the framework does not keep any badging information in its state (aside from a volatile reference to a badge image in a view cell). Thus, the framework must always query the host application’s delegate for appropriate badges. The upside of this design decision is that it is easier to keep the framework’s and the host application’s state in sync compared to storing badge information with the framework’s media objects. The downside obviously is that it is less performant.