{"id":662,"date":"2012-09-25T21:04:30","date_gmt":"2012-09-25T20:04:30","guid":{"rendered":"http:\/\/www.widemann.net\/wp-fr\/?p=662"},"modified":"2012-09-25T21:04:30","modified_gmt":"2012-09-25T20:04:30","slug":"rotation-sous-ios-6","status":"publish","type":"post","link":"https:\/\/widemann.net\/wp-fr\/rotation-sous-ios-6\/","title":{"rendered":"Rotation sous iOS 6"},"content":{"rendered":"<p><strong>Si vous utilisez un iPhone ou un iPad, vous avez peut-\u00eatre remarqu\u00e9 un nombre inhabituel de mises \u00e0 jour depuis la sortie d&#8217;iOS 6.<\/strong> Bien s\u00fbr, il y a l&#8217;adaptation urgente \u00e0 l&#8217;\u00e9cran \u00e9tir\u00e9 de l&#8217;iPhone 5, mais il y a une autre raison : Apple a modifi\u00e9 la gestion de la rotation, sans trop se soucier de la compatibilit\u00e9 des applications existantes&#8230;<!--more--><\/p>\n<p>L&#8217;id\u00e9e g\u00e9n\u00e9rale derri\u00e8re cette \u00e9volution part plut\u00f4t d&#8217;un bon sentiment : la m\u00e9thode pr\u00e9c\u00e9dente induisait une confusion entre rotation et mise en page (layout). Par exemple, sur un iPad, il est banal d&#8217;utiliser une subView organis\u00e9e de fa\u00e7on identique pour la \u201cdetail view\u201d quelle que soit la rotation.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-664\" title=\"Layout\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/Layout1.png\" alt=\"Layout\" width=\"350\" height=\"360\" srcset=\"https:\/\/widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/Layout1.png 350w, https:\/\/widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/Layout1-291x300.png 291w\" sizes=\"auto, (max-width: 350px) 85vw, 350px\" \/><\/p>\n<p>Si le viewController pilotant la subView est interrog\u00e9 classiquement via<br \/>\n<code>- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation<\/code><br \/>\nil peut se produire des situations ambigu\u00ebs o\u00f9 cette confusion pose probl\u00e8me. Pour \u00e9viter \u00e7a, Apple a rendue \u201cdeprecated\u201d cette m\u00e9thode et le framework a tout simplement\u00a0cess\u00e9 de l&#8217;appeler (de ce fait, certaines applications ont tout simplement cess\u00e9 de pivoter !). \u00c0 la place, l&#8217;application s&#8217;appuie sur les orientations indiqu\u00e9es dans le fichier Info.plist. Autant de code en moins \u00e0 \u00e9crire. Ou bien, avec une nouvelle API, elle interroge seulement le viewController \u201cracine\u201d, mais plus les autres. <strong>C&#8217;est maintenant le rootViewController qui d\u00e9cide pour ses \u201cenfants\u201d.<\/strong><\/p>\n<p>Cette nouvelle m\u00e9thode est \u00e9videmment beaucoup trop simple. Un nouveau probl\u00e8me appara\u00eet lorsque l&#8217;application doit g\u00e9rer s\u00e9lectivement la rotation selon la view courante, accepter la rotation sur une view et la refuser sur une autre. Pour pr\u00e9server la souplesse n\u00e9cessaire, Apple\u00a0a introduit plusieurs nouvelles m\u00e9thodes dans UIViewController :<\/p>\n<p><code>- (BOOL)shouldAutorotate;<\/code><br \/>\n<code>- (NSUInteger)supportedInterfaceOrientations;<\/code><\/p>\n<p>Il faut d&#8217;abord pr\u00e9venir le framework que l&#8217;application les utilise. La solution retenue pour \u00e7a est assez \u00e9trange : au lieu d&#8217;une m\u00e9thode de d\u00e9l\u00e9gu\u00e9 qu&#8217;on aurait pu s&#8217;attendre \u00e0 trouver (mais un UIViewController n&#8217;a pas de d\u00e9l\u00e9gu\u00e9 par d\u00e9faut&#8230;), c&#8217;est un userDefault qu&#8217;il faut cr\u00e9er :<\/p>\n<p><code>- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions<br \/>\n{<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>\/\/Register for new API rotation calls<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@\"UIApplicationSupportedInterfaceOrientationsIsEnabled\"];<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>...<br \/>\n}<\/code><\/p>\n<p>Deuxi\u00e8me contrainte \u00e0 respecter : la fen\u00eatre doit imp\u00e9rativement \u00eatre dot\u00e9e d&#8217;un viewController \u201croot\u201d. C&#8217;\u00e9tait facultatif jusqu&#8217;ici (mais on voyait depuis iOS 5 passer un warning dans la console en son absence), c&#8217;est devenu obligatoire en iOS 6, sinon il est impossible d&#8217;exploiter cette nouvelle API. En outre, la bonne syntaxe doit \u00eatre utilis\u00e9e pour installer le viewController et sa view dans la fen\u00eatre :<\/p>\n<p><code>self.window.rootViewController = viewController;<\/code><\/p>\n<p>et surtout pas <code>[window addSubView:viewController.view];<\/code> qui ne fonctionne pas correctement puisque la property rootViewController n&#8217;est pas sollicit\u00e9e. Attention avec les projets anciens cr\u00e9\u00e9s avec les vieux templates&#8230;<\/p>\n<p>Ensuite, il faut impl\u00e9menter\u00a0<code>shouldAutorotate<\/code> et\u00a0<code>supportedInterfaceOrientations<\/code> dans le rootViewController lui-m\u00eame. Pas de probl\u00e8me si c&#8217;est une sous-classe. Si c&#8217;est un UINavigationController, il faut le sous-classer aussi, idem pour un UITabBarController utilis\u00e9 en rootViewController. C&#8217;est assez \u00e9trange d&#8217;avoir \u00e0 sous-classer ces v\u00e9n\u00e9rables objets juste pour \u00e7a, mais apparemment c&#8217;est la bonne solution.<\/p>\n<p>Comme indiqu\u00e9 plus haut, le framework appelle seulement ces m\u00e9thodes dans le rootViewController, et non dans les viewControllers \u201cenfants\u201d. La d\u00e9cision effective de rotation sera l&#8217;intersection entre ce qu&#8217;il y a dans Info.plist et ce que retourne <code>supportedInterfaceOrientations<\/code>.\u00a0Il y a peut-\u00eatre une solution plus simple qui m&#8217;a \u00e9chapp\u00e9 pour l&#8217;instant, en tout cas voici une approche qui fonctionne pour autoriser s\u00e9lectivement la rotation. Impl\u00e9menter ainsi les deux m\u00e9thodes dans la sous-classe de UINavigationController :<\/p>\n<p><code>- (NSUInteger)supportedInterfaceOrientations<br \/>\n{<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>UIViewController *lastVC = [self.viewControllers lastObject];<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>if ([lastVC respondsToSelector:@selector(supportedInterfaceOrientations)]) {<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>return [lastVC supportedInterfaceOrientations];<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>}<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>return UIInterfaceOrientationMaskAllButUpsideDown;<br \/>\n}<\/code><\/p>\n<p><code>- (BOOL)shouldAutorotate<br \/>\n{<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>UIViewController *lastVC = [self.viewControllers lastObject];<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>if ([lastVC respondsToSelector:@selector(shouldAutorotate)]) {<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>return [lastVC shouldAutorotate];<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>}<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-666\" src=\"http:\/\/www.widemann.net\/wp-fr\/wp-content\/uploads\/2012\/09\/indentation.png\" alt=\"\" width=\"50\" height=\"10\" \/>return YES;<br \/>\n}<\/code><\/p>\n<p>On peut adapter \u00e7a \u00e0 un UITabBarController \u201croot\u201d en lui indiquant le \u201cchemin\u201d du viewController \u00e0 interroger (\u00e7a peut \u00eatre plus compliqu\u00e9, car un tabBarItem peut contenir \u00e0 son tour un navigationController, donc il faudra adapter l&#8217;approche au cas par cas). Ensuite il ne reste plus qu&#8217;\u00e0 impl\u00e9menter ces m\u00e9thodes dans les viewControllers qui doivent se comporter diff\u00e9remment de la r\u00e8gle g\u00e9n\u00e9rale, et y retourner les valeurs appropri\u00e9es, par exemple <code>return NO;<\/code> pour refuser la rotation, ou une valeur diff\u00e9rente de masque pour l&#8217;autoriser s\u00e9lectivement.<\/p>\n<p>Bien s\u00fbr, on conserve en parall\u00e8le l&#8217;API pr\u00e9c\u00e9dente pour pr\u00e9server la compatibilit\u00e9 avec iOS 5 encore quelques temps&#8230;<\/p>\n<p>Ceci n&#8217;est peut-\u00eatre pas la solution optimale. La doc est assez elliptique pour le moment et laisse largement place \u00e0 l&#8217;interpr\u00e9tation. N&#8217;h\u00e9sitez pas \u00e0 commenter si vous trouvez une solution plus simple !<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Si vous utilisez un iPhone ou un iPad, vous avez peut-\u00eatre remarqu\u00e9 un nombre inhabituel de mises \u00e0 jour depuis la sortie d&#8217;iOS 6. Bien s\u00fbr, il y a l&#8217;adaptation urgente \u00e0 l&#8217;\u00e9cran \u00e9tir\u00e9 de l&#8217;iPhone 5, mais il y a une autre raison : Apple a modifi\u00e9 la gestion de la rotation, sans trop &hellip; <a href=\"https:\/\/widemann.net\/wp-fr\/rotation-sous-ios-6\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Rotation sous iOS 6&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32],"tags":[45,46],"class_list":["post-662","post","type-post","status-publish","format-standard","hentry","category-programmation","tag-ios6","tag-iphone"],"_links":{"self":[{"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/posts\/662","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/comments?post=662"}],"version-history":[{"count":0,"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/posts\/662\/revisions"}],"wp:attachment":[{"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/media?parent=662"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/categories?post=662"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/widemann.net\/wp-fr\/wp-json\/wp\/v2\/tags?post=662"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}