<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Tewha.net</title>
    <link>https://tewha.net/tags/uiviewcontroller/</link>
    <description>Recent content on Tewha.net</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 04 Oct 2012 14:00:00 +0000</lastBuildDate>
    <atom:link href="https://tewha.net/tags/uiviewcontroller/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>View Controller Lifecycle in iOS 6</title>
      <link>https://tewha.net/2012/10/view-controller-lifecycle-in-ios-6/</link>
      <pubDate>Thu, 04 Oct 2012 14:00:00 +0000</pubDate>
      <guid>https://tewha.net/2012/10/view-controller-lifecycle-in-ios-6/</guid>
      <description>&lt;p&gt;I previously wrote about breaking the old pattern of &lt;a href=&#34;https://tewha.net/2012/09/dont-write-viewdidunload/&#34;&gt;writing viewDidUnload&lt;/a&gt;. The other half of that is the new reality, which Joe Conway’s written about in &lt;a href=&#34;http://thejoeconwayblog.wordpress.com/2012/10/04/view-controller-lifecycle-in-ios-6/&#34;&gt;View Controller Lifecycle in iOS 6&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I previously wrote about breaking the old pattern of <a href="/2012/09/dont-write-viewdidunload/">writing viewDidUnload</a>. The other half of that is the new reality, which Joe Conway’s written about in <a href="http://thejoeconwayblog.wordpress.com/2012/10/04/view-controller-lifecycle-in-ios-6/">View Controller Lifecycle in iOS 6</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How to avoid starting view controllers in the wrong orientation on startup</title>
      <link>https://tewha.net/2012/09/how-to-avoid-starting-view-controllers-in-the-wrong-orientation-on-startup/</link>
      <pubDate>Thu, 27 Sep 2012 14:00:00 +0000</pubDate>
      <guid>https://tewha.net/2012/09/how-to-avoid-starting-view-controllers-in-the-wrong-orientation-on-startup/</guid>
      <description>&lt;p&gt;This is a bit obscure, but I ran into it earlier this week. Why would a view controller appear in the wrong orientation on startup?&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>This is a bit obscure, but I ran into it earlier this week. Why would a view controller appear in the wrong orientation on startup?</p>
<p>When your iPad application first launches it will be in portrait mode, even if it’s held in landscape mode. The rotate event is already in the queue and is consumed early enough that it’ll never actually be shown like that. So no problem, right?</p>
<p>The problem is that the rotate event will not have been consumed when your <code>viewWillAppear:</code> is first called. Opening a new view controller in <code>viewWillAppear:</code> (whether using <code>presentModalViewController:</code> or a storyboard segue) will open that new view controller in portrait mode, rather than landscape mode. Even that wouldn’t be a problem, except iOS will not add a rotate event added for the new view controller.</p>
<p>This all makes a great deal of sense when you think it through: The rotate events for the initial view controller or controllers are already on queue before <code>viewWillAppear</code> is called. iOS already recognizes the device has been rotated; as far as it’s concerned, the rotation is done. Your app is just lagging a bit behind as part of the startup process, with everything all queued up.</p>
<p>Conceptually, this is a hard problem.</p>
<p>But in practice, there’s a very simple and very elegant solution using blocks.</p>
<p>All you need to do is delay your view controller’s appearance. Before blocks, you might have had to use a timer. But now you can just use <code>dispatch_async</code> to schedule the new view controller for later.</p>
<p>That’s a little confusing, so here’s an example:</p>
<p>I have a view controller that appears when my application is first installed, to walk the user through the signup process. (Later, if there’s an authentication problem, I fall back on the standard username and password alert view.)</p>
<p>This view controller, then, needs to appear when my main view first appears. If I use this code, the view controller that appears will always be in portrait, even if the device is started in landscape.</p>
<pre><code>- (void)viewWillAppear: (BOOL)animated {
    [super viewWillAppear:animated];
    if (freshInstall) {
        [self performSegueWithIdentifier: @&quot;firstRun&quot; sender: self];
    }
}
</code></pre>
<p>If I use this code, however, the view controller that appears will match the device (and the view controller it’s appearing over):</p>
<pre><code>- (void)viewWillAppear: (BOOL)animated {
    [super viewWillAppear:animated];
    if (freshInstall) {
        dispatch_async(dispatch_get_main_queue(),^{
            [self performSegueWithIdentifier: @&quot;firstRun&quot; sender: self];
        });
    }
}
</code></pre>
<p>Later in this case is <em>still before</em> the user has a chance to interact, or even before the UI really and truly appears onscreen. The block for showing the view controller will be added to the queue. Everything will just work. By the time your block is run from the main queue, the rotation event will be dealt with and your app will really and truly be in landscape orientation. However, it’ll still be early enough for your view controller to look like part of early application startup.</p>
<p><em>Note: This was done on my “stable” branch, so it was probably on iOS 5. I have not yet tried this on iOS 6, but <a href="https://twitter.com/shusta/statuses/251389962661027842">I’ve read reports this is no longer necessary</a>. Either way, it’s a good trick if you plan on maintaining iOS 5 compatibility (which you should, for a while at least).</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Don’t write viewDidUnload</title>
      <link>https://tewha.net/2012/09/dont-write-viewdidunload/</link>
      <pubDate>Wed, 05 Sep 2012 14:00:00 +0000</pubDate>
      <guid>https://tewha.net/2012/09/dont-write-viewdidunload/</guid>
      <description>&lt;p&gt;A lot of people misunderstand the point of &lt;code&gt;viewDidUnload&lt;/code&gt;. That’s because despite the name that implies otherwise, it is &lt;strong&gt;not&lt;/strong&gt; the counterpart of &lt;code&gt;viewDidLoad&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>A lot of people misunderstand the point of <code>viewDidUnload</code>. That’s because despite the name that implies otherwise, it is <strong>not</strong> the counterpart of <code>viewDidLoad</code>.</p>
