Best Practice for laying out UI Programmatically for both iPhone and iPad

Your question, as-is, cannot be answered...

Wha does your app do? If it's a photo slide-show, constrain an imageView to the full view and set its content mode to scale-fit. Voila! It "looks good" on all devices!

If your app is more complex than that, you will likely need to make use of all the types of constraints: elements relative to each other; equal to each other; relative/equal with constant adjustments; relative/equal with multiplier adjustments; etc. And you may want different layouts (not just different sizing) based on device+orientation, in which case you'll also want to take advantage of size-class-variations.

In addition, getting an app to "look right on every device" involves much, much, MUCH more than applying constraints.

  • Should the app use a Tab Bar?
  • A Navigation Bar?
  • A combination of them?
  • Neither?
  • Should it use text-buttons or image-buttons?
  • Should it adjust for accessibility and dynamic fonts?
  • Might it even have different functionality when running on a small screen vs a large screen?

In general, your first step should be hand-drawing every screen and UI element you expect to have - including the activity "flow" - with variations for sizes and orientations, so you are designing the best UI/UX from the beginning. At that point, you begin actual UI construction.

Keep in mind, there are people who can't produce a "Hellow World" app but make a very good living as "App Designers."

Having said all that, though... one approach you almost certainly should not take is:

view.widthAnchor.constraint(equalToConstant: 20.scale())

EDIT

Here is a quick example - based on this article: https://www.raywenderlich.com/1343912-adaptive-layout-tutorial-in-ios-12-getting-started

A simple weather app - constraints set so it looks similar on all iPhone and iPad models:

enter image description here

enter image description here

But, what happens when you rotate the phone?

enter image description here

Little, tiny cloud doesn't look so good. Add trait-variations / size-classes, and we can get:

enter image description here


And, to try and answer your edited question of: "Is it bad practice to just check if the device is an iPad..."?

Yes. It's recommended to design for trait-variations / size-classes so your app will look the way you want in all configurations (hopefully future-proof for the next device that comes out as well).


Is it bad practice to just check if the device is an iPad, and if it is, have constraints just for iPad, and, if not, have constraints for just iPhone.

If you're planning for Split-View support, that is definitely a no-go. The code for achieving and handling this is always going to be slightly verbose. There are no one-liners here to achieve what you want.

In your -[UIViewController viewDidLoad] method, you'd want to setup your views with the base trait collection available to the controller's view. This is your starting point.

As and when your App's windows are resized by the OS (imagine going for a fullscreen app in landscape mode to a split-sized app, occupying one-third of the screen): the -[UIViewController traitCollectionDidChange:] method is called on your View Controller. This is where you update your layout constraints.

You can, and should, encapsulate your layout logic in a single method and call it from -[UIViewController viewDidLoad] as well as -[UIViewController traitCollectionDidChange:].

In such a method, I do not recommend checking if the host device is an iPad. You specifically want to look at the active trait collection's horizontalSizeClass & verticalSizeClass properties to determine the values for your layout constraints.

For further information, I suggest you go through the following documentation:
1. https://developer.apple.com/documentation/uikit/uitraitcollection?language=objc
2. https://developer.apple.com/documentation/uikit/uitraitenvironment/1623516-traitcollectiondidchange?language=objc

The 2nd link has a simple example on how to check if you need to update your constraints.