Ogre Codes

Follow @ogre_codes to get notified when new articles are posted to this site.

Roll Your Own Split View

Feb 19, 2017 at 7:48 PM

Almost every single tutorial I see which talks about getting started with iOS programming suggests you should write for one form factor, either for the phone or for the iPad (usually for the phone). I get why, it's much easier to build an app for one platform. Much easier, often you wind up doing 2-3 times as much work to get something fairly simple done when you are building for both. Of course—since I'm a glutton for punishment—I like my iPad and I like my phone and I want an app that works on both, so SwiftBlog is a universal app. I spent most of Yesterday making the Instant Preview pane work for the iPad, Today I built the same feature for the phone.

Obviously a different approach is needed for the phone, the screen isn't wide enough for dual panes to make any sense. So my approach was a simple "Preview" button in the upper right which let you instantly switch to see what the page will look like on the site. There were two aspects to this which were a bit tricky, the first was supporting both split view and a separate switching view for the phone, the second was getting a nice smooth animation when switching to the preview mode.

Split View

Apple has a special SplitView container for doing something similar to what I had in mind, but it's specific UITableViews which didn't exactly work for what I wanted. My approach was based on some simple auto-layout logic.

I started by laying out the Stack View with the entry editor (EntryStack) and the Web View side-by-side and pinning the non-joined sides to the adjacent elements. Then I added an equal widths constraint to the two containers and set a constraint which pinned the leading edge of the Web View to the trailing edge of the EntryStack. This was the basic side-by-side view for the iPad.

To set up the iPhone view, I added a size based variation to turn off the constraint that pins the joining edge of the EntryStack to the Web View and added a second size based constraint to the Web View which made it equal widths to the main view when it's on an iPhone.

Using this setup, I was able to set a simple button to toggle between the two views by either raising or lowering the WebView, or toggling it's "hidden" state, but I wanted something a little snazzier than that.

37 Pieces of Flair

The final piece of this was to make the switch on the iPhone a bit more fluid, the instant flip between the two views didn't feel complete, it need a bit of animation. I decided a cross-fade would be a nice way to indicate the transformative nature of the preview.

A quick search made UIView.transitionFromView look promising and while it seemed to do the right thing the first time, I couldn't get it to transition back to the edit view. The solution I came up with was to just animate the alpha of both layers in opposite directions and the result was the fairly smooth animation between the two states. The result looks like this:

func togglePreview() {
  if webView.isHidden {
        RenderPage()
        self.webView.alpha = 0.0
        self.webView.isHidden = false
        UIView.transition(with: webView,
                          duration: 0.33,
                          options: .curveEaseIn,
                          animations: {
                            self.webView.alpha = 1.0
                            self.entryStack.alpha = 0.0
        })
    } else {
        UIView.transition(with: webView,
                          duration: 0.33,
                          options: .curveEaseIn,
                          animations: {
                            self.webView.alpha = 0.0
                            self.entryStack.alpha = 1.0
        }) {_ in self.webView.isHidden = true }
    }
}

The Web View is hidden at the end of the animation to avoid having a transparent view floating over the top of the edit pane.

In retrospect, this all would have been easier and likely a little more consistent with the "Apple Way" if I'd just pushed a new WebView onto the Navigation Stack, and I might still switch to that, but right now it was fun learning how to do the crossfade effectively and it looks quite slick.