<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Peter Steinberger]]></title>
  <link href="http://petersteinberger.com/atom.xml" rel="self"/>
  <link href="http://petersteinberger.com/"/>
  <updated>2013-03-06T22:50:23+00:00</updated>
  <id>http://petersteinberger.com/</id>
  <author>
    <name><![CDATA[Peter Steinberger]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Adding keyboard shortcuts to UIAlertView]]></title>
    <link href="http://petersteinberger.com/blog/2013/adding-keyboard-shortcuts-to-uialertview/"/>
    <updated>2013-03-06T22:38:00+00:00</updated>
    <id>http://petersteinberger.com/blog/2013/adding-keyboard-shortcuts-to-uialertview</id>
    <content type="html"><![CDATA[<p>I&#8217;m not even home from the more than excellent <a href="http://nsconference.com">NSConference</a> in Leicester, but had to hack on something super awesome that <a href="http://twitter.com/edog1203">Evan Doll of Flipboard</a> presented earlier today. They added keyboard support to UIAlertView and UIActionSheet (amongst other things) - simply to <strong>make debugging in the Simulator faster by accepting ESC and Enter keys</strong> - something that Apple should have done anyway. There&#8217;s not much value in shipping this in release builds except better support for bluetooth keyboards. And since this hack uses private API AND accesses a struct whose memory layout could change, I don&#8217;t recommend shipping it. If you do, make sure that you white-list iOS versions and block this method by default on unknown future versions of iOS. I&#8217;m using it in <a href="http://pspdfkit.com">PSPDFKit</a> only when compiled in DEBUG mode for the Simulator.</p>

<p>The actual hack is mostly based on <a href="http://nacho4d-nacho4d.blogspot.co.uk/2012/01/catching-keyboard-events-in-ios.html">this blog post about intercepting the keyboard on iOS</a>, and it&#8217;s not pretty. I had to modify some constants to make it work on iOS5/6.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#define GSEVENT_TYPE 2</span>
</span><span class='line'><span class="cp">#define GSEVENT_FLAGS 12</span>
</span><span class='line'><span class="cp">#define GSEVENTKEY_KEYCODE 15</span>
</span><span class='line'><span class="cp">#define GSEVENT_TYPE_KEYUP 10</span>
</span><span class='line'><span class="c1">// http://nacho4d-nacho4d.blogspot.co.uk/2012/01/catching-keyboard-events-in-ios.html</span>
</span><span class='line'><span class="n">__attribute__</span><span class="p">((</span><span class="n">constructor</span><span class="p">))</span> <span class="k">static</span> <span class="kt">void</span> <span class="n">PSPDFKitAddKeyboardSupportForUIAlertView</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">@</span><span class="n">autoreleasepool</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// Hook into sendEvent: to get keyboard events.</span>
</span><span class='line'>        <span class="kt">SEL</span> <span class="n">sendEventSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_sendEvent:&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="n">IMP</span> <span class="n">sendEventIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="kt">id</span> <span class="n">_self</span><span class="p">,</span> <span class="n">UIEvent</span> <span class="o">*</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="n">sendEventSEL</span><span class="p">,</span> <span class="n">event</span><span class="p">);</span> <span class="c1">// call original implementation.</span>
</span><span class='line'>
</span><span class='line'>            <span class="kt">SEL</span> <span class="n">gsEventSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">([</span><span class="n">NSString</span> <span class="nl">stringWithFormat:</span><span class="s">@&quot;%@%@Event&quot;</span><span class="p">,</span> <span class="s">@&quot;_&quot;</span><span class="p">,</span> <span class="s">@&quot;gs&quot;</span><span class="p">]);</span>
</span><span class='line'>            <span class="k">if</span> <span class="p">([</span><span class="n">event</span> <span class="nl">respondsToSelector:</span><span class="n">gsEventSEL</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>                <span class="c1">// Key events come in form of UIInternalEvents.</span>
</span><span class='line'>                <span class="c1">// They contain a GSEvent object which contains a GSEventRecord among other things.</span>
</span><span class='line'>                <span class="kt">int</span> <span class="o">*</span><span class="n">eventMem</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)[</span><span class="n">event</span> <span class="nl">performSelector:</span><span class="n">gsEventSEL</span><span class="p">];</span>
</span><span class='line'>                <span class="k">if</span> <span class="p">(</span><span class="n">eventMem</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                    <span class="k">if</span> <span class="p">(</span><span class="n">eventMem</span><span class="p">[</span><span class="n">GSEVENT_TYPE</span><span class="p">]</span> <span class="o">==</span> <span class="n">GSEVENT_TYPE_KEYUP</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                        <span class="n">UniChar</span> <span class="o">*</span><span class="n">keycode</span> <span class="o">=</span> <span class="p">(</span><span class="n">UniChar</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">eventMem</span><span class="p">[</span><span class="n">GSEVENTKEY_KEYCODE</span><span class="p">]);</span>
</span><span class='line'>                        <span class="kt">int</span> <span class="n">eventFlags</span> <span class="o">=</span> <span class="n">eventMem</span><span class="p">[</span><span class="n">GSEVENT_FLAGS</span><span class="p">];</span>
</span><span class='line'>                        <span class="c1">//NSLog(@&quot;Pressed %d&quot;, *keycode);</span>
</span><span class='line'>                        <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">postNotificationName:</span><span class="s">@&quot;PSPDFKeyboardEventNotification&quot;</span> <span class="nl">object:</span><span class="nb">nil</span> <span class="nl">userInfo:</span> <span class="err">@</span><span class="p">{</span><span class="s">@&quot;keycode&quot;</span> <span class="o">:</span> <span class="err">@</span><span class="p">(</span><span class="o">*</span><span class="n">keycode</span><span class="p">),</span> <span class="s">@&quot;eventFlags&quot;</span> <span class="o">:</span> <span class="err">@</span><span class="p">(</span><span class="n">eventFlags</span><span class="p">)}];</span>
</span><span class='line'>                    <span class="p">}</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">});</span>
</span><span class='line'>        <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">UIApplication</span><span class="p">.</span><span class="n">class</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">sendEvent:</span><span class="p">),</span> <span class="n">sendEventSEL</span><span class="p">,</span> <span class="n">sendEventIMP</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// Add keyboard handler for UIAlertView.</span>
</span><span class='line'>        <span class="kt">SEL</span> <span class="n">didMoveToWindowSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_didMoveToWindow&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="n">IMP</span> <span class="n">didMoveToWindowIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="n">UIAlertView</span> <span class="o">*</span><span class="n">_self</span><span class="p">,</span> <span class="n">UIEvent</span> <span class="o">*</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="n">didMoveToWindowSEL</span><span class="p">,</span> <span class="n">event</span><span class="p">);</span> <span class="c1">// call original implementation.</span>
</span><span class='line'>
</span><span class='line'>            <span class="k">static</span> <span class="kt">char</span> <span class="n">kPSPDFKeyboardEventToken</span><span class="p">;</span>
</span><span class='line'>            <span class="k">if</span> <span class="p">(</span><span class="n">_self</span><span class="p">.</span><span class="n">window</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                <span class="kt">id</span> <span class="n">observerToken</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">addObserverForName:</span><span class="s">@&quot;PSPDFKeyboardEventNotification&quot;</span> <span class="nl">object:</span><span class="nb">nil</span> <span class="nl">queue:</span><span class="nb">nil</span> <span class="nl">usingBlock:</span><span class="o">^</span><span class="p">(</span><span class="n">NSNotification</span> <span class="o">*</span><span class="n">notification</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                    <span class="n">NSUInteger</span> <span class="n">keyCode</span> <span class="o">=</span> <span class="p">[</span><span class="n">notification</span><span class="p">.</span><span class="n">userInfo</span><span class="p">[</span><span class="s">@&quot;keycode&quot;</span><span class="p">]</span> <span class="n">integerValue</span><span class="p">];</span>
</span><span class='line'>                    <span class="k">if</span> <span class="p">(</span><span class="n">keyCode</span> <span class="o">==</span> <span class="mi">41</span><span class="p">)</span> <span class="cm">/* ESC */</span> <span class="p">{</span>
</span><span class='line'>                        <span class="p">[</span><span class="n">_self</span> <span class="nl">dismissWithClickedButtonIndex:</span><span class="n">_self</span><span class="p">.</span><span class="n">cancelButtonIndex</span> <span class="nl">animated:</span><span class="n">YES</span><span class="p">];</span>
</span><span class='line'>                    <span class="p">}</span><span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">keyCode</span> <span class="o">==</span> <span class="mi">40</span><span class="p">)</span> <span class="cm">/* ENTER */</span> <span class="p">{</span>
</span><span class='line'>                        <span class="p">[</span><span class="n">_self</span> <span class="nl">dismissWithClickedButtonIndex:</span><span class="n">_self</span><span class="p">.</span><span class="n">numberOfButtons</span><span class="o">-</span><span class="mi">1</span> <span class="nl">animated:</span><span class="n">YES</span><span class="p">];</span>
</span><span class='line'>                    <span class="p">}</span>
</span><span class='line'>                <span class="p">}];</span>
</span><span class='line'>                <span class="n">objc_setAssociatedObject</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">kPSPDFKeyboardEventToken</span><span class="p">,</span> <span class="n">observerToken</span><span class="p">,</span> <span class="n">OBJC_ASSOCIATION_RETAIN_NONATOMIC</span><span class="p">);</span>
</span><span class='line'>            <span class="p">}</span><span class="k">else</span> <span class="p">{</span>
</span><span class='line'>                <span class="kt">id</span> <span class="n">observerToken</span> <span class="o">=</span> <span class="n">objc_getAssociatedObject</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">kPSPDFKeyboardEventToken</span><span class="p">);</span>
</span><span class='line'>                <span class="k">if</span> <span class="p">(</span><span class="n">observerToken</span><span class="p">)</span> <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">removeObserver:</span><span class="n">observerToken</span><span class="p">];</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">});</span>
</span><span class='line'>        <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">UIAlertView</span><span class="p">.</span><span class="n">class</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="n">didMoveToWindow</span><span class="p">),</span> <span class="n">didMoveToWindowSEL</span><span class="p">,</span> <span class="n">didMoveToWindowIMP</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>You also need some swizzling helpers, here&#8217;s what I use:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html</span>
</span><span class='line'><span class="k">static</span> <span class="kt">void</span> <span class="nf">PSPDFSwizzleMethod</span><span class="p">(</span><span class="n">Class</span> <span class="n">c</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">orig</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">new</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">Method</span> <span class="n">origMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">orig</span><span class="p">);</span>
</span><span class='line'>    <span class="n">Method</span> <span class="n">newMethod</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">new</span><span class="p">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">class_addMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">orig</span><span class="p">,</span> <span class="n">method_getImplementation</span><span class="p">(</span><span class="n">newMethod</span><span class="p">),</span> <span class="n">method_getTypeEncoding</span><span class="p">(</span><span class="n">newMethod</span><span class="p">)))</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">class_replaceMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">new</span><span class="p">,</span> <span class="n">method_getImplementation</span><span class="p">(</span><span class="n">origMethod</span><span class="p">),</span> <span class="n">method_getTypeEncoding</span><span class="p">(</span><span class="n">origMethod</span><span class="p">));</span>
</span><span class='line'>    <span class="p">}</span><span class="k">else</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">method_exchangeImplementations</span><span class="p">(</span><span class="n">origMethod</span><span class="p">,</span> <span class="n">newMethod</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">void</span> <span class="nf">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">Class</span> <span class="n">c</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">orig</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">newSel</span><span class="p">,</span> <span class="n">IMP</span> <span class="n">impl</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">Method</span> <span class="n">method</span> <span class="o">=</span> <span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">orig</span><span class="p">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">class_addMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">newSel</span><span class="p">,</span> <span class="n">impl</span><span class="p">,</span> <span class="n">method_getTypeEncoding</span><span class="p">(</span><span class="n">method</span><span class="p">)))</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">PSPDFLogError</span><span class="p">(</span><span class="s">@&quot;Failed to add method: %@ on %@&quot;</span><span class="p">,</span> <span class="n">NSStringFromSelector</span><span class="p">(</span><span class="n">newSel</span><span class="p">),</span> <span class="n">c</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span><span class="k">else</span> <span class="n">PSPDFSwizzleMethod</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">orig</span><span class="p">,</span> <span class="n">newSel</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to center content within UIScrollView]]></title>
    <link href="http://petersteinberger.com/blog/2013/how-to-center-uiscrollview/"/>
    <updated>2013-02-21T12:07:00+00:00</updated>
    <id>http://petersteinberger.com/blog/2013/how-to-center-uiscrollview</id>
    <content type="html"><![CDATA[<p>There seems to be <a href="http://stackoverflow.com/questions/1496015/is-it-possible-to-center-content-in-a-uiscrollview-like-apples-photos-app/3479059">quite some</a> <a href="http://stackoverflow.com/questions/1316451/center-content-of-uiscrollview-when-smaller/14188223">confusion</a> around the net when it comes to the best way to center a view within an UIScrollView.</p>

<p>And it&#8217;s actually not that easy to get right. In <a href="http://pspdfkit.com">PSPDFKit</a> v1 I used the Apple recommended way (see their Photo example) of <a href="https://github.com/steipete/PSTCenteredScrollView/blob/master/PSTCenteredScrollView/PSTLayoutSubviewCenteredScrollView.m">subclassing layoutSubviews</a>. This was &#8220;good enough&#8221; until I added <a href="http://pspdfkit.com/features.html">smart zoom</a> in version 2 of my PDF framework. (That is, text block are detected and zoomed onto with a double tap, just like you&#8217;re used to in Mobile Safari). I noticed that the zoomToRect method did not work properly, the final rect was offset - and it was quite obvious why, since the layoutSubview-method moved around the frame of the view that was zoomed on. And <a href="http://stackoverflow.com/questions/7270135/uiscrollviews-zoomtorect-needs-to-be-called-twice/7291882#7291882">don&#8217;t try to compensate this moving</a> - it seems quite impossible to get right.</p>

<p>The next generation (<a href="http://pspdfkit.com">PSPDFKit</a> 2.2 and upwards) used <a href="https://github.com/steipete/PSTCenteredScrollView/blob/master/PSTCenteredScrollView/PSTContentOffsetCenteredScrollView.m#L13">override setContentOffset:</a> to always center the content. This works, but has the drawback that you can&#8217;t pan the view around when you&#8217;re zooming out more - it&#8217;s always fixed centered. This solution also has a very nasty hidden bug where the UIScrollView can be &#8220;locked up&#8221; and don&#8217;t accept any touches anymore until you perform a pinch in/out. I&#8217;ve had an Apple DTS against this and even they only came up with useless workarounds. (like not using gesture recognizers&#8230; right.) It&#8217;s hard to reproduce this bug here without open sourcing half of my framework, so please just trust me on that one.</p>

<p>I&#8217;ve asked around on Twitter (thanks, everyone!) and most recommendations were about overriding layoutSubviews. Some suggested stuffing the view into another view and doing the centering manually (which I tried) but this has the drawback that you can scroll into the empty space when zoomed in (or you adapt the view and get the same problem as you had in layoutSubviews).</p>

<p>The solution that works by far best is by <a href="https://github.com/steipete/PSTCenteredScrollView/blob/master/PSTCenteredScrollView/PSTContentInsetCenteredScrollView.m#L13">setting contentInset</a>. It allows to pan around the view when zoomed out and doesn&#8217;t expose the &#8220;lock&#8221; bug that overriding setContentOffset: had. It also works fine with using zoomToRect. There are a few gotchas when using edgeInsets as well - for example I have a mechanism that <a href="http://pspdfkit.com/documentation/Classes/PSPDFViewState.html">preserves the current view state</a> (page, zoom, contentOffset) and restores that later on. If you set contentOffset to 0,0 this will un-center your image (in contrast to method 1 and 2). But you can trigger re-centering with following trick:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'>        <span class="c1">// Trigger a new centering (center will change the content offset)</span>
</span><span class='line'>        <span class="n">scrollView</span><span class="p">.</span><span class="n">contentInset</span> <span class="o">=</span> <span class="n">UIEdgeInsetsZero</span><span class="p">;</span>
</span><span class='line'>        <span class="p">[</span><span class="n">scrollView</span> <span class="n">ensureContentIsCentered</span><span class="p">];</span>
</span></code></pre></td></tr></table></div></figure>


<p>I&#8217;ve made a <a href="https://github.com/steipete/PSTCenteredScrollView">small GitHub project</a> that shows off all three centering techniques and zooms to a small rect on double tap (to show off the zoomToRect problem). Hopefully this helps someone save a few hours!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[UIAppearance for Custom Views]]></title>
    <link href="http://petersteinberger.com/blog/2013/uiappearance-for-custom-views/"/>
    <updated>2013-02-12T14:31:00+00:00</updated>
    <id>http://petersteinberger.com/blog/2013/uiappearance-for-custom-views</id>
    <content type="html"><![CDATA[<p>UIAppearance hardly is a new technologly since it was first introduced at WWDC 2011, but it still doesn&#8217;t have the adoption it deserves (guilty as charged here as well). Since now most apps are IOS5 only, there&#8217;s no excuse anymore to not adopt it. Also chances are quite high that at least some properies of your classes already support UIAppearance implicitely - since the preprocessor macro to &#8216;enable&#8217; UIAppearance is actually defined to be empty:</p>

<blockquote><p>#define UI_APPEARANCE_SELECTOR</p></blockquote>


<p>In the simplest case, add UI_APPEARANCE_SELECTOR to your properties to <em>inform others</em> that this property can be set via an UIAppearance proxy. There are however some gotchas that are not clearly mentioned in the documentation, and it&#8217;s always interesting how something like this works behind the scenes. (This is not a tutorial - go ahead and read <a href="http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html">Apple&#8217;s documentation on UIAppearance</a> if you&#8217;ve never used it before)</p>

<p>From looking at the debugger, UIAppearance is quite smart and only applies properties before the view is added to a window.</p>

<p><img src="http://petersteinberger.com/images/posts/UIAppearance-setter.png"></p>

<p>UIAppearance mostly is for UIView subclasses, with some exceptions like UIBarItem (and UIBarButtonItem) who internally handle their respecive view. For those classes Apple implemented <a href="https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UIBarItemAppearance.h">a custom apperance proxy (_UIBarItemAppearance)</a>.</p>

<p>When a custom apperance is set, <a href="https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UIAppearanceRecorder.h">_UIAppearanceRecorder</a> will track the customizations. There are also certain optimized apperance storage classes like <a href="https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UISegmentedControlAppearanceStorage.h">(_UISegmentedControlAppearanceStorage)</a> for UISegmentedControl or <a href="https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UINavigationBarAppearanceStorage.h">_UINavigationBarAppearanceStorage</a> for UINavigationBar.</p>

<p>Let&#8217;s start with a simple example, converting this class <a href="http://pspdfkit.com/">(taken from my iOS PDF SDK)</a> to work with UIAppearance:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">/// Simple rounded label.</span>
</span><span class='line'><span class="k">@interface</span> <span class="nc">PSPDFRoundedLabel</span> : <span class="nc">UILabel</span>
</span><span class='line'><span class="c1">/// Corner radius. Defaults to 10.</span>
</span><span class='line'><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="n">CGFloat</span> <span class="n">cornerRadius</span><span class="p">;</span>
</span><span class='line'><span class="c1">/// Label background. Defaults to [UIColor colorWithWhite:0.f alpha:0.6f]</span>
</span><span class='line'><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="n">UIColor</span> <span class="o">*</span><span class="n">rectColor</span><span class="p">;</span>
</span><span class='line'><span class="k">@end</span>
</span><span class='line'>
</span><span class='line'><span class="k">@implementation</span> <span class="nc">PSPDFRoundedLabel</span>
</span><span class='line'><span class="k">-</span> <span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nf">initWithFrame:</span><span class="p">(</span><span class="n">CGRect</span><span class="p">)</span><span class="nv">frame</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="nl">initWithFrame:</span><span class="n">frame</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">rectColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nl">colorWithWhite:</span><span class="mf">0.f</span> <span class="nl">alpha:</span><span class="mf">0.6f</span><span class="p">];</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">cornerRadius</span> <span class="o">=</span> <span class="mf">10.f</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setBackgroundColor:</span><span class="p">(</span><span class="n">UIColor</span> <span class="o">*</span><span class="p">)</span><span class="nv">color</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="n">super</span> <span class="nl">setBackgroundColor:</span><span class="p">[</span><span class="n">UIColor</span> <span class="n">clearColor</span><span class="p">]];</span>
</span><span class='line'>    <span class="n">self</span><span class="p">.</span><span class="n">rectColor</span> <span class="o">=</span> <span class="n">color</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="c1">// drawRect is trivial</span>
</span><span class='line'><span class="k">@end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Simply adding UI_APPEARANCE_SELECTOR will not work here. One gotcha is that UIAppearance swizzles all setters that have a default apperance, and tracks when they get changed - so that UIAppearance doesn&#8217;t override your customizations. This is a problem here, since we use setters in the initializer, and for UIAppearance it now looks as we already customized the class ourselves.
<strong>Lesson: Only use direct ivar access in the initializer for properties that comply to UI_APPEARANCE_SELECTOR</strong>.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">/// Simple rounded label.</span>
</span><span class='line'><span class="k">@interface</span> <span class="nc">PSPDFRoundedLabel</span> : <span class="nc">UILabel</span>
</span><span class='line'><span class="c1">/// Corner radius. Defaults to 10.</span>
</span><span class='line'><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="n">CGFloat</span> <span class="n">cornerRadius</span> <span class="n">UI_APPEARANCE_SELECTOR</span><span class="p">;</span>
</span><span class='line'><span class="c1">/// Label background. Defaults to [UIColor colorWithWhite:0.f alpha:0.6f]</span>
</span><span class='line'><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="n">UIColor</span> <span class="o">*</span><span class="n">rectColor</span> <span class="n">UI_APPEARANCE_SELECTOR</span><span class="p">;</span>
</span><span class='line'><span class="k">@end</span>
</span><span class='line'>
</span><span class='line'><span class="k">@implementation</span> <span class="nc">PSPDFRoundedLabel</span> <span class="p">{</span>
</span><span class='line'>    <span class="kt">BOOL</span> <span class="n">_isInitializing</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nl">initWithFrame:</span><span class="p">(</span><span class="n">CGRect</span><span class="p">)</span><span class="n">frame</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">_isInitializing</span> <span class="o">=</span> <span class="n">YES</span><span class="p">;</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="nl">initWithFrame:</span><span class="n">frame</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">_rectColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nl">colorWithWhite:</span><span class="mf">0.f</span> <span class="nl">alpha:</span><span class="mf">0.6f</span><span class="p">];</span>
</span><span class='line'>        <span class="n">_cornerRadius</span> <span class="o">=</span> <span class="mf">10.f</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="n">_isInitializing</span> <span class="o">=</span> <span class="n">NO</span><span class="p">;</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">setBackgroundColor:</span><span class="p">(</span><span class="n">UIColor</span> <span class="o">*</span><span class="p">)</span><span class="n">color</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="n">super</span> <span class="nl">setBackgroundColor:</span><span class="p">[</span><span class="n">UIColor</span> <span class="n">clearColor</span><span class="p">]];</span>
</span><span class='line'>    <span class="c1">// Check needed for UIAppearance to work (since UILabel uses setters in init)</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">_isInitializing</span><span class="p">)</span> <span class="n">self</span><span class="p">.</span><span class="n">rectColor</span> <span class="o">=</span> <span class="n">color</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="c1">// drawRect is trivial</span>
</span><span class='line'><span class="k">@end</span>
</span></code></pre></td></tr></table></div></figure>


<p>This class now fully works with UIAppearance. Notice that we had to do some ugly state checking (_isInitializing), because UILabel internally calls self.backgroundColor = [UIColor whiteColor] in the init, which then calls the setRectColor, which would already could as &#8220;changed&#8221; for UIAppearance. Notice the <strong>TaggingApperanceGeneralSetterIMP</strong> that Apple uses to track any change to the setter:</p>

<p><img src="http://petersteinberger.com/images/posts/UIAppearance-TaggingApperanceGeneralSetterIMP.png"></p>

<p>I&#8217;m using following code to test the customizations:</p>

<blockquote><p>[[PSPDFRoundedLabel appearanceWhenContainedIn:[PSCThumbnailGridViewCell class], nil] setRectColor:[UIColor colorWithRed:0.165 green:0.226 blue:0.650 alpha:0.800]];<br/>    [[PSPDFRoundedLabel appearanceWhenContainedIn:[PSCThumbnailGridViewCell class], nil] setCornerRadius:2];</p></blockquote>


<p>We can also use the runtime to query at any point what apperance settings there are for any given class. This is only meant to be used within the debugger, since it uses private API to query <a href="https://github.com/nst/iOS-Runtime-Headers/blob/2d1452d163050ef211efed237de1ea132823fc8c/Frameworks/UIKit.framework/_UIAppearance.h">_UIAppearance</a>.</p>

<blockquote><p>po [[NSClassFromString(@&#8221;_UIAppearance&#8221;) _appearanceForClass:[PSPDFRoundedLabel class] withContainerList:@[[PSCThumbnailGridViewCell class]]] valueForKey:@&#8221;_appearanceInvocations&#8221;]<br/>$0 = 0x0bd08cc0 <__NSArrayM 0xbd08cc0>(<br/>&lt;NSInvocation: 0xbd08a60&gt;<br/>return value: {v} void<br/>target: {@} 0x0<br/>selector: {:} _UIAppearance_setRectColor:<br/>argument 2: {@} 0xbd08210<br/>,<br/>&lt;NSInvocation: 0xbd09100&gt;<br/>return value: {v} void<br/>target: {@} 0x0<br/>selector: {:} _UIAppearance_setCornerRadius:<br/>argument 2: {f} 0.000000<br/>)</p></blockquote>


<p>That&#8217;s it, the class is fully compatible with UIAppearance. When using this inside a framework, you should write custom UIAppearance rules instead of manually setting the property, to allow to override those rules from the outside (remember, manually setting a property will disable it for apperance usage). +load is a good time for that. There are some more gotchas on UIAppearance like BOOL not being supported (use NSInteger instead), and some honorable exceptions that do support apperance selectors, like <a href="https://github.com/danielamitay/DACircularProgress/commit/f5fbf993b432eeedd3d8110f346361b33cf6482f">DACircularProgress</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Hacking block support into UIMenuItem]]></title>
    <link href="http://petersteinberger.com/blog/2012/hacking-block-support-into-uimenuitem/"/>
    <updated>2012-07-17T16:22:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/hacking-block-support-into-uimenuitem</id>
    <content type="html"><![CDATA[<p>tl;dr: UIMenuItem! Blocks! <a href="https://github.com/steipete/PSMenuItem">Get the code on GitHub.</a></p>

<p>While developing a new version of <a href="http://pspdfkit.com">PSPDFKit</a>, I started using UIMenuController more and more. The first thing you&#8217;ll notice is that it&#8217;s different from your typical target/action pattern: the target is missing.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">[</span><span class="n">UIMenuItem</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTitle:</span><span class="s">@&quot;Title&quot;</span> <span class="nl">action:</span><span class="k">@selector</span><span class="p">(</span><span class="nl">menuItemAction:</span><span class="p">)];</span>
</span></code></pre></td></tr></table></div></figure>


<p>This is partly actually pretty genius. iOS checks if the @selector can be invoked calling <code>canPerformAction:withSender:</code> which is part of UIResponder. If the object returns NO, the system walks up the UIResponder chain until it finds an object that returns YES or <code>nextResponder</code> returns nil.</p>

<p>A typical resonder chain looks like this: UIView -> UIViewController -> UINavigationController -> UIApplication -> AppDelegate.</p>

<p>Now this is great in theory, if you implement e.g. <code>copy:</code> on your viewController, the firstResponder can be any subview and it still works. In practice however I found this more limitating and annoying. And <a href="https://twitter.com/hatfinch/statuses/224925043556225024">I&#8217;m not the only one</a>.</p>

<p>Especially if your menus get more complex, your code is littered with selectors. So let&#8217;s write a block-based subclass. Enter PSMenuItem.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">PSMenuItem</span> <span class="o">*</span><span class="n">actionItem</span> <span class="o">=</span> <span class="p">[[</span><span class="n">PSMenuItem</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTitle:</span><span class="s">@&quot;Action 1&quot;</span> <span class="nl">block:</span><span class="o">^</span><span class="p">{</span>
</span><span class='line'>    <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Hello, from a block!&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">}];</span>
</span></code></pre></td></tr></table></div></figure>


<p>My <em>naive</em> approach was to just use one common selector internally and execute the block that is saved in the PSMenuItem subclass. The only problem: <code>(id)sender</code> of the action gets called with the UIMenuController. This is <strong>wrong</strong> on so many levels, especially since UIMenuController is a singleton anyway. There&#8217;s no easy way to know what UIMenuItem has been pressed.
But since I already was committed in writing the subclass, that couldn&#8217;t stop me. We just create a unique selector for each UIMenuItem, and catch execution at a lower level.</p>

<h3>Enter Cocoa&#8217;s Message Forwarding</h3>

<p>If the runtime can&#8217;t find a selector on the current class, message forwarding is started. (That&#8217;s the tech that allows classes like NSUndoManager or NSProxy.)</p>

<ol>
<li><p><strong>Lazy method resolution:</strong> <code>resolveInstanceMethod:</code> is called. If this returns YES, message sending is restarted as the systme assumes that the method has been added at runtime. We could theoretically use this and add a method that calls the block at runtime. But this would pollute the object with many new methods - not what we want.</p></li>
<li><p><strong>Fast forwarding path:</strong>  <code>-(id)forwardingTargetForSelector:(SEL)sel</code> has been added in Leopard as a faster approach to the NSInvocation-based message forwarding. We could use this to react to our custom selector, but we would have to return an object that implements our selector (or does not throw an exception with undefined methods.) Possible candidate, but there&#8217;s something better.</p></li>
<li><p><strong>Normal forwarding path:</strong> This is the &#8220;classic&#8221; message forwarding that exists since the old days. Here actually two methods are called. <code>methodSignatureForSelector:</code>, followed by <code>forwardInvocation:</code>. (The method signature is needed to build the NSInvocation). PSMenuItem hooks into both of those methods. But let&#8217;s go step by step through PSMenuItem&#8217;s <code>+ (void)installMenuHandlerForObject:(id)object</code></p></li>
</ol>


<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">+</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">installMenuHandlerForObject:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nv">object</span> <span class="p">{</span>
</span><span class='line'>    <span class="err">@</span><span class="n">autoreleasepool</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">@synchronized</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="c1">// object can be both a class or an instance of a class.</span>
</span><span class='line'>            <span class="n">Class</span> <span class="n">objectClass</span> <span class="o">=</span> <span class="n">class_isMetaClass</span><span class="p">(</span><span class="n">object_getClass</span><span class="p">(</span><span class="n">object</span><span class="p">))</span> <span class="o">?</span> <span class="n">object</span> <span class="o">:</span> <span class="p">[</span><span class="n">object</span> <span class="n">class</span><span class="p">];</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note the @synchronized, swizzling is not thread save. Also we add an @autoreleasepool here, as this could be executed from +load or +initialize; at a very early time where&#8217;s no default NSAutoreleasePool in place yet.</p>

<p><code>class_isMetaClass</code> checks if <code>object_getClass</code> returns a class or a meta-class. This is needed because object can both be a instance of a class or a class object itself, and you can&#8217;t just invoke a isKindOfClass on a Class object. If you&#8217;re wondering what a meta-class is; it&#8217;s basically a class that defines methods available on the class. <a href="http://cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html">CocoaWithLove has a great article on that.</a></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'>        <span class="c1">// check if menu handler has been already installed.</span>
</span><span class='line'>        <span class="kt">SEL</span> <span class="n">canPerformActionSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_canPerformAction:withSender:&quot;</span><span class="p">);</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">class_getInstanceMethod</span><span class="p">(</span><span class="n">objectClass</span><span class="p">,</span> <span class="n">canPerformActionSEL</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// add canBecomeFirstResponder if it is not returning YES. (or if we don&#39;t know)</span>
</span><span class='line'>            <span class="k">if</span> <span class="p">(</span><span class="n">object</span> <span class="o">==</span> <span class="n">objectClass</span> <span class="o">||</span> <span class="o">!</span><span class="p">[</span><span class="n">object</span> <span class="n">canBecomeFirstResponder</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>                <span class="kt">SEL</span> <span class="n">canBecomeFRSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_canBecomeFirstResponder&quot;</span><span class="p">);</span>
</span><span class='line'>                <span class="n">IMP</span> <span class="n">canBecomeFRIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="n">PSPDFBlockImplCast</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="kt">id</span> <span class="n">_self</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                    <span class="k">return</span> <span class="n">YES</span><span class="p">;</span>
</span><span class='line'>                <span class="p">}));</span>
</span><span class='line'>                <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">objectClass</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="n">canBecomeFirstResponder</span><span class="p">),</span> <span class="n">canBecomeFRSEL</span><span class="p">,</span> <span class="n">canBecomeFRIMP</span><span class="p">);</span>
</span><span class='line'>            <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Here we test if the class has already been swizzled by us with using <code>class_getInstanceMethod</code>. Again, because object might be a Class already, we can&#8217;t just use <code>respondsToSelector:</code>. Next, we test if we should add a handler to <code>canBecomeFirstResponder</code>. This is needed to make the UIMenuController display in the first place.</p>

<p>Note the <code>imp_implementationWithBlock</code>. This is a new method in iOS4.3 upwards, but has just a much nicer syntax and is more compact than classic C functions. There&#8217;s another small annoyance, <code>PSPDFBlockImplCast</code>. The syntax of <code>imp_implementationWithBlock</code> was slightly changed in yet to be released versions of Xcode, older versions still need the <code>(__bridge void *)</code> cast, newer versions will complain and only work without.</p>

<p><code>PSPDFReplaceMethod</code> is a helper that first adds the new method via our pspdf_ selector name, then swizzles the original implementation with our custom implementation.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'>            <span class="c1">// swizzle canPerformAction:withSender: for our custom selectors.</span>
</span><span class='line'>            <span class="c1">// Queried before the UIMenuController is shown.</span>
</span><span class='line'>            <span class="n">IMP</span> <span class="n">canPerformActionIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="n">PSPDFBlockImplCast</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="kt">id</span> <span class="n">_self</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">action</span><span class="p">,</span> <span class="kt">id</span> <span class="n">sender</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                <span class="k">return</span> <span class="n">PSIsMenuItemSelector</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> <span class="o">?</span> <span class="n">YES</span> <span class="o">:</span> <span class="p">((</span><span class="kt">BOOL</span> <span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">id</span><span class="p">,</span> <span class="kt">SEL</span><span class="p">,</span> <span class="kt">SEL</span><span class="p">,</span> <span class="kt">id</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">)(</span><span class="n">_self</span><span class="p">,</span> <span class="n">canPerformActionSEL</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">sender</span><span class="p">);</span>
</span><span class='line'>            <span class="p">}));</span>
</span><span class='line'>            <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">objectClass</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">canPerformAction:withSender:</span><span class="p">),</span> <span class="n">canPerformActionSEL</span><span class="p">,</span> <span class="n">canPerformActionIMP</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next up, we swizzle <code>canPerformAction:withSender:</code>. This is called before the UIMenuController is displayed. If we detect our custom selector (<code>PSIsMenuItemSelector</code>), we return YES, else we call the original implementation. Note the tricky casting on <code>objc_msgSend</code>. (We could also build a NSInvocation, but that would be much slower and needs much more code).</p>

<p><code>PSIsMenuItemSelector</code> is just a shorthand for <code>return [NSStringFromSelector(selector) hasPrefix:kMenuItemTrailer];</code></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'>            <span class="c1">// swizzle methodSignatureForSelector:.</span>
</span><span class='line'>            <span class="kt">SEL</span> <span class="n">methodSignatureSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_methodSignatureForSelector:&quot;</span><span class="p">);</span>
</span><span class='line'>            <span class="n">IMP</span> <span class="n">methodSignatureIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="n">PSPDFBlockImplCast</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="kt">id</span> <span class="n">_self</span><span class="p">,</span> <span class="kt">SEL</span> <span class="n">selector</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                <span class="k">if</span> <span class="p">(</span><span class="n">PSIsMenuItemSelector</span><span class="p">(</span><span class="n">selector</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>                    <span class="k">return</span> <span class="p">[</span><span class="n">NSMethodSignature</span> <span class="nl">signatureWithObjCTypes:</span><span class="s">&quot;v@:@&quot;</span><span class="p">];</span> <span class="c1">// fake it.</span>
</span><span class='line'>                <span class="p">}</span><span class="k">else</span> <span class="p">{</span>
</span><span class='line'>                    <span class="k">return</span> <span class="p">(</span><span class="n">NSMethodSignature</span> <span class="o">*</span><span class="p">)</span><span class="n">objc_msgSend</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="n">methodSignatureSEL</span><span class="p">,</span> <span class="n">selector</span><span class="p">);</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>            <span class="p">}));</span>
</span><span class='line'>            <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">objectClass</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">methodSignatureForSelector:</span><span class="p">),</span> <span class="n">methodSignatureSEL</span><span class="p">,</span> <span class="n">methodSignatureIMP</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Finally we arrive at the method that&#8217;s called during message forwarding, when the user selects a UIMenuItem. We again check for the selector and return a faked <code>NSMethodSignature</code>. If we wouldn&#8217;t return a signature here, we&#8217;d get a <em>selector not implemented</em> exception. <code>"v@:@"</code> is the selector encoding for <code>-(void)action:(id)sender</code>. v is the return type (void), the first @ is self, the : is the selector (_cmd), the @ finally is id sender. <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html">You can learn more on Apple Developer about objc type encodings.</a></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'>            <span class="c1">// swizzle forwardInvocation:</span>
</span><span class='line'>            <span class="kt">SEL</span> <span class="n">forwardInvocationSEL</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@&quot;pspdf_forwardInvocation:&quot;</span><span class="p">);</span>
</span><span class='line'>            <span class="n">IMP</span> <span class="n">forwardInvocationIMP</span> <span class="o">=</span> <span class="n">imp_implementationWithBlock</span><span class="p">(</span><span class="n">PSPDFBlockImplCast</span><span class="p">(</span><span class="o">^</span><span class="p">(</span><span class="kt">id</span> <span class="n">_self</span><span class="p">,</span> <span class="n">NSInvocation</span> <span class="o">*</span><span class="n">invocation</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                <span class="k">if</span> <span class="p">(</span><span class="n">PSIsMenuItemSelector</span><span class="p">([</span><span class="n">invocation</span> <span class="n">selector</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'>                    <span class="k">for</span> <span class="p">(</span><span class="n">PSMenuItem</span> <span class="o">*</span><span class="n">menuItem</span> <span class="k">in</span> <span class="p">[</span><span class="n">UIMenuController</span> <span class="n">sharedMenuController</span><span class="p">].</span><span class="n">menuItems</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                        <span class="k">if</span> <span class="p">([</span><span class="n">menuItem</span> <span class="nl">isKindOfClass:</span><span class="p">[</span><span class="n">PSMenuItem</span> <span class="n">class</span><span class="p">]]</span> <span class="o">&amp;&amp;</span> <span class="n">sel_isEqual</span><span class="p">([</span><span class="n">invocation</span> <span class="n">selector</span><span class="p">],</span> <span class="n">menuItem</span><span class="p">.</span><span class="n">customSelector</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>                            <span class="p">[</span><span class="n">menuItem</span> <span class="n">performBlock</span><span class="p">];</span> <span class="k">break</span><span class="p">;</span> <span class="c1">// find corresponding MenuItem and forward</span>
</span><span class='line'>                        <span class="p">}</span>
</span><span class='line'>                    <span class="p">}</span>
</span><span class='line'>                <span class="p">}</span><span class="k">else</span> <span class="p">{</span>
</span><span class='line'>                    <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="n">forwardInvocationSEL</span><span class="p">,</span> <span class="n">invocation</span><span class="p">);</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>            <span class="p">}));</span>
</span><span class='line'>            <span class="n">PSPDFReplaceMethod</span><span class="p">(</span><span class="n">objectClass</span><span class="p">,</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">forwardInvocation:</span><span class="p">),</span> <span class="n">forwardInvocationSEL</span><span class="p">,</span> <span class="n">forwardInvocationIMP</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Finally, the last piece. After <code>methodSignatureForSelector</code> returned a valid NSMethodSignature, the system builds a NSInvocation object that we can handle (or not). Here we load the selector, loop through all menuItems in the UIMenuController and finally call the block on the PSMenuItem, if found. Note that we could also extract UIMenuController from the NSInviation itself, but since it&#8217;s a singleton, there&#8217;s no need for that.</p>

<p>One, simple piece remains. We build up the custom selector in our <code>initWithTitle:block:</code></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Create a unique, still debuggable selector unique per PSMenuItem.</span>
</span><span class='line'><span class="n">NSString</span> <span class="o">*</span><span class="n">strippedTitle</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">title</span> <span class="nl">componentsSeparatedByCharactersInSet:</span><span class="p">[[</span><span class="n">NSCharacterSet</span> <span class="n">letterCharacterSet</span><span class="p">]</span> <span class="n">invertedSet</span><span class="p">]]</span> <span class="nl">componentsJoinedByString:</span><span class="s">@&quot;&quot;</span><span class="p">]</span> <span class="n">lowercaseString</span><span class="p">];</span>
</span><span class='line'><span class="n">CFUUIDRef</span> <span class="n">uuid</span> <span class="o">=</span> <span class="n">CFUUIDCreate</span><span class="p">(</span><span class="n">kCFAllocatorDefault</span><span class="p">);</span>
</span><span class='line'><span class="n">NSString</span> <span class="o">*</span><span class="n">uuidString</span> <span class="o">=</span> <span class="n">CFBridgingRelease</span><span class="p">(</span><span class="n">CFUUIDCreateString</span><span class="p">(</span><span class="n">kCFAllocatorDefault</span><span class="p">,</span> <span class="n">uuid</span><span class="p">));</span>
</span><span class='line'><span class="n">CFRelease</span><span class="p">(</span><span class="n">uuid</span><span class="p">);</span>
</span><span class='line'><span class="kt">SEL</span> <span class="n">customSelector</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">([</span><span class="n">NSString</span> <span class="nl">stringWithFormat:</span><span class="s">@&quot;%@_%@_%@:&quot;</span><span class="p">,</span> <span class="n">kMenuItemTrailer</span><span class="p">,</span> <span class="n">strippedTitle</span><span class="p">,</span> <span class="n">uuidString</span><span class="p">]);</span>
</span></code></pre></td></tr></table></div></figure>


<p>I&#8217;ve used a UUID to even allow menu items with the same title, they else would generate the same selector and would potentially call the wrong block.</p>

<p>Also, thanks to Mike Ash for his great <a href="http://www.mikeash.com/pyblog/friday-qa-2009-03-27-objective-c-message-forwarding.html">Friday Q&amp;A about Objective-C Message Forwarding.</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Using subscripting with Xcode 4.4 and iOS 4.3+]]></title>
    <link href="http://petersteinberger.com/blog/2012/using-subscripting-with-Xcode-4_4-and-iOS-4_3/"/>
    <updated>2012-07-11T15:03:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/using-subscripting-with-Xcode-4_4-and-iOS-4_3</id>
    <content type="html"><![CDATA[<p>Now that Xcode 4.4 finally has reached Golden Master and <a href="https://devforums.apple.com/message/694250#694250">you can submit apps</a>, here&#8217;s a trick to use <a href="http://clang.llvm.org/docs/ObjectiveCLiterals.html">subscripting</a> <em>right now</em>. Yes, Apple will introduce this feature in a future version of OS X and iOS, but why wait? Here&#8217;s the snippet from <a href="http://pspdfkit.com">PSPDFKit, my iOS PDF framework</a> to make it work:</p>

<script src="https://gist.github.com/3090279.js"> </script>


<p>It&#8217;s a bit crude, and Xcode won&#8217;t show any warnings if you&#8217;re doing subscripting on <em>any</em> object now, but you know what you&#8217;re doing, right? Furthermore this is only <em>temporary</em>, to make it compile with Xcode 4.4. In Xcode 4.5, this snippet won&#8217;t do anything and isn&#8217;t needed anymore, since the new SDK already has those methods defined on the classes you care about. Just make sure this is in your global header file. (e.g. precompiled headers)</p>

<p>Note that unlike literals, who are really just <em>syntactical sugar</em> and already work great in Xcode 4.4+ (LLVM 4.0+), subscripting actually calls into new methods. So how does this &#8220;magically defining headers&#8221; actually work?</p>

<p>Thanks to <a href="http://www.mikeash.com/pyblog/friday-qa-2012-06-22-objective-c-literals.html">Mike Ash</a> and <a href="http://twitter.com/0xced">Cédric Luthi</a>, here&#8217;s the answer:</p>

<blockquote><p>The subscripting compatibility shim is actually part of ARCLite. The <strong>ARCLite</strong>load function takes care of dynamically adding the four subscript methods with class_addMethod. The implementations of these methods simply call the equivalent non-subscript methods.</p></blockquote>

<script src="https://gist.github.com/3090318.js"> </script>


<p>If you, for <a href="http://www.learn-cocos2d.com/2012/06/mythbusting-8-reasons-arc/"><em>any</em> reason</a>, are not on ARC yet, you really want to force the compiler to link with libarclite using the <code>-fobjc-arc</code> linker flag. This works all the way back to iOS 4.3.</p>

<p>Also check out the new Xcode Refactoring Assistant to convert your codebase to Modern Objective-C. It&#8217;s awesome.</p>

<p>Wanna know more tips and tricks about iOS? <a href="http://twitter.com/steipete">Follow @steipete on Twitter</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Pimping recursiveDescription]]></title>
    <link href="http://petersteinberger.com/blog/2012/pimping-recursivedescription/"/>
    <updated>2012-07-01T15:49:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/pimping-recursivedescription</id>
    <content type="html"><![CDATA[<p>While working on <a href="http://pspdfkit.com">PSPDFKit</a>, I more and more embrace viewController containment to better distribute responsibilities between different view controllers.
One thing that always annoyed me about that is that <code>po [[UIWindow keyWindow] recursiveDescription]</code> is less and less useful if you just see a big bunch of UIView&#8217;s.
I asked some engineers at WWDC if there&#8217;s something like recursiveDescription, just for UIViewControllers, but they didn&#8217;t had a answer on that, so I finally wrote my own.</p>

<p>Regular output of <code>po [[UIWindow keyWindow] recursiveDescription]</code></p>

<script src="https://gist.github.com/3028506.js"> </script>


<p>Pimped output:</p>

<script src="https://gist.github.com/3028503.js"> </script>


<p>The solution is surprisingly simple. Add the following code somewhere to your project and you&#8217;re done.
As a bonus, this also <em>lists attached childViewControllers</em> if you run iOS5 or above, and it will show you if a childViewController has a greater frame than it&#8217;s parentViewController (this is usually a easy to miss bug).</p>

<p>Note the usage of <code>__attribute__((constructor))</code>, this will call the method at a very early stage on app loading. (thus we need an explicit @autorelease pool).</p>

<script src="https://gist.github.com/3028524.js"> </script>


<p>Works with or without ARC, iOS 4.3 and above. (<code>imp_implementationWithBlock</code> requires iOS 4.3, unless you want to perform some <a href="https://github.com/landonf/plblockimp">dark magic</a>.)</p>

<p>For those if you that are curious, I use a private API call (<code>_viewDelegate</code>), but that&#8217;s obfuscated (it would pass a AppStore review) and you really only should compile that function in DEBUG mode anyway.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[NSURLCache uses a disk-cache as of iOS5]]></title>
    <link href="http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5/"/>
    <updated>2012-04-10T19:37:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5</id>
    <content type="html"><![CDATA[<p>While writing  <a href="https://github.com/steipete/AFDownloadRequestOperation">AFDownloadRequestOperation</a>, a new subclass for <a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a> I discovered that the behavior of NSURLCache changed between iOS 4.x and iOS 5.x.</p>

<p>Before iOS5, NSURLCache just saved requests <em>to memory</em>, even if <a href="https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html">the documentation said otherwise</a> - the diskCapacity property was silently ignored. This led to some open source subclasses of NSURLCache, that retrofit disc caching. Most popular is <a href="https://github.com/rs/SDURLCache">SDURLCache</a> and <a href="https://github.com/steipete/SDURLCache">my enhanced, faster fork of it</a>. Even Apple has an example online that shows how to <a href="https://developer.apple.com/library/ios/#samplecode/URLCache/Introduction/Intro.html">create a simple URLCache</a>.</p>

<p>As of iOS5, NSURLCache automatically saves responses <em>to disk</em>. I haven&#8217;t found anything the release notes that confirms the change, but I tried it both with iOS 4.3/5.0/5.1 on the simulator and the device, and with every 5.x version a disk cache file is created and populated. This is great, as many developers probably aren&#8217;t aware of this and the system just does the right thing - and it&#8217;s fast.</p>

<p><img src="http://petersteinberger.com/assets/cache_db.png" title="[477] [116] [Cache.db]" ></p>

<p>If the <a href="http://condor.depaul.edu/dmumaugh/readings/handouts/SE435/HTTP/node24.html">Cache-Control headers</a> indicate that this request can be cached, iOS automatically saves it to a local sqlite cache file in AppDirectory/Caches/(bundleid)/Cache.db. E.g. <code>public, max-age=31536000</code> marks that the request cache will be valid for a year, as max-age is listed in seconds.</p>

<p>The sqlite scheme for the cache looks identical with the one used in OS X:</p>

<div><script src='https://gist.github.com/2356581.js'></script>
<noscript><pre><code>PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER);
CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, version INTEGER, hash_value INTEGER, storage_policy INTEGER, request_key TEXT UNIQUE, time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB);
CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, receiver_data BLOB);
DELETE FROM sqlite_sequence;
CREATE INDEX request_key_index ON cfurl_cache_response(request_key);
CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp);
CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID);
CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID);
COMMIT;
</code></pre></noscript></div>


