A Custom Layer Controller

From AwkwardTV
Revision as of 03:59, 17 April 2007 by Alan quatermain (talk | contribs) (New page: {{template:banner}} In our second tutorial we'll pull back a little and cover the '''BRLayerController''' class, and what you can do with it. If this isn't your first experience with Back ...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search


In our second tutorial we'll pull back a little and cover the BRLayerController class, and what you can do with it. If this isn't your first experience with Back Row plugins, then you're probably already familiar with the BRMenuController or BRMediaMenuController, but those are both fairly specialized subclasses. By creating our own BRLayerController subclasses we can define our own content and layout in a more comprehensive manner.

Along the way we will also take a look at an anciliary class, BRImageManager, which enables us to dynamically download and cache images from the Internet, or indeed from any URL, remote or local, and we will see how to use the BRHeaderControl and BRTextControl classes.



The BRLayerController class is used to present a significant part of the AppleTV user interface. Examples include the full-screen lists, alerts, and the settings facade. It is also used for items such as preview controllers and parade controllers, which show static or animated content to the left of a list. Specifically, a layer controller differs from a control in that it will handle user input in some intelligent way: where a layer draws content and a control collects layers and handles events, a layer controller is where application-specific logic is contained.

Let's start by examining the interface:


Setup & Contents

- (BRRenderScene *) scene;
- (BRRenderLayer *) masterLayer;
- (NSRect) masterLayerFrame;
- (void) resetLayout;
- (void) setControls: (NSArray *) controls;
- (void) addControl: (BRControl *) control;
- (NSArray *) controls;

Like the BRControl class, BRLayerControllers have a single master layer to which others are added. Unlike BRControl however, this layer is provided for you by the base class. There is no specific -frame or -setFrame: method here; the controller class is not by definition a UI class, so these operations are performed directly on the master layer, retrieved by calling -masterLayer. As with all controls and layers, however, an explicit reference to the scene is kept by the controller, and is used to redraw the scene when the layer changes.

The -resetLayout method will reset the frame of the master layer according to the -masterLayerFrame function. Both of these can be overridden in subclasses to implement useful behaviour. For instance, a particular subclass might have its masterLayerFrame return the frame of the entire scene (i.e. the entire screen itself), as opposed to the default implementation which will return the frame of the interface area of the scene. By default, the -resetLayout method will simply reset the frame of the master layer. Subclasses can override it to realign their own sublayers according to a potentially changed master frame.

The layer controller also contains a list of controls. While it can have layers added to it directly via [[self masterLayer] addSubLayer: layer], it is generally used to contain controls, which are added to a list, and which have their layers added to the master layer for you. Note that no method exists to remove a control; instead, you must use the following snippet to perform that task:

- (void) removeControl: (BRControl *) aControl
    NSMutableArray * controls = [NSMutableArray arrayWithArray: [self controls]];
    [[aControl layer] removeFromSuperlayer];
    [controls removeObject: aControl];
    [self setControls: [NSArray arrayWithArray: controls]];

Also note that if the control being removed is retained by the controller subclass outside of the control list, it will need to be released explicitly in addition to the code shown above.

The Controller Stack

- (void) setStack: (BRControllerStack *) stack;
- (BRControllerStack *) stack;

Controllers are placed onto a stack which is used to maintain the display list. Each time you select an item from a menu or click a button which will put up a new interface layer through a controller, then that controller is added to the current stack. When you press the Menu button, the current controller will be popped from the stack, revealing the one underneath.

The functions listed above are used for book-keeping within the controller, but you are unlikely to need to use those explicitly, because the layer controller class defines a number of callbacks used by the BRControllerStack class to notify a layer of impending status changes:

- (void) willBePushed;
- (void) wasPushed;
- (void) willBePopped;
- (void) wasPopped;
- (void) willBeBuried;
- (void) wasBuriedByPushingController: (BRLayerController *) controller;
- (void) willBeExhumed;
- (void) wasExhumedByPoppingController: (BRLayerController *) controller;

Here we see pairs of notifications -- one called in advance of the operation, one called after it has been performed. There is a set for when the controller is being pushed onto the top of the stack (being put onscreen) and when it is being popped (removed before deallocation). There are also items for when the controller is to be buried (another controller pushed on top of this one) and exhumed (controllers on top of this one were removed). Each of these can be overridden to performs specific duties, but in this example we will use only the -wasPushed and -willBePopped methods, which are generally the most useful points at which to perform our operations.


- (BRAnimation *) popAnimation;
- (BRAnimation *) pushAnimation;

By default, a layer controller offers a fade-to-black animation when popped and a fade-from-black animation when pushed. Subclasses can override these to implement specific effects; for instance the main menu provides a custom animation where the icon zooms up to the header of the incoming menu. We will cover animations in a later tutorial.


- (BOOL) brEventAction: (BREvent *) event;
- (void) addLabel: (NSString *) label;
- (void) removeLabel: (NSString *) label;
- (BOOL) isLabelled: (NSString *) label;
- (BOOL) isNetworkDependent;
- (BOOL) popsOnBury;

Layer controllers can handle events directly; this allows them to customize the response to the menu button, or to move focus amongst their controls.

Labels can be used to 'tag' controllers. The BRControllerStack class offers methods based on these, such as fetching a controller with a specific label from the stack (if there), or popping everything above a certain label.

A layer can mark itself as being network dependant, in which case it will be monitored by the framework, so that some action can be taken should the network status change.

It can also choose to be popped automatically if another controller is pushed on top of it, removing itself from the stack immediately.


The BRImageManager class implements a useful cache for images pulled from the network, along with an asynchronous notification-based loading system for remote images. A number of its methods are based around the iTunes/AppleTV media library format, but there are some which are more generally useful:

- (BOOL) isImageAvailable: (NSString *) name;
- (NSString *) imageNameFromURL: (NSURL *) url;
- (NSString *) writeImageFromURL: (NSURL *) url;
- (void) writeImage: (CGImageRef) image named: (NSString *) name;
- (void) removeImageNamed: (NSString *) name;
- (CGImageRef) imageNamed: (NSString *) name;