The reviewers at the Apple iTunes App Store have approved the revised version of the EP Mobile app. For information on why the app needed to be revised, see my earlier posts on the subject. I removed the drug dose calculators (note though that the Warfarin Clinic module was not removed), but added detailed drug dosing information and a creatinine clearance calculator that can be used while viewing the dosing information. Other improvements to the app have been made as well. The changelog is as follows:
Changes from version 3.5
* Removed drug dose calculators as requested by Apple (see developers guide section 22.9)
* Added new drug reference section with creatinine clearance toolbar
* Added creatinine clearance calculator
* Added right ventricular hypertrophy criteria
* Added D'Avila WPW accessory pathway location algorithm
I will release the new version in 2 days (March 22). If for some reason you can’t live without the drug dose calculators, then don’t update the app. Turn autoupdate off if it is on to prevent inadvertently updating the app. Regardless of the lack of drug dose calculators, I encourage most people to update the app, because I think that using the new drug reference information in the app is a better way to determine drug dosages, and because I will continue to add new features to the app, which will not be available to those who do not update.
Note that Android users of EP Mobile still have access to the drug dose calculators, as well as the new features noted above.
I hope that at some point Apple changes its policy and adds physicians to the groups permitted to write apps that calculate drug doses. After all, it’s what we do.
Countdown to version 3.6 release — Done! Released Mar 22, 2015!
My initial experiences with Apple’s iOS AutoLayout were pretty negative. Using Interface Builder’s (IB) ability to generate AutoLayout constraints automatically based on the positioning of views turned out to be frustrating, as it would generate constraints that were incompatible with iOS 7. As iOS 8 has only been out for a few weeks, I definitely want to keep supporting iOS 7 in my app. But Xcode 6 generates these incompatible constraints anyway, even though the deployment target is iOS 7. Furthermore the automatically generated constraints don’t really do what I want, such as keeping views centered on the screen when the screen enlarges from iPhone size to iPad size. So I was forced to go back to the drawing board and really try to understand how AutoLayout works.
My impetus for all this was my desire to upgrade one of my apps from an iPhone only app to a Universal app — optimized for display on both the iPhone and iPad. The app (EP Mobile) has a big storyboard and many different views. By using AutoLayout I hoped to avoid having two different storyboards, one for iPhone and another for iPad, and just use one storyboard for both devices. I decided to check the Use Size Classes option in IB. Supposedly this allows for designing separate layouts in a single storyboard for different size devices. As it turns out, this was not helpful, as apparently this feature only works on pure iOS 8 apps. Moreover, as the compiler seems to generate code for each possible device, building your app after making a change in the storyboard takes much longer than it did before enabling this option. After a while I grew tired of this and decided to turn off Size Classes. However trying to do this resulted in a warning dialog from Xcode that stated a lot of nasty things that might happen (I hate dialogs that use the word “irreversible”) and so I decided to just live with the longer build times.
I watched some YouTube videos on AutoLayout that were helpful (here and here), but in truth the best way to learn AutoLayout is to play around with it. Take a view, clear any constraints that are there, put the subviews where you want them, and then add your own constraints manually. While doing this, ignore warning messages from Xcode about ambiguous constraints and misplaced views. Ignore the yellow and red lines that show up on the screen indicating these errors. Until you have completely specified all the constraints needed to determine unambiguously the location of the subviews without conflicts, these warnings will show up. Prematurely asking IB to Update Frames before all the constraints are specified will make the subviews jump around or disappear. Unfortunately even when all constraints are specified and correct, the yellow warnings don’t go away. IB is not capable of automatically applying your constraints and misplaces your controls in your views whenever you change constraints. Sometimes it misplaces controls even when you are just changing the storyboard metrics from one size to another. Update All Frames then puts everything where it belongs.
One way to start is by putting constraints on heights and widths of controls that you don’t want to resize when the screen size changes or the device rotates. Note that some controls, such as buttons, have an intrinsic size based on the button label, and it is not always necessary to add specific constraints to these controls. However, it looks to me like the system will ignore the intrinsic size at times, especially if you are trying to do something fancy with constraints, and your button will grow to a ridiculous size to satisfy your constraints. So it doesn’t hurt to specify width and height constraints manually even in these controls. Of course if you want controls to expand in one direction or another, don’t specify a constraint in that direction.
Next step is to align controls that are lined up horizontally. You can select multiple controls and then align their vertical centers using Editor | Align | Vertical Centers on the menu. If there are rows of controls like this you can take the leftmost control and working from the top to bottom, pin each row to the row above (or to the superview for the first row) to make sure there is vertical separation between the rows. Finally, usually you want your controls to be centered on the screen, even when using different screen sizes and with rotation. If you have one wide control, such as a segmented control or a large text field, you can horizontally center that control. You can then pin the leading edge of that control to the leading edge of the superview (i.e. the window) and that view will grow as the screen width increases. Aligning the leading edges and trailing edges of the other controls to this view will allow the whole set of controls and expand and contract with the width of the screen. If you have rows of controls, you may still need to put constraints between individual controls to control the horizontal distance between them.
One issue I noticed was that, while it’s nice to have controls expand to fill the screen of the iPhone when going from the small iPhone 4s to the 5.5 inch iPhone 6, sometimes the controls get too wide when viewed in landscape mode or on the iPad if they are just pinned to the superview leading edge. For example, this segmented control is centered horizontally and vertically in the superview, and the leading edge is pinned to the superview leading edge.
On the iPhone 4s, with rotation the view remains centered and enlarges when the device is rotated.
To show the flexibility of AutoLayout, we can limit the expansion of the segmented control to a maximum we select, by making the width less than or equal to a value (in this case 350) and lowering the priority of the pinning of the leading edge to the superview. This achieves the desired effect.
You can do a lot with AutoLayout just using IB if you are patient and try out various effects. You can do more by attaching your constraints to outlets and manipulating them in code. It is unfortunate that some glitches in the implementation of AutoLayout in Xcode 6 Interface Builder make using AutoLayout more frustrating than it needs to be. To those who are discouraged like I was by AutoLayout, I urge you to keep experimenting with it. The a-ha moment will come and it will be worth it.
The new larger iPhones and iOS 8 are here. Xcode 6, upgraded to deal with these new beasts, is also ready for download. Anyone who has written apps designed for the iPhone has to make sure their apps run on these new devices and the new iOS. Previous iPhones had two different heights (3.5 and 4 inches) but were the same width. The new iPhones are not only longer, but are wider. With the previous devices, it was relatively easy to design for the slightly different heights. Now the developer has to deal with layouts that work with many different aspect ratios. If your app is a universal app (i.e. runs on iPhone and iPad) there are even more sizes that you must deal with.
Anticipating this, Apple, a few versions back, introduced AutoLayout to take the place of their older layout system using springs and struts. With AutoLayout, you place your widgets where you want and, automagically, they retain their relationship with different screen sizes or when rotating the screen. Or so it’s supposed to work in theory.
As it turns out, AutoLayout introduces its own set of headaches which rank up there with your worst migraines. Let’s begin.
If you have AutoLayout turned on (which is the default now), any prior layouts you have, whether you used springs and struts or absolute layouts, will be converted to the AutoLayout system. Any new layouts will use the AutoLayout system automatically. When placing widgets, the dotted blue guidelines will have more meaning than they did before. For example, if you center a widget using the blue lines, that widget will appear centered with different device sizes or with rotation. This is called intrinsic AutoLayout, and the effect is added at run-time. That means, somewhat surprisingly, that when you change device size using Interface Builder (IB), the layout won’t look right.
But if you run the simulator, the layout will look fine.
Hopefully that will be true when running on the new hardware itself, but since I don’t have an iPhone 6 I can’t test this. The moral of this story is don’t start fiddling with your layout because it looks weird on IB. Run your app on the simulator at each size (and on hardware if you have it). Most of your layouts will look OK on the larger iPhones due to intrinsic AutoLayout.
If you need to alter what is produced by intrinsic AutoLayout, you have to manually add constraints. Constraints are a rigorous description of a relation between two views. Examples include centering a widget in a superview, fixing a widget’s height, but allowing width to change, and setting a fixed distance between two widgets. Constraints are actually pretty cool in theory and can be added in code. But using IB, once you start adding constraints all hell breaks loose.
Suppose everything about your view looks good, but you want the bottom of your widget to be pinned to the bottom of the view it is contained in. So you add that one constraint. With that one action you have blown away the intrinsic AutoLayout system and IB will inform you that you have all sorts of ambiguous heights and widths. So if you add any constraints in IB, you have to add constraints to all the widgets. There is a menu item to do this, and suddently there are about 50 constraints added to your view. And here you hit your first surprise. Even though you are targeting your app for iOS 7, Xcode adds constraints that are incompatible with iOS 7!
Constraints can’t be relative to the superview margin in iOS 7 but IB blithely adds them anyway. So you go through the constraints, one by one, edit the ones that have “relative to margin” checked, which ends up changing the appearance of your view. It is then that you discover that changing a constraint tends to mess up your view or mess up the other constraints. You often get a message saying that the view at runtime will not appear the same as it appears on IB. I found that pressing Option-Command+= fixes that. It is easy to get frustrated when editing constraints in IB. The “Clear Constraints” menu item is your friend here, so you can start all over.
AutoLayout and ScrollViews
It gets worse. If you have any ScrollViews in your app, with AutoLayout they appear to work on the simulator, but they don’t work on actual hardware! Turning off AutoLayout fixes this, but DON’T DO THIS! You cannot turn off AutoLayout for a single view. It goes off for your whole storyboard. And your carefully laid out and tweaked constraints disappear. Version control is your friend here. Using AutoLayout and ScrollViews is complicated, as you can see from this. After reading this and playing around, I was able to get my views to scroll by pinning the ScrollView to the superview on all four sides. Interestingly, when setting up a ScrollView with AutoLayout, you no longer have to add any code about the ScrollView as was necessary before (you would have to add the content size of the ScrollView for it to work). But again, this is a real trap, especially as if you do it wrong, it looks like it is working on the simulator but won’t work on an actual device.
Handling resizing and rotation is one thing that is a lot easier when developing for Android than for Apple iOS. AutoLayout has a steep learning curve, and others have had these same problems (search for AutoLayout on StackOverflow, or see this. I hope that Apple can improve this experience. I like the concept of AutoLayout, but the devil is in the details.
Update: Since writing the above, I have gone back to basics with AutoLayout and made a concentrated effort to understand the system. I don’t think the AutoLayout system is bad; however, using AutoLayout in Interface Builder can be rough going for the reasons given above. AutoLayout itself though is very flexible and I do think you can do just about anything you want with it if you understand it well. I am trying to achieve this level of understanding. I have found that the best way to learn is to play around with a view, adding constraints and seeing what happens. I have also discovered the new feature of Size Classes in Interface Builder in Xcode 6 which makes developing a Universal App using a single storyboard much easier. So I’ll keep plugging away until I have conquered this beast.