<p>So, why should you care? Well, the Cache.db caches <strong>any file</strong> that has a correct Cache-Control header set. Thus, if you download a pdf document, it might end up in your disk cache as well, taking up twice the memory.</p>

<p>The <a href="https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html">default NSURLCache</a> will be used, with a disk limit of 20MB. You can easily test this with gdb/lldb:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>p (int)[[NSURLCache sharedURLCache] diskCapacity]</span></code></pre></td></tr></table></div></figure>


<p>The <code>memoryCapacity</code> defaults to 40MB, although the cache will clear itself in low memory situations.</p>

<p>So for downloads that you manually save to disk, you might want to override the <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Concepts/URLOverview.html#//apple_ref/doc/uid/20001834-155585-BCIBICDJ">NSURLConnection delegate connection:willCacheResponse:</a> and return nil.</p>

<div><script src='https://gist.github.com/2356842.js'></script>
<noscript><pre><code>// changes the default implementation in AFURLConnectionOperation to NOT cache per default, unless a block is set.
// we assume that downloads shouln't be saved into NSURLCache (which uses a disk-cache since iOS5)
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection 
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse 
{
    if (self.cacheResponse) {
        return self.cacheResponse(connection, cachedResponse);
    } else {
        if ([self isCancelled]) {
            return nil;
        }
        
        return nil; 
    }
}</code></pre></noscript></div>


