{"id":106,"date":"2015-11-04T02:17:22","date_gmt":"2015-11-04T02:17:22","guid":{"rendered":"http:\/\/thibaultklein.com\/ios\/?p=106"},"modified":"2016-08-18T10:40:05","modified_gmt":"2016-08-18T10:40:05","slug":"using-the-focus-guide-to-improve-your-tvos-apps","status":"publish","type":"post","link":"https:\/\/thibaultklein.com\/ios\/using-the-focus-guide-to-improve-your-tvos-apps\/","title":{"rendered":"Using the Focus Guide to Improve your tvOS Apps"},"content":{"rendered":"<p>After experimenting a lot with tvOS and playing around with the Apple TV remote, I noticed how easy it was to navigate following simple paths (up, down, left, or right), but also how impossible it was to move along a <em>diagonal<\/em> path. This was the central problem: how are you supposed to focus zones of your view that are located diagonally from one another? That\u2019s where `UIFocusGuide` comes in.<\/p>\n<p>The `UIFocusGuide` class is designed to fix this problem, allowing developers to expose non-view areas as focusable; by adding a focus guide to your UI, you will be able to focus an element that couldn\u2019t be handled automatically by the focus engine.<\/p>\n<p>Let&#8217;s walk through a simple example:<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-7481\" src=\"https:\/\/i0.wp.com\/blog.prolificinteractive.com\/wp-content\/uploads\/focus-guide1.jpg?resize=750%2C422\" alt=\"focus-guide1\" width=\"750\" height=\"422\" \/><\/p>\n<p>As you can see here, the \u201cMORE INFO\u201d button below the image is not directly aligned with the \u201cBUY\u201d button which means it is impossible for a right swipe on the remote to focus on that item and make it active. Even overriding `didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)` will not help as you\u2019ll notice that this method is not called when swiping right because the focus engine is not able to find a focusable view.<\/p>\n<p>To solve this problem, we need to create a focus guide between the two buttons to fill the gap and use the `preferredFocusedView` property of the focus guide to tell the focus engine who should be focused next.<\/p>\n<h2>Focus guide set up<\/h2>\n<p>The first step in my view controller is to make a property for the focus guide:<\/p>\n<pre>private var focusGuide = UIFocusGuide()\r\n<\/pre>\n<p>Then in `viewDidLoad`, I add the focus layout to my view and set up its constraints:<\/p>\n<pre>\/\/ We create a focus guide to fill the space between the more info button \r\n\/\/ and the buy button since it's not obvious for the focus engine which element should be focused.\r\nself.view.addLayoutGuide(self.focusGuide)\r\n\r\nself.focusGuide.leftAnchor.constraintEqualToAnchor(self.buyButton.leftAnchor).active = true\r\nself.focusGuide.topAnchor.constraintEqualToAnchor(self.moreInfoButton.topAnchor).active = true\r\nself.focusGuide.widthAnchor.constraintEqualToAnchor(self.buyButton.widthAnchor).active = true\r\nself.focusGuide.heightAnchor.constraintEqualToAnchor(self.moreInfoButton.heightAnchor).active = true\r\n<\/pre>\n<p>An interesting thing to note here is the usage of `constraintEqualToAnchor`. With tvOS and iOS 9.1, Apple finally provides a new, easy way to create auto-layout constraints that are much more convenient than the older APIs. You can define a relationship between two attributes that you can subsequently activate to create a constraint. Here is how the above code would look using the old system:<\/p>\n<pre>\/\/ Creating a constraint using NSLayoutConstraint\r\nNSLayoutConstraint(item: subview,\r\n   attribute: .Leading,\r\n   relatedBy: .Equal,\r\n   toItem: view,\r\n   attribute: .LeadingMargin,\r\n   multiplier: 1.0,\r\n   constant: 0.0).active = true\r\n\r\n\/\/ Creating the same constraint using constraintEqualToAnchor:\r\nlet margins = view.layoutMarginsGuide\r\nsubview.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor).active = true\r\n<\/pre>\n<p>Much better, right?<\/p>\n<p>Anyway, now that our focus guide has its layout constraints ready, the invisible view should be placed correctly. Below, I&#8217;ve highlighted in red the focus guide, but remember that a focus guide is <em>always<\/em> an invisible frame that is just there to help the focus engine.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-7484\" src=\"https:\/\/i0.wp.com\/blog.prolificinteractive.com\/wp-content\/uploads\/focus2.jpg?resize=750%2C422\" alt=\"focus2\" width=\"750\" height=\"422\" \/><\/p>\n<p>With our guide ready, it&#8217;s time to add the logic. In my view controller, I override the focus element method `didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)` and implement the following:<\/p>\n<pre>override func didUpdateFocusInContext(context: UIFocusUpdateContext, \r\nwithAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {\r\n        super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)\r\n\r\n        guard let nextFocusedView = context.nextFocusedView else { return }\r\n\r\n        \/\/ When the focus engine focuses on the focus guide, we can programmatically tell \r\n        \/\/ it which element should be focused next.\r\n        switch nextFocusedView {\r\n        case self.moreInfoButton:\r\n            self.focusGuide.preferredFocusedView = self.buyButton\r\n\r\n        case self.buyButton:\r\n            self.focusGuide.preferredFocusedView = self.moreInfoButton\r\n\r\n        default:\r\n            self.focusGuide.preferredFocusedView = nil\r\n        }\r\n    }\r\n<\/pre>\n<p>Here we are getting the next focused view from the focus context, and based on this, we can redirect the focus engine to the new focused view.<\/p>\n<p>If you put a breakpoint in this method, you can use Xcode Quick Look on the context parameter which will show you where the focus engine is trying to get the next focusable view.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-7485\" src=\"https:\/\/i0.wp.com\/blog.prolificinteractive.com\/wp-content\/uploads\/focus3.png?resize=750%2C279\" alt=\"focus3\" width=\"750\" height=\"279\" \/><\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-7486\" src=\"https:\/\/i0.wp.com\/blog.prolificinteractive.com\/wp-content\/uploads\/focus4.png?resize=750%2C430\" alt=\"focus4\" width=\"750\" height=\"430\" \/><\/p>\n<p>You can see that the focus engine found our focus guide! Without it, nothing would be focusable, and we couldn\u2019t manually update the focus to the BUY button.<\/p>\n<p>I created a <a href=\"https:\/\/github.com\/klein-thibault\/FocusGuide\">sample project<\/a> to illustrate this example that will show you the full implementation. It demonstrates that with a few lines of code you can drastically improve the navigation into your views using the new <a href=\"https:\/\/developer.apple.com\/library\/prerelease\/tvos\/documentation\/UIKit\/Reference\/UIFocusGuide_Class\/\">UIFocusGuide<\/a> class, and make sure the users are not frustrated when trying to navigate to a specific zone of your app. It also introduces you the new <a href=\"https:\/\/developer.apple.com\/library\/prerelease\/tvos\/documentation\/AppKit\/Reference\/NSLayoutAnchor_ClassReference\/index.html#\/\/apple_ref\/occ\/instm\/NSLayoutAnchor\/constraintEqualToAnchor:\">NSLayoutAnchor<\/a> API Apple released for tvOS and iOS 9.1.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>After experimenting a lot with tvOS and playing around with the Apple TV remote, I noticed how easy it was to navigate following simple paths (up, down, left, or right), but also how impossible it was to move along a diagonal path. This was the central problem: how are you supposed to focus zones of&hellip; <a class=\"more-link\" href=\"https:\/\/thibaultklein.com\/ios\/using-the-focus-guide-to-improve-your-tvos-apps\/\">Continue reading <span class=\"screen-reader-text\">Using the Focus Guide to Improve your tvOS Apps<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-106","post","type-post","status-publish","format-standard","hentry","category-tvos","entry"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6mZvu-1I","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/posts\/106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/comments?post=106"}],"version-history":[{"count":2,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/posts\/106\/revisions"}],"predecessor-version":[{"id":172,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/posts\/106\/revisions\/172"}],"wp:attachment":[{"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/media?parent=106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/categories?post=106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thibaultklein.com\/ios\/wp-json\/wp\/v2\/tags?post=106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}