A few AutoLayout and general UIKit tips & tricks
Tags: ios, swift
Published: 2020-05-25 17:57:52

A collection of tips that I found very helpful when building UI.

### Use width + aspect ratio constraints instead of width + height

This one applies mainly to square elements like buttons with just icons or images. If you set either width or height constraint and 1:1 aspect ratio, you can easily experiment with different sizes by changing just the width or height.

## Create constraints relative to other elements rather than a container

_I hope I will be able to explain this one right :-)_

Let's say you are building fairly standard table view cell that has title label, then maybe some subtitle and/or description and possibly another component to show timestamp or tag. In this case I would pick title label to be sort of the dominant component and set its leading constraint to maybe 15 from the left edge, same for the top one.

What about the subtitle below? You can also constrain it to the leading edge but if you constrain it to the leading edge of the title label you will have much easier time modifying the layout later on.

If your subtitle, description and timestamp labels have leading constraint to the title label you can just change the leading constraint of the title to say 20 instead of previous 15 and all the other components will automatically align and you don't have to modify their leading constraints one by one to the new value.

### Auto-sizing table view header and footer views

TableView is such a versatile and useful component and thanks to its header and footer views you can build a lot of screens just with TableViews.

To be clear I am talking about table view header and footer not those for individual sections. You can drag them in storyboard or assign in code.
There is a big downside and that is even if you have correct AutoLayout code they won't have correct size at runtime.

Instead there is a few lines of code you need to add to your project.

```swift
override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        if let footer = tableView.tableFooterView {
            let newSize = footer.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            footer.frame.size.height = newSize.height
        }
        
        if let header = tableView.tableHeaderView {
            let newSize = header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            header.frame.size.height = newSize.height
        }
}
```
This will make the header and footer views behave correctly.

I would recommend creating something like `BaseTableViewController` and putting this code there so you don't have to duplicate it in each View Controller.

If you are not using subclasses of `UITableViewController` then I would consider creating protocol that requires `tableView` property and has method to do this auto-sizing.

### Always correct rounded corners

This one is really small but I think also really useful.

So we are all used to creating rounded corners like this `layer.cornerRadius = 10`.

But say you want to have perfect circle. Then you can do something like this:

```swift
button.layer.cornerRadius = button.bounds.height/2
```

This works great assuming the button stays the same size. 

If you want to have view that has always perfect circular rounded corners, I think the best way is to create subclass and override `layoutSubviews()` method:

```swift
override func layoutSubviews() {
        super.layoutSubviews()
        
        layer.cornerRadius = bounds.height / 2
}
```

This way your rounded corners will keep looking great no matter the resizing.

### Do not use `UITableViewController`

/Note: Same applies to `UICollectionViewController`./

`UITableViewController` is very useful because you can get started much quicker with `UITableView`. You don't need to drag it to Storyboard (or create in code), setup constraints and also the dataSource and delegate.
These are the benefits that will save you some minutes when creating new screen for your app.

**But there are important downsides.**

If you decide later on that say you don't want your `UITableView` to cover the whole screen, you can't. There is no simple way.

Or maybe you decide to use `UICollectionView` which will look like `UITableView` on iPhone but will much better use space on iPads. Once again, you can't.

Also image that you need some sort of custom base view controller that will have common functionality for multiple screens. So you create it as a subclass of `UIViewController` or as a subclass of `UITableViewController` but then all the subclasses automatically must become subclasses of `UITableViewController` in this hypothetical case.

There is no way around it. You could try protocols of course but some things just require `UIViewController` subclass.

So in a nutshell using `UITableViewController` costs a **lot** of flexibility for a few spared minutes.