Tuesday, October 6, 2015

How to use the UIFocusGuide for navigation

I really didn't quite understand how UIFocusGuides were supposed to work until after several hours of banging my head against the wall.  I think I got it now....

The focus guide, like the layout guide it extends, can be thought of as an invisible view - in this case, an invisible focusable view.

A UIFocusGuide functions as a bridge between different areas of the screen since focus navigation only works up/down or left/right.

Consider a screen that has two objects A and B.  

     AAAA

               BBBB

With the default focus behaviors, there is no way to transition from A to B because B is not directly to the right or bottom.

Thus, to make this work, you'd need a focus guide in between, like


     AAAA

FFFFFFFFFFFFFFFFFFFFF

               BBBB


and we would set the focus guide's preferredFocusedView = B to start.  

So, given this example layout, if view A has the focus, and the user navigates down, it will find the focus guide (F), and the focus guide will then redirect the focus to the guide's  preferredFocusedView, in this case B.

Of course, if B has the focus, you'd want to update the focus guide so its preferredFocusedView is now A, so that when the user navigates up, it goes from B to A.

Okay, now that you get the concept, what's the code look like?  Something like this (Warning: I have not compiled/run this segment):


UIFocusGuide *F;

- (void)viewDidLoad
{
   [super viewDidLoad];
   
   F = [UIFocusGuide new];
   [self.view addLayoutGuide:F];

   [F.topAnchor constraintEqualToAnchor:A.bottomAnchor].active = YES;
   [F.heightAnchor constraintEqualToConstant:1].active = YES;
   [F.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
   [F.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;

   [F setPreferredFocusedView:B];
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{

   if (context.nextFocusedView == A){
       [F setPreferredFocusedView:B];
   } else if (context.nextFocusedView == B){
       [F setPreferredFocusedView:A];
   }
}


Apple has a simple example in the UIKitCatalog demo that demonstrates the concept in swift.  The layout in that example case is more like:

AAAAA    BBBBB

CCCCC    FFFFF

and it shows the navigation between B and C via F.

No comments:

Post a Comment