<p>With creating NSURLRequest using <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLRequest_Class/Reference/Reference.html">requestWithURL:cachePolicy:timeoutInterval:</a>, you can define the cachePolicy, but this only allows you to choose how if and how the cache will be <em>read</em>.</p>

<p>Available options are: only use the cache value (<code>NSURLRequestReturnCacheDataDontLoad</code>), try the cache and load if different (<code>NSURLRequestReturnCacheDataElseLoad</code>) or ignore cache entirely (<code>NSURLRequestReloadIgnoringLocalCacheData</code>).</p>

<p>The default option, if not set explicitly, is <code>NSURLRequestUseProtocolCachePolicy</code>, which most of the time is equivalent to <code>NSURLRequestReturnCacheDataElseLoad</code>, thus uses the cache if the object hasn&#8217;t changed. There are a few other options in the enum, but those are unimplemented.</p>

<p><strong>Note</strong>: There doesn&#8217;t seem a way to <em>force</em> caching of certain requests, connection:willCacheResponse: is only called if the response contains a Cache-Control header, according to <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html#//apple_ref/doc/uid/20001836-169425">Apple&#8217;s documentation</a>:</p>

<blockquote><p>The delegate receives connection:willCacheResponse: messages only for protocols that support caching.</p></blockquote>


<p>Lastly, Apple suggests that caching can also be fine-tuned with subclassing NSURLProtocol, which indeed allows some interesting use-cases like <a href="http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588">providing a cache for UIWebView</a> or <a href="http://aptogo.co.uk/2010/07/protecting-resources/">decrypting files on the fly</a>.</p>

