<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jeremy Koch</title>
    <description>A place for code, design, and any other random thoughts.</description>
    <link>https://jerkoch.com/</link>
    <atom:link href="https://jerkoch.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 23 Oct 2018 20:06:49 -0700</pubDate>
    <lastBuildDate>Tue, 23 Oct 2018 20:06:49 -0700</lastBuildDate>
    <generator>Jekyll v3.7.4</generator>
    
      <item>
        <title>Swiper, No Swiping Right on a UITableViewCell</title>
        <description>&lt;p&gt;Yes, I know I’m late to the cell swiping party. You know what they say: necessity is the mother of invention.&lt;/p&gt;

&lt;h4 id=&quot;backstory&quot;&gt;Backstory&lt;/h4&gt;

&lt;p&gt;My journey started innocently enough. I simply wanted to add a single swipe action to the left side of a &lt;code class=&quot;highlighter-rouge&quot;&gt;UITableViewCell&lt;/code&gt;, but a harsh reality quickly set in.&lt;/p&gt;

&lt;p&gt;If you have dreams of enabling &lt;em&gt;Mail.app&lt;/em&gt;-like gesture features with only a few lines of code, you will quickly be disappointed to learn that the existing &lt;code class=&quot;highlighter-rouge&quot;&gt;UITableViewRowAction&lt;/code&gt; class is very limited. You can change the text and background color for an action, but no icons, no swiping right, and no dragging to perform destructive or selection type behavior.&lt;/p&gt;

&lt;p&gt;No problem, I figured a quick search on Stack Overflow would help brighten my day. That led me to &lt;a href=&quot;https://github.com/MortimerGoro/MGSwipeTableCell&quot;&gt;MGSwipeTableCell&lt;/a&gt; and &lt;a href=&quot;https://github.com/CEWendel/SWTableViewCell&quot;&gt;SWTableViewCell&lt;/a&gt;. Both have great APIs, are very popular, and provide many customizations to make most developers happy.  I decided to take &lt;em&gt;MGSwipeTableCell&lt;/em&gt; for a spin.&lt;/p&gt;

&lt;p&gt;One thing you need to know about me - I’m a stickler for the details. Visually, &lt;em&gt;MGSwipeTableCell&lt;/em&gt; got me what I needed, but the physics just didn’t feel right. It was only &lt;em&gt;after&lt;/em&gt; I ported it to Swift (yes, you read that right) that I realized its animation timings were all fixed and did not take into account things such as gesture velocity. Looking back at the &lt;em&gt;SWTableViewCell&lt;/em&gt; demo app also left me unsatisfied.&lt;/p&gt;

&lt;p&gt;It was then that I took a step back and accepted the fact that I was about to roll my own swipeable &lt;code class=&quot;highlighter-rouge&quot;&gt;UITableViewCell&lt;/code&gt;. And of course, I would write it in Swift. I spent a long time analyzing the iOS 10 Mail.app swiping behavior - slow motion screen recordings and all.  I also looked at the News.app, which has similar swipeable cell behavior, although it uses a &lt;code class=&quot;highlighter-rouge&quot;&gt;UICollectionView&lt;/code&gt;. Either way, they are both very similar, with some slight differences in transition animations and the highlighting style when tapping action buttons.&lt;/p&gt;