<p>You should <strong>never</strong> consider a deallocation in <code>viewDidUnload</code> a proper balance for an allocation in <code>viewDidLoad</code>. In fact, you should probably shouldn’t write a <code>viewDidUnload</code>.</p>
<h2 id="make_your_outlets_unload_automatically">Make your outlets unload automatically</h2>
<p>The idea behind <code>viewDidUnload</code> is that the view is being unloaded out from behind your view controller. This gives you a chance to detach any pointers to it and clear any information you can rebuild easily. Your view will <em>probably</em> be loaded back in, at which point you’ll need to rebuild any caches. Apple describes this in <a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW36">UIViewController Class Reference</a>:</p>
<blockquote>
<p>When a low-memory condition occurs and the current view controller’s views are not needed, the system may opt to remove those views from memory. This method is called after the view controller’s view has been released and is your chance to perform any final cleanup. If your view controller stores separate references to the view or its subviews, you should use this method to release those references. You can also use this method to remove references to any objects that you created to support the view but that are no longer needed now that the view is gone. You should not use this method to release user data or any other information that cannot be easily recreated.</p>
</blockquote>
<p>This was mostly about handling <a href="http://en.wikipedia.org/wiki/Dangling_pointer">dangling pointers</a> before Automatic Reference Counting (ARC). But now that you’re <a href="%5B/2012/05/automatic-reference-counting/%5D">using ARC</a> and zeroing weak references, the dangling pointers will be cleaned up automatically. No need to write a <code>viewDidUnload</code>!</p>
<p>Generally, the stuff people put in <code>viewDidUnload</code> is better handled in <code>viewDidDisappear</code> or <code>dealloc</code>. The only thing left for <code>viewDidUnload</code> is nilling any of <strong>your</strong> caches that can be rebuilt without data loss while the view controller is still open, when needed at some point after the view has been reloaded. Really, these caches should be handled in <code>didReceiveMemoryWarning</code> instead.</p>
<h2 id="clearing_out_caches">Clearing out caches</h2>
<p>Clearing out cached information fits better into <code>didReceiveMemoryWarning</code>, so you should write it instead. Apple describes didReceiveMemoryWarning in <a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW4">UIViewController Class Reference</a>, too:</p>
<blockquote>
<p>You can override this method to release any additional memory used by your view controller. If you do, your implementation of this method must call the super implementation at some point to allow the view controller to release its view. If your view controller holds references to views in the view hierarchy, you should release those references in the <code>viewDidUnload</code> method instead.</p>
</blockquote>
<p>Not only does writing your cache purging in <code>didReceiveMemoryWarning</code> better suit the documentation, but you’ll be less confused when you read <code>didReceiveMemoryWarning</code> in your code than <code>viewDidUnload</code>.</p>
<p>I’m not taking a ridiculous position here, either. <code>viewDidUnload</code> is deprecated with iOS 6.</p>
<h2 id="summary">Summary</h2>
<ol>
<li>Use ARC.</li>
<li>Use weak references for <code>IBOutlets</code> that are part of the view hierarchy. They’ll automatically be zeroed when the view is unloaded.</li>
<li>Use <code>didReceiveMemoryWarning</code> to dispose of anything you can rebuild easily, by (for example) removing all objects from a <code>NSDictionary</code>. Or, better yet, use <code>NSCache</code> where this behaviour is automatic.</li>
<li>Use <code>dealloc</code> to dispose of any external resources that ARC will handle automatically.</li>
<li>Don’t even write a <code>viewDidUnload</code>, unless you’re observing values of outlets with KVO.</li>
</ol>
<p>If you’re still using Manual Release Retain, none of this applies. You should use retained outlets instead. It’s better to potentially use memory a little too long than to have a dangling pointer. And you should really think about climbing that fence and joining us on the Automatic Reference Counting side. Unbelievably, the grass is just as green here as it looks.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