<p>If you&#8217;re not yet using <a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a>, you really should. It&#8217;s a big step forward compared to classical NSURLConnection handling, even if Apple recently added a few <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsurlconnection_Class/Reference/Reference.html">new fancy shorthands</a> in iOS5. In AFNetworking, your network operations are indeed subclasses of NSOperation, which allows a much better control what&#8217;s currently running, and <a href="http://afnetworking.org/Documentation/Classes/AFHTTPClient.html">AFHTTPClient</a> is the perfect base class to implement any API.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Moving on... or how waiting for a visa was the best thing ever happened to me.]]></title>
    <link href="http://petersteinberger.com/blog/2012/moving-on/"/>
    <updated>2012-04-06T16:00:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/moving-on</id>
    <content type="html"><![CDATA[<p>A few days ago I booked my flight back to <a href="https://p.twimg.com/Ak83Q74CEAAprvc.jpg">Austria</a>. Starting with April 13, I&#8217;m a <strong>full-time indie</strong> again.
You gonna say, what! You&#8217;re leaving the Bay Area? This is sad! But I&#8217;m <a href="http://en.memgenerator.pl/mem-image/i-m-so-excited-en-ffffff"><em>very</em></a> excited about it.</p>

<p>I&#8217;m in a better situation than ever. I finally figured out what I wanna do with my life. I&#8217;m no longer enviously looking at other places. I learned what&#8217;s really important. And that you can&#8217;t just replace life-long friends.</p>

<p>Don&#8217;t get me wrong. I&#8217;m gonna miss San Francisco. Really. Such a great city. Such an awesome <em>spirit</em>; unlike any other city I&#8217;ve ever been to. And heck, half of my Twitter followers are here, so super-nice to finally meet up with you guys!</p>

<p>So why leave at all? Well, it just didn&#8217;t work out. I&#8217;m <a href="http://www.visualrevolt.net/wp-content/uploads/2011/05/head-full-of-ideas.jpg">full of ideas</a> and really, <em>really</em> wanna work on my own stuff.</p>

<p>Back when I got my job offer at a WWDC party in 2011, I was in a whole different position, <a href="http://itunes.apple.com/at/app/3mobiletv/id404154552?mt=8">worked</a>. <a href="http://itunes.apple.com/at/app/inside3/id440477229?mt=8">as</a>. <a href="http://itunes.apple.com/app/amano-media/id403978602?mt=8">a</a>. <a href="http://itunes.apple.com/us/app/planetromeo-date-chat-for/id404386888?mt=8">freelancer</a>. for some nice companies, but wanted a change. So the prospect of living in San Francisco, being part of the <a href="http://news.ycombinator.com/">startup culture</a>, working together on something great, was <em>very, very</em> appealing. (damn you, Hacker News!)</p>

<p>So, I accepted the offer and waited for my visa. Waited, and waited. I stopped doing freelance work because initially we thought it&#8217;d all be done in a month. Ha, how wrong we were. In the end it took more than <em>6</em> month! I even finished my bachelors degree during that time.</p>

<p>Not only that. Suddenly my mind was free from all the freelance work (and the feeling of guilt when I didn&#8217;t work on them). Naturally, I filled that space up with <a href="https://github.com/steipete/PSPushPopPressView">other</a>. <a href="https://github.com/steipete/PSStackedView">projects</a>. And, partly <a href="http://getsuperpin.com/">inspired by some friends</a>, I wanted to try the paid component business. <a href="http://pspdfkit.com">So this happened</a>. In the end, while waiting for my visa, I&#8217;ve created a viable business that&#8217;s now making more money than my full-time job possibly ever could.</p>

<p>All gears where set for SF, so I took the gig, it was all about the experience now. I really believed I could do this. But then, managing a 40h+ job with basically <em>another</em> full-time job on the side is nearly impossible, and after <a href="http://i0.kym-cdn.com/photos/images/original/000/000/578/1234931504682.jpg">killing myself</a> for a while, I finally had to choose which is more fun. Well, let&#8217;s say it wasn&#8217;t a hard decision. (To be correct, it was very hard. I hate letting people down, and of course while they&#8217;re a business, I also respected them, and had a hard time to disappoint their high expectations they had in me.)</p>

<p>This all sounds so simple and logical now, but I really jumped into the whole business and landed on my feet. Support requests and ideas are exploding, and as revenue goes up, so does the amount of time to care about my product. Also, I was really looking forward to work wit some well-respected people in the iOS scene, only to find out that they had already left the company before I came here.</p>

<p>Lastly, <a href="http://nsconference.com">NSConference</a> gave me the final kick. Such an amazingly great amount of inspiring people that genuinely love what they do - you can&#8217;t just go back to your 11-7 job after experiencing that. Also, meeting customers that use <em>your</em> product and really love it is the sweetest thing ever.</p>

<p>I have no regrets on trying it, and I&#8217;m very thankful for the opportunity they&#8217;ve given me - in fact it&#8217;s one of the best things ever happened to me! It gave me the guts to go full-indie, without even thinking about it. It helped me to figure out what I really wanna do with my life.</p>

<p>So, what&#8217;s next? Well, I&#8217;ve big plans with making <a href="http://pspdfkit.com">PSPDFKit</a> even better&#8230; and then there&#8217;s this other app idea, that&#8217;s crossing my mind way too often. I&#8217;m building this for myself, because I really, really want it. And every time I did that, it turned out to be a great success. If you wanna hear more about it, you should <a href="http://twitter.com/steipete">follow me on Twitter</a>.</p>

<p>And about San Francisco, I&#8217;ll probably be back <a href="https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash4/392412_3632276169661_1356861561_4220899_1031066655_n.jpg">pretty soon</a> ;)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Don't call willChangeValueForKey unless it's really needed]]></title>
    <link href="http://petersteinberger.com/blog/2012/dont-call-willchangevalueforkey/"/>
    <updated>2012-04-05T15:27:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/dont-call-willchangevalueforkey</id>
    <content type="html"><![CDATA[<p>You&#8217;re using KVO, right? So you most likely already have written code like this:</p>

<div><script src='https://gist.github.com/2314685.js'></script>
<noscript><pre><code>- (void)setActivityCount:(NSInteger)activityCount {
    [self willChangeValueForKey:@&quot;activityCount&quot;];
    _activityCount = MAX(activityCount, 0);
    [self didChangeValueForKey:@&quot;activityCount&quot;];

    // some more yada yada
}</code></pre></noscript></div>


<p>Carefully encapsulating your calls within a willChangeValueForKey/didChangeValueForKey call to &#8220;enable&#8221; KVO.
Well, turns out, you just did <a href="http://files.sharenator.com/shit_Engineer_explains_why_lightsabers_WOULDNT_work-s600x477-89574-580.jpg">shit work</a>. There&#8217;s no reason to do the will/did dance, as long as you are using <em>a setter method</em> to change the ivar. It doesn&#8217;t need to be backed by an ivar or declared as a property. KVO was written long before there was anything like @property in Objective-C.</p>

<p>In fact, I am now writing iOS apps for about 4 years and didn&#8217;t know this. A <a href="https://github.com/mattt/TTTAttributedLabel/blob/d09777b2875381d660d1a183c0cb41b7f1068a32/TTTAttributedLabel.m#L226">lot</a>. <a href="https://github.com/quamen/noise/blob/2021a1e9348ee9bb9c17b42f32f498d569b22d5e/Message.m#L20">of</a>. <a href="https://github.com/artifacts/microcosm/blob/a5adb56469aad80897f3496d71b150b6f3cbbcd7/TextureAtlas.m#L63">open</a>. <a href="https://github.com/blommegard/HSPlayer/blob/6f4bb5215dd1f30a71d3fcdba46e3a7bcf3a84d1/HSPlayer/HSPlayerView.m#L278">source</a>. <a href="https://github.com/abrahamvegh/AVWebViewController/blob/f24720b414106ecb7bcd4a0ad5f7c6e34a1f2c8f/AVWebViewController.m#L64">code</a>. also gets this wrong. Apple has some good KVO documentation, where they explain the concept of <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-BAJEAIEE">Automatic Change Notifications</a>.</p>

<p>There is a reason why you want to manually add willChangeValueForKey, most likely <a href="https://github.com/BigZaphod/Chameleon/blob/d8a6d6c680abe4609ddad7b24f154f0166e486fa/UIKit/Classes/UIView.m#L224">changing a variable also affects another variables</a>. The most popular example is NSOperation:</p>

<div><script src='https://gist.github.com/2314833.js'></script>
<noscript><pre><code>- (void) start {
    Log(@&quot;Starting %@&quot;,self);
    [self willChangeValueForKey: @&quot;isExecuting&quot;];
    _isExecuting = YES;
    [self didChangeValueForKey: @&quot;isExecuting&quot;];
}

- (void) finish {
    [self willChangeValueForKey: @&quot;isExecuting&quot;];
    [self willChangeValueForKey: @&quot;isFinished&quot;];
    _isExecuting = NO;
    _isFinished = YES;
    [self didChangeValueForKey: @&quot;isFinished&quot;];
    [self didChangeValueForKey: @&quot;isExecuting&quot;];
}</code></pre></noscript></div>


<p>Sometimes you also might wanna optimize how often you&#8217;re sending KVO notifications with overriding <a href="http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html#//apple_ref/occ/clm/NSObject/automaticallyNotifiesObserversForKey:">automaticallyNotifiesObserversForKey:</a> to disable automatic change notifications for certain properties.</p>

<p>In <a href="https://github.com/keremk/CViPhoneLibrary/blob/a845c169916c0dea05680773b10e85f8020ae700/CVLibrary/CVImage.m#L27">this example</a>, there might be expensive KVO observations when the image changes, so we wanna make damn sure that KVO is only fired if the image actually is a different one to the already set image:</p>

<div><script src='https://gist.github.com/2314760.js'></script>
<noscript><pre><code>// snippet via https://github.com/keremk/CViPhoneLibrary/blob/a845c169916c0dea05680773b10e85f8020ae700/CVLibrary/CVImage.m#L27

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
    BOOL automatic = NO;
 
    if ([theKey isEqualToString:@&quot;image&quot;]) {
        automatic = NO;
    } else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}

- (void) setImage:(UIImage *) image {
    if (image_ != image) {
        [self willChangeValueForKey:@&quot;image&quot;];
        previousMemorySize_ = [self memorySize];
        [image_ release];
        image_ = [image retain];
        [self didChangeValueForKey:@&quot;image&quot;];
    }
}</code></pre></noscript></div>


<p>If you currently have such obsolete calls, they&#8217;re not doing any harm. Incrementally called willChangeValueForKey doesn&#8217;t emit more notifications. Still, time to delete some code!</p>

<p><strong>Update:</strong> Don&#8217;t forget that there are more ways that&#8217;ll save you manual calls to will/did, like using the little known addition <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVODependentKeys.html#//apple_ref/doc/uid/20002179-BAJEAIEE">keyPathsForValuesAffecting(PropertyName)</a>, which uses some runtime magic to make KVO smarter. Here&#8217;s a real-life example how I used that on AFNetworking, so people can register a KVO notification on &#8220;isNetworkActivityIndicatorVisible&#8221; and it&#8217;ll get sent every time activityCount is changed. (You&#8217;ll also see that I do some <a href="http://www.mikeash.com/pyblog/friday-qa-2011-03-04-a-tour-of-osatomic.html">atomic updating</a> that requires manual KVO.)</p>

<div><script src='https://gist.github.com/2322095.js'></script>
<noscript><pre><code>- (BOOL)isNetworkActivityIndicatorVisible {
    return _activityCount &gt; 0;
}

- (void)incrementActivityCount {
    [self willChangeValueForKey:@&quot;activityCount&quot;];
    OSAtomicIncrement32((int32_t*)&amp;_activityCount);
    [self didChangeValueForKey:@&quot;activityCount&quot;];
}

- (void)decrementActivityCount {
    [self willChangeValueForKey:@&quot;activityCount&quot;];
    OSAtomicDecrement32((int32_t*)&amp;_activityCount);
    [self didChangeValueForKey:@&quot;activityCount&quot;];
}

+ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible {
    return [NSSet setWithObject:@&quot;activityCount&quot;];
}</code></pre></noscript></div>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Reboot]]></title>
    <link href="http://petersteinberger.com/blog/2012/reboot/"/>
    <updated>2012-04-05T00:35:00+01:00</updated>
    <id>http://petersteinberger.com/blog/2012/reboot</id>
    <content type="html"><![CDATA[<p>I&#8217;ve rebooted my blog. Will add random bits about iOS development and some personal thoughts, especially since some of my friends complained that apparently <a href="http://twitter.com/amyhoy/status/185733715874422784">I tweet too much</a> (<em>cough</em> Amy <em>cough</em> :)
And they&#8217;re right. <a href="http://favstar.fm/users/steipete">I love twitter</a>, but sometimes you just need more than 140 characters.</p>

<p>The old content has been moved into <a href="http://inception-explained.com/">limbo</a>. Most of it would have needed some update to be valid again, so you&#8217;re not missing a lot. So if you&#8217;re coming from Google and miss a particular thing, hit me up on Twitter and I might dig it up for you.</p>
]]></content>
  </entry>
  
</feed>
