Skip to content

Instantly share code, notes, and snippets.

@apsun
Last active January 2, 2021 04:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apsun/6a0f503149eac94e40fdeca904072855 to your computer and use it in GitHub Desktop.
Save apsun/6a0f503149eac94e40fdeca904072855 to your computer and use it in GitHub Desktop.

iOS notes

Just some random stuff I'm learning as I make my first iOS app.

UIViewController: present vs show

present() will always show the view controller modally, whereas with show() it will bubble up the chain where it can be handled by some parent view controller.

For example, if you use UIAlertController, you should use present(), because alert dialogs are modal.

If you are using UINavigationController, use show() to display the view controller for navigation like the settings app (nested menus).

@objc

This generates a trampoline function that allows Objective-C code to call into Swift. This is necessary as Swift uses a different ABI from Objective-C.

@main vs @UIApplicationMain

@UIApplicationMain tells Swift that the class contains the entry point of the program, and that it should instantiate your class and call UIApplicationMain() for you. @main tells Swift that the class contains a function called main() which is the entry point of the program.

You can use either one for iOS apps, as UIApplicationDelegate contains its own main() implementation that just calls UIApplicationMain().

Or if you want to do it yourself, put this into main.swift:

import UIKit

UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv,
    nil,
    NSStringFromClass(AppDelegate.self)
)

iOS app delegate lifecycle

Most of these have Android analogies:

willConnect <--> onCreate

  • you create the views/view controllers here

willEnterForeground <--> onStart

  • app is about to be shown on the screen

didBecomeActive <--> onResume

  • app is top-level and receiving user input

willResignActive <--> onPause

  • app is about to be hidden (e.g. by a modal dialog)

didEnterBackground <--> onStop

  • user pressed the home button/switched apps

didDisconnect <--> onDestroy

  • not sure when you get this

Note that this lifecycle is not strict. As in, it's possible to get some messages twice in a row. Always assume this can happen, and make your lifecycle handlers idempotent.

SF Symbols

Load into images using UIImage(systemName: "<icon name>"). Note that the color of the icon is set by the tint color of the control you put this in, which is by default blue. Do not try to force the "tint" onto the image itself using withTintColor.

Storyboard-free manual UIs (for masochists)

In your scene:willConnextTo:options: method, replace the content with:

if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = YourViewController()
    window.makeKeyAndVisible()
    self.window = window
}

To create views, override loadView() in your view controller, and (this is super important) set self.view to whatever view you want to be the "root" of the window. If you don't need a custom root view, you can skip this; the default loadView() implementation behaves as if you had written self.view = UIView(). Make sure that you WRITE to self.view as the first thing in this function, as reading from self.view with no value set will call loadView() again and wreck your stack.

Code in loadView vs viewDidLoad

If you're creating your views manually (see above), there is no difference. If you're using interface builder, override viewDidLoad().

Parents should resize children

Child views should never set their own layout constraints relative to their superview. Instead, the parent should set constraints on the child view after adding them to the view hierarchy.

translatesAutoresizingMaskIntoConstraints

ALWAYS set this to false if you are using auto layout. Which you should be doing. In other words, ALWAYS set this to false. If you don't, views will mysteriously fail to layout correctly.

More generally, to create UIs that scale well, think really hard every time you write .frame or .bounds. 99% of the time, that code is wrong and will break the moment you rotate your device.

Animating auto layout constraints

UIView.animate can be used to animate auto layout constraints. To make this work, you should (in this order):

  1. Call layoutIfNeeded on the PARENT of the view you want to lay out. It's absolutely critical that you do it on the parent view. If you do it on the view itself, the animation will not work. The reason we do this is to make sure that the view is in the correct position to begin the animation.

  2. Adjust constraints as needed.

  3. Call UIView.animate, and in the animations block, call layoutIfNeeded, again on the PARENT view.

You can also put the constraint adjustments into the animations block before the call to layoutIfNeeded, that has the same effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment