{"id":85,"date":"2010-07-08T20:32:41","date_gmt":"2010-07-09T04:32:41","guid":{"rendered":"http:\/\/bynomial.com\/blog\/?p=85"},"modified":"2010-07-08T20:32:41","modified_gmt":"2010-07-09T04:32:41","slug":"handling-rotations-in-uiviewcontroller","status":"publish","type":"post","link":"http:\/\/bynomial.com\/blog\/?p=85","title":{"rendered":"Handling rotations in UIViewController"},"content":{"rendered":"<p>In this post I&#8217;ll give a few general tips for handling device rotations at the UIViewController level. \u00c2\u00a0These tips are for code using iOS (formerly known as iPhone OS) 3.0 or later, which includes all iPad code.<\/p>\n<h2>1. \u00c2\u00a0In\u00c2\u00a0willAnimateRotationToInterfaceOrientation:duration:, size and position the view for the new orientation.<\/h2>\n<p>This sounds obvious and easy, but there are a <em>lot<\/em> of innocent-seeming ways to screw this up.<\/p>\n<p>In this method, you can think of the rotation as being done, and that you&#8217;re now moving things to fit the new screen size. \u00c2\u00a0You do <em>not<\/em> have to do any rotations yourself. \u00c2\u00a0You do <em>not <\/em>have to make any calls to UIView&#8217;s animation methods. \u00c2\u00a0UIKit handles both of those for you.<\/p>\n<p>One way to think about your responsibility in this method is as follows: Imagine someone started with a tall skinny monitor as their only display, and then unplugged it and plugged back in a short wide monitor. \u00c2\u00a0The<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">willAnimateRotationToInterfaceOrientation:duration:<\/div><\/div>\n<p>method has the job of making things fit nicely on the new monitor.<\/p>\n<p>But there are still some easy mistakes to make, as we&#8217;ll see.<\/p>\n<h2>2. \u00c2\u00a0Use interfaceOrientation, and nothing else, to determine your orientation.<\/h2>\n<p>It can be tempting to use something like<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self.frame.size.width<\/div><\/div>\n<p>to determine if you&#8217;re in portrait or landscape mode, but this is a terrible idea for a few reasons:<\/p>\n<ol>\n<li>During the call to\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">willAnimateRotationToInterfaceOrientation:duration:<\/div><\/div>\n<p>, the actual bounds of your view has not yet changed to the new orientation.<\/li>\n<li>If your view controller has the top-level non-window view (i.e., it&#8217;s the bottom-most view controller), then\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self.frame<\/div><\/div>\n<p>is <em>always<\/em> in portrait orientation. \u00c2\u00a0Wha? \u00c2\u00a0Yes, always in portrait &#8211; what changes is the transform of your view. \u00c2\u00a0So your<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self.bounds<\/div><\/div>\n<p>is always accurate (keeping in mind the last point), but<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self.frame<\/div><\/div>\n<p>may or may not give the aspect ratio that the user is really seeing, since each view&#8217;s frame is reported in terms of the superview&#8217;s coordinates, and takes into account any transforms applied to the view.<\/li>\n<li>In general, it&#8217;s good to use a consistent and reliable method to determine orientation. \u00c2\u00a0When you first start the app, your best bet is to refer to\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self.interfaceOrientation<\/div><\/div>\n<p>, and to use the helpful macros designed to work with the type<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">UIInterfaceOrientation<\/div><\/div>\n<p>(<a href=\"http:\/\/bynomial.com\/blog\/?p=25\">see here<\/a> for more details on that). \u00c2\u00a0To keep your code consistent, it makes sense to use<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">UIInterfaceOrientation<\/div><\/div>\n<p>variables in all situations where orientation is important.<\/li>\n<\/ol>\n<h2>3. \u00c2\u00a0Don&#8217;t do extra work that you think may be needed for rotation.<\/h2>\n<p>In particular, there&#8217;s no need to adjust any view&#8217;s transform directly, since it&#8217;s done for you. \u00c2\u00a0And you don&#8217;t have to think in terms of rotated coordinates either &#8211; the point (0,0) is always the upper-left corner of the user&#8217;s screen, and this point is moved for you automatically during rotation.<\/p>\n<p>If you&#8217;re using iOS 3.0 or later (as this post assumes), then you also don&#8217;t want to implement the older functions\u00c2\u00a0<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">willAnimate{First,Second}HalfOfRotationToInterfaceOrientation:duration:<\/div><\/div>\n<p>methods. \u00c2\u00a0This old system is considered less efficient and deprecated. \u00c2\u00a0If you try to implement both the new technique (the single method above) and the old technique (the two-step methods here), then only the new technique will actually be executed at runtime.<\/p>\n<h2>4. Don&#8217;t forget to report that you handle all orientations.<\/h2>\n<p>If you don&#8217;t override the\u00c2\u00a0<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">shouldAutorotateToInterfaceOrientation:<\/div><\/div>\n<p>method of<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">UIViewController<\/div><\/div>\n<p>, then the default implementation of this method will always return<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">NO<\/div><\/div>\n<p>, and you&#8217;ll be stuck in portrait mode forever. \u00c2\u00a0It&#8217;s a one-line method to rotate to any orientation &#8211; &#8221;<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">return YES;<\/div><\/div>\n<p>&#8221; &#8211; and that&#8217;s more-or-less required behavior for non-immersive apps on the iPad.<\/p>\n<h2>References<\/h2>\n<p><a href=\"http:\/\/developer.apple.com\/iphone\/library\/documentation\/uikit\/reference\/UIViewController_Class\/Reference\/Reference.html\">UIViewController class reference<\/a><\/p>\n<p><a href=\"http:\/\/developer.apple.com\/iphone\/library\/documentation\/uikit\/reference\/UIApplication_Class\/Reference\/Reference.html#\/\/apple_ref\/c\/tdef\/UIInterfaceOrientation\">UIInterfaceOrientation docs<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I&#8217;ll give a few general tips for handling device rotations at the UIViewController level. \u00c2\u00a0These tips are for code using iOS (formerly known as iPhone OS) 3.0 or later, which includes all iPad code. 1. \u00c2\u00a0In\u00c2\u00a0willAnimateRotationToInterfaceOrientation:duration:, size and position the view for the new orientation. This sounds obvious and easy, but there [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[40,12,15,3,9],"tags":[81,19,80,79],"_links":{"self":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/85"}],"collection":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=85"}],"version-history":[{"count":0,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/85\/revisions"}],"wp:attachment":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=85"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=85"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=85"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}