A few months ago, Apple removed my app Interval RunCalc from the App Store for being too old and not updated. In this series of articles I’ll document what I did on rebuilding and improving the app. I fixed a bunch of bugs in the last two installments, with one major exception: Landscape is hard to read. My solution makes landscape look like the iPad version. In this lesson I’ll set up those layouts. I’ll also add one more thing to the iPad version that Apple calls good style: popovers for the pickers.
After fixing all those bugs, we still have a pretty cramped landscape version on the phone.
My solution is two columns, one for the time, distance and pace and one for the intervals. I’ll set up both using auto layout, and then change font sizes based on the size classes.
For those not familiar with size classes, or those who still get them confused let’s deal with them with a rule of thumb. There are two sizes: compact and regular. By looking at the horizontal size class, which is the width of the superview, you can distinguish most devices.
As a rule of thumb for the full screen size, compact width is an iPhone and regular width is an iPad. The exception is an iPhone plus in landscape which has a regular width. All phones are compact height in landscape, and iPads are always regular height. If you want all phones including the plus to act like a phone, use the compact height. If you want the iPhone plus to act like a iPad in landscape, use the regular width. Xcode 8 made this all a bit easier by using icons.
For my app, I don’t have problem with the iPhone plus, since I’m making iPhone landscape and iPads looks alike. To handle some font issues, I’ll handle the plus as a phone, and work with the compact vertical size class.
I select the iPhoneSE in landscape and vary for traits in the vertical size class. I’m using the smallest layout to make sure everything fits. I move around a few things with drag and drop.
This is basically my new view, with the main stats on the left and the table on the right. I’ll just clean it up a bit.
Unfortunately, that messes up my portrait. Everything there messes up. For some reason, varying the constraints for class sizes doesn’t seems to work even when it is supposed to. I can’t move between a side by side arrangement and a top bottom arrangement depending on the size class. There’s another way to do this by having the interval and entry views as part of an embedded stack view. Then I just need to rotate the stack view. I’ll put all three Stats in a Vertical stack view and then that stack voew and the tableview for the intervals into another stack view.
All of landscape and all iPad orientations uses a side to side view. Only compact width uses the top to bottom version, so I redesign everything to make iPhone portrait the exception. The basic stack view Axis attribute will be Horizontal. This is an important rule with auto layout and size classes: Design for the most common layout first thenwork on variations. I’ll have even sized spaces for the entry and interval. This means I can use the Fill Equally distribution in the stack views, and saves a lots work configuring intrinsic sizes for the views. I re-design the application like this for the iPad:
For the stack view’s Axis, I make a class size variation by clicking the plus next to the attribute and selecting Width Compact Height Regular from the selection
. For the compact width, regular hight devices, which are iPhones in portrait and some split panes, I’ll Change the axis to vertical
That works a lot better. The iPhone 7 Plus simulator shows me this:
When using modal views like the picker controller on an iPad, Apple highly recommends using popovers instead of full-screen modals. The difference is only three lines of code for each presentation.
//presenting for a popover vc.modalPresentationStyle = .popover present(vc, animated: true, completion: nil) vc.popoverPresentationController?.sourceView = sender.superView vc.popoverPresentationController?.sourceRect = sender.frame
I set my modal presentation style to popover. Immediatey after launching the popover, a popoverpresentationcontroller is created. IN that controller, I anchor the popover to the button in the superview.
The great thing about popovers since iOS 8 is they decide by class size and device, using that same autolayout rule of thumb what to present on their own. This same code, placed in all my buttons, will give me different results for different devices. On an iPad I’ll get the popover:
In iPhones in landscape and portrait, I’ll get a full sheet
On iPhone plus in landscape, I’ll get a formsheet
The app is working on all devices, and the user interface is usable on all devices. Sticking it on my iPad and iPhone, I play with it to make sure everything works and everything feels good.
I’ve gotten to one of the most dangerous points in any app development. Most of the bugs are fixed, but I can see a bunch of features I could add to create a more functional app. I can get stuck adding features and not shipping or shipping.
This was to replace a badly performing app that is out of the App Store right now. Ship first, fix later. The app was thrown out of the store because I didn’t update it regularly. All I need to do is keep updating and this won’t happen again.
So I should submit the app. But… Xcode 9 GM seed or production ships in less than a week. So instead of shipping, in our next installment, we have one last round of updates, which will prove if the changes we made make sense.