&lt;p&gt;I wanted to write something that would not only provide a great user experience, but also provide an equally as great developer experience. And the same principles applied: clarity, consistency, and delight. &lt;em&gt;Let’s put the discoverability debate aside for a moment :)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to support both the Mail.app and News.app cell swiping styles, my three main objectives were:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Left and Right swiping support with configurable transitions as the actions are exposed&lt;/li&gt;
  &lt;li&gt;Draggable cells past a threshold triggering auto-expansion of the default action&lt;/li&gt;
  &lt;li&gt;Flexible action button styling (text attributes, support images, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My first implementation used &lt;code class=&quot;highlighter-rouge&quot;&gt;UIScrollView&lt;/code&gt;. It seemed like the obvious choice.  But it wasn’t long before the technical debt started to accrue. This came in the form of hacks trying to handle dragging past thresholds and handling the extents of the scroll view’s &lt;code class=&quot;highlighter-rouge&quot;&gt;contentView&lt;/code&gt;, among many other things. More importantly, however, the physics still didn’t feel the same as my two benchmark apps - and that’s what I cared about most.&lt;/p&gt;

&lt;p&gt;I’m not sure what this says about my developer-chops, but I’m constantly trying to write as little code as possible. Usually, spending way more time searching for the &lt;em&gt;right&lt;/em&gt; way rather than the &lt;em&gt;quickest&lt;/em&gt; way.  I justify it as an investment in any future code that I write.&lt;/p&gt;

&lt;p&gt;I had never really gotten my hands dirty with &lt;code class=&quot;highlighter-rouge&quot;&gt;UIPanGestureRecognizer&lt;/code&gt;.  I knew that throwing out &lt;code class=&quot;highlighter-rouge&quot;&gt;UIScrollView&lt;/code&gt; meant giving up all its cozy abstractions, and that would put me one more layer out of my comfort zone. But with great risk comes great reward!&lt;/p&gt;

&lt;p&gt;The next few days were a bit of struggle. I kept at it and managed to get the basics working. I was definitely getting closer. It was when I stumbled back upon &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2014/236/&quot;&gt;Building Interruptible and Responsive Interactions&lt;/a&gt; from WWDC 2014 that I knew I was on the right track. Seamlessly transitioning from gestures to animations was the key achieving the consistency with the Mail.app that I was looking for. The code quickly began to flow.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/jerkoch/SwipeCellKit/develop/Screenshots/Expansion-Selection.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After the animations were implemented using the shiny new &lt;code class=&quot;highlighter-rouge&quot;&gt;UIViewPropertyAnimator&lt;/code&gt; class, I spent a lot of time ensuring that the elasticity felt accurate when dragging beyond allowable thresholds. I also made sure that the drag thresholds for selection and destruction behaviors matched the Mail.app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/jerkoch/SwipeCellKit/develop/Screenshots/Transition-Border.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For transitions, I started with the Mail.app &lt;em&gt;border&lt;/em&gt; style. Once that was working, I added support for a &lt;em&gt;reveal&lt;/em&gt; style transition like the News.app uses, and then added a &lt;em&gt;drag&lt;/em&gt; style for good measure.&lt;/p&gt;

&lt;p&gt;After adding some final polish by integrating Apple’s new Haptic Feedback API, it was time to call it a day and send my creation out into the world.&lt;/p&gt;

&lt;h4 id=&quot;the-final-product&quot;&gt;The Final Product&lt;/h4&gt;

&lt;p&gt;Introducing &lt;a href=&quot;https://github.com/jerkoch/SwipeCellKit&quot;&gt;SwipeCellKit&lt;/a&gt;. A swipeable UITableViewCell based on the stock Mail.app, implemented in Swift.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/jerkoch/SwipeCellKit/develop/Screenshots/Hero.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A swipeable UITableViewCell with support for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Left and right swipe actions&lt;/li&gt;
  &lt;li&gt;Action buttons with: &lt;em&gt;text only, text + image, image only&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Haptic Feedback&lt;/li&gt;
  &lt;li&gt;Customizable transitions: &lt;em&gt;Border, Drag, and Reveal&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Animated expansion when dragging past threshold&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Integration is simple. Set the &lt;code class=&quot;highlighter-rouge&quot;&gt;delegate&lt;/code&gt; property on &lt;code class=&quot;highlighter-rouge&quot;&gt;SwipeTableViewCell&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cellForRowAt&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UITableViewCell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then adopt the &lt;code class=&quot;highlighter-rouge&quot;&gt;SwipeTableViewCellDelegate&lt;/code&gt; protocol:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;editActionsForRowAt&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;orientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwipeActionsOrientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;SwipeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orientation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;deleteAction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwipeAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destructive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// handle action by updating model with deletion&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// customize the action appearance&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;deleteAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deleteAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Optionally, you call implement the &lt;code class=&quot;highlighter-rouge&quot;&gt;editActionsOptionsForRowAt&lt;/code&gt; method to customize the behavior of the swipe actions:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;editActionsOptionsForRowAt&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;orientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwipeActionsOrientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwipeTableOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwipeTableOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expansionStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destructive&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transitionStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;border&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;So there you have it!  A few years too late, but hopefully &lt;em&gt;SwipeCellKit&lt;/em&gt; is still valuable to some people out there.&lt;/p&gt;

&lt;p&gt;All source code for &lt;em&gt;SwipeCellKit&lt;/em&gt; is available on &lt;a href=&quot;https://github.com/jerkoch/SwipeCellKit&quot;&gt;GitHub&lt;/a&gt;. It includes a &lt;a href=&quot;https://github.com/jerkoch/SwipeCellKit/tree/develop/Example&quot;&gt;Demo&lt;/a&gt; app which shamelessly copies the Mail.app &lt;em&gt;Inbox&lt;/em&gt; interface.&lt;/p&gt;

&lt;p&gt;You can read the full &lt;a href=&quot;http://www.jerkoch.com/SwipeCellKit&quot;&gt;documentation&lt;/a&gt; which was generated with &lt;a href=&quot;https://github.com/realm/jazzy&quot;&gt;Jazzy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please try it out! Pull requests welcome.&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Feb 2017 00:00:00 -0800</pubDate>
        <link>https://jerkoch.com/2017/02/07/swiper-no-swiping.html</link>
        <guid isPermaLink="true">https://jerkoch.com/2017/02/07/swiper-no-swiping.html</guid>
        
        
      </item>
    
      <item>
        <title>Welcome</title>
        <description>&lt;p&gt;I finally got around to setting up my development blog.  I’ve been meaning to do this forever, but never got around to it. So why now?  Mainly selfish reasons. These days I’m all Swift, all the time. It’s been really exciting experiencing the evolution of such young language. I’ve learned a ton from a number of great community members, and I want a place to record all the useful tips, tricks, and code snippets that I come across. If that helps anyone else, then great!&lt;/p&gt;

&lt;p&gt;My latest adventure was selecting and setting up my blogging infrastructure. I landed on &lt;a href=&quot;http://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;. Dead simple, static, markdown, and free hosting on GitHub. Done. I found a nice Jeykll port of Ghost’s default theme, appropriately named &lt;a href=&quot;https://github.com/rosario/kasper&quot;&gt;Kasper&lt;/a&gt;.  After a little bit of tweaking, I’m pretty happy with the layout.&lt;/p&gt;

&lt;p&gt;Now I just need to write my first technical post… stay tuned!&lt;/p&gt;

</description>
        <pubDate>Thu, 17 Sep 2015 03:18:00 -0700</pubDate>
        <link>https://jerkoch.com/2015/09/17/welcome.html</link>
        <guid isPermaLink="true">https://jerkoch.com/2015/09/17/welcome.html</guid>
        
        
      </item>
    
  </channel>
</rss>