0% found this document useful (0 votes)
0 views52 pages

UIKit Complete Guide

The document is a comprehensive guide to UIKit, covering fundamental concepts such as the differences between UIKit and SwiftUI, the UIViewController lifecycle, view hierarchy, Auto Layout, and common UI controls. It emphasizes the imperative nature of UIKit and provides detailed instructions on managing views, constraints, and user interactions. The guide is structured to help developers transition from SwiftUI to UIKit effectively, with practical examples and best practices throughout.

Uploaded by

asif0171797
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views52 pages

UIKit Complete Guide

The document is a comprehensive guide to UIKit, covering fundamental concepts such as the differences between UIKit and SwiftUI, the UIViewController lifecycle, view hierarchy, Auto Layout, and common UI controls. It emphasizes the imperative nature of UIKit and provides detailed instructions on managing views, constraints, and user interactions. The guide is structured to help developers transition from SwiftUI to UIKit effectively, with practical examples and best practices throughout.

Uploaded by

asif0171797
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

UIKit

Complete Developer Guide


From Zero to Production

Covers UIKit Fundamentals • Auto Layout • Table & Collection Views


Navigation • Delegates • Gestures • Animations • Networking
1. UIKit vs SwiftUI — The Big Picture
As a SwiftUI developer, the most important thing to understand first is the mental model shift. UIKit is
imperative — you tell iOS exactly what to do and when to do it. SwiftUI is declarative — you describe
what you want and let the framework figure out the rest.

SwiftUI (What You Know) UIKit (What You're Learning)


Declarative — describe the UI Imperative — command the UI
State drives the view automatically You manually update UI on state change
Views are structs (value types) Views are UIView subclasses (reference types)
No lifecycle to manage manually Must manage ViewController lifecycle
Layout via modifiers (.frame, .padding) Layout via Auto Layout constraints or frames
Works from iOS 13+ Works from iOS 2+ (all legacy apps)
Less code for simple layouts More control over complex/custom layouts
Previews built-in Build & run to see changes

Why UIKit Still Dominates Bangladesh's App Market


• Most apps were built before SwiftUI existed (pre-2019)
• Many companies require iOS 12 or older support
• UIKit has richer third-party library ecosystem
• More fine-grained control over performance-critical UI
• Enterprise and banking apps almost always in UIKit

💡 Think of UIKit as working with building blocks by hand. You pick up each brick, set it in place,
define its position precisely, and tell it how to behave. SwiftUI is like describing a house and letting
an architect figure out the bricks.
2. UIViewController Lifecycle
The UIViewController lifecycle is the most fundamental concept in UIKit. Every screen in a UIKit app IS
a UIViewController. Understanding when each method fires is critical.

The Complete Lifecycle Flow


class MyViewController: UIViewController {

// 1. Called ONCE — view is about to be created from nib/storyboard


override func loadView() {
[Link]() // Creates [Link]
// Only override this if creating view entirely programmatically
}

// 2. Called ONCE — view is loaded into memory


override func viewDidLoad() {
[Link]()
// ✅ Setup UI, add subviews, configure data
// ✅ Add constraints here
// ✅ API calls if you need data on first load
setupUI()
setupConstraints()
fetchData()
}

// 3. Called EVERY time view appears on screen


override func viewWillAppear(_ animated: Bool) {
[Link](animated)
// ✅ Refresh data that may have changed
// ✅ Update navigation bar
// ✅ Register for keyboard notifications
navigationController?.setNavigationBarHidden(false, animated: animated)
}

// 4. Called EVERY time view is fully on screen


override func viewDidAppear(_ animated: Bool) {
[Link](animated)
// ✅ Start animations
// ✅ Start camera/audio
// ✅ Show onboarding/tooltips
}

// 5. Called EVERY time view is about to leave


override func viewWillDisappear(_ animated: Bool) {
[Link](animated)
// ✅ Save draft/unsaved changes
// ✅ Stop timers
}

// 6. Called EVERY time view has left the screen


override func viewDidDisappear(_ animated: Bool) {
[Link](animated)
// ✅ Stop audio/video playback
// ✅ Unregister keyboard notifications
// ✅ Cancel ongoing network requests
}

// Called when device rotates


override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {
[Link](to: size, with: coordinator)
[Link] { _ in
// Update layout for new size
}
}

deinit {
// Called when VC is deallocated
print("VC deallocated — no retain cycle")
}
}

When to Put Code — Quick Reference


Task Where to Put It
Create & add subviews viewDidLoad()
Set up Auto Layout constraints viewDidLoad()
Initial API/database fetch viewDidLoad()
Refresh stale data viewWillAppear()
Update nav bar appearance viewWillAppear()
Register keyboard observers viewWillAppear()
Start animations viewDidAppear()
Stop timers / cancel tasks viewWillDisappear()
Unregister observers viewDidDisappear()
Handle rotation layout viewWillTransition(to:with:)

⚠️ NEVER access [Link] in viewDidLoad() — the frame is not final yet. Use
viewDidLayoutSubviews() if you need the final frame for custom drawing.
viewDidLayoutSubviews — The Hidden Lifecycle
override func viewDidLayoutSubviews() {
[Link]()
// Called AFTER Auto Layout runs — frame values are final here
// ✅ Set corner radius based on view size
// ✅ Create CALayer or gradient based on actual frame
[Link] = [Link] / 2
[Link] = [Link]
}
3. Views & View Hierarchy
In UIKit, everything visible on screen is a UIView. Views form a tree structure — the window contains a
root view, which contains child views, which contain their own children. Understanding this hierarchy is
essential.

The UIView Basics


// Creating a view
let redBox = UIView()
[Link] = .red
[Link] = 0.8 // 0.0 = invisible, 1.0 = opaque
[Link] = false // hides but keeps space
[Link] = true

// Adding to parent
[Link](redBox)

// Frame-based positioning (x, y, width, height)


[Link] = CGRect(x: 20, y: 100, width: 200, height: 50)

// Removing from parent


[Link]()

// Layering
[Link](redBox) // Move to front
[Link](redBox) // Move to back
[Link](redBox, at: 2) // Insert at index
[Link](redBox, aboveSubview: otherView)
[Link](redBox, belowSubview: otherView)

Key UIView Properties


Property Type Description Example
frame CGRect Position & size in parent's coord [Link] = CGRect(x:0,
system y:0, w:100, h:100)
bounds CGRect Own internal coordinate system [Link] — own
width
center CGPoint Center point in parent's [Link] = [Link]
coordinates
backgroundColor UIColor? Fill color .systemBlue, .clear,
UIColor(hex:)
alpha CGFloat Transparency 0–1 0 = invisible, 1 = solid
isHidden Bool Hide without removing true = hidden but in
hierarchy
clipsToBounds Bool Clip children to bounds true — needed for
cornerRadius
[Link] CGFloat Rounded corners [Link] =
12
[Link] Float Shadow 0.0–1.0
tag Int Find view by tag viewWithTag(101)
subviews [UIView] Direct children array [Link] {...}
superview UIView? Parent view [Link] ==
containerView

Making Views Round & Styled


// Rounded corners (IMPORTANT: clipsToBounds must be true)
[Link] = [Link] / 2 // Circle
[Link] = 12 // Rounded card
[Link] = true

// Shadow (IMPORTANT: clipsToBounds must be FALSE for shadows)


[Link] = [Link]
[Link] = 0.2
[Link] = CGSize(width: 0, height: 4)
[Link] = 8

// Border
[Link] = [Link]
[Link] = 1.5

// Gradient background
let gradient = CAGradientLayer()
[Link] = [[Link], [Link]]
[Link] = [Link]
[Link] = CGPoint(x: 0, y: 0)
[Link] = CGPoint(x: 1, y: 1)
[Link](gradient, at: 0)
4. Auto Layout — Programmatic Constraints
Auto Layout is the constraint-based layout system in UIKit. Instead of setting fixed frames, you define
rules (constraints) between views that the system solves automatically for any screen size. This is what
replaces SwiftUI's modifiers like .padding() and .frame().

⚠️ CRITICAL: Always set translatesAutoresizingMaskIntoConstraints = false BEFORE activating


constraints. Forgetting this is the #1 mistake beginners make.

The NSLayoutConstraint Way (Old, Verbose)


// ❌ NSLayoutConstraint — verbose, rarely used directly
let constraint = NSLayoutConstraint(
item: button, attribute: .centerX,
relatedBy: .equal,
toItem: view, attribute: .centerX,
multiplier: 1.0, constant: 0
)
[Link] = true

NSLayoutAnchor Way (Modern, Preferred)


class MyViewController: UIViewController {

private let loginButton = UIButton(type: .system)


private let emailField = UITextField()

override func viewDidLoad() {


[Link]()
setupUI()
setupConstraints()
}

private func setupUI() {


[Link]("Login", for: .normal)
[Link] = .systemBlue
[Link](loginButton)
[Link](emailField)
}

private func setupConstraints() {


// Step 1: ALWAYS disable autoresizing mask
[Link] = false
[Link] = false
// Step 2: Activate constraints
[Link]([
// emailField — pin to top with padding
[Link](equalTo:
[Link],
constant: 24),
[Link](equalTo: [Link],
constant: 20),
[Link](equalTo: [Link],
constant: -20),
[Link](equalToConstant: 48),

// loginButton — below emailField, centered


[Link](equalTo: [Link],
constant: 16),
[Link](equalTo: [Link]),
[Link](equalTo: [Link],
multiplier: 0.8),
[Link](equalToConstant: 50),
])
}
}

Anchor Types — Complete Reference


Anchor Axis What It Constrains Example
topAnchor Y Top edge .[Link](equalTo:
[Link], constant: 20)
bottomAnchor Y Bottom edge .[Link](equalTo:
[Link], constant: -20)
leadingAnchor X Left (RTL-aware) .[Link](equalTo:
[Link], constant: 16)
trailingAnchor X Right (RTL-aware) .[Link](equalTo:
[Link], constant: -16)
widthAnchor Size Width .[Link](equalToConstant:
100)
heightAnchor Size Height .[Link](equalToConstant:
44)
centerXAnchor X Horizontal center .[Link](equalTo:
[Link])
centerYAnchor Y Vertical center .[Link](equalTo:
[Link])
firstBaselineAnchor Y Text baseline For aligning labels of different sizes
safeAreaLayoutGuide - Safe area region [Link]
Safe Area — ALWAYS Use It
// ❌ Wrong — overlaps status bar / home indicator
[Link](equalTo: [Link])

// ✅ Correct — respects notch, status bar, home indicator


[Link](equalTo: [Link])
[Link](equalTo: [Link])

Dynamic Constraints — Animating Layout


// Save constraint as property to update later
private var buttonBottomConstraint: NSLayoutConstraint!

// In setupConstraints():
buttonBottomConstraint = [Link](
equalTo: [Link], constant: -20
)
[Link] = true

// Later — animate constraint change:


func keyboardWillShow(height: CGFloat) {
[Link] = -(height + 20)
[Link](withDuration: 0.3) {
[Link]() // ← This applies the new constraint
}
}

Content Hugging & Compression Resistance


// Content Hugging Priority: resistance to GROWING
// (higher = view stays small, doesn't stretch)
[Link](.defaultHigh, for: .horizontal)

// Compression Resistance Priority: resistance to SHRINKING


// (higher = view stays at full size, doesn't compress)
[Link](.required, for: .horizontal)

// Common use: side-by-side label + field


// titleLabel hugs at 251, textField hugs at 250
// Result: textField stretches to fill remaining space
5. Common UI Controls

UILabel
let label = UILabel()
[Link] = "Hello, UIKit!"
[Link] = [Link](ofSize: 16, weight: .medium)
[Link] = UIFont(name: "Helvetica-Bold", size: 18) // Custom font
[Link] = .label // Adaptive dark/light mode color
[Link] = .center // .left, .right, .center, .justified
[Link] = 0 // 0 = unlimited lines
[Link] = .byWordWrapping
[Link] = true
[Link] = 0.7 // Scale down to 70% before truncating

// Attributed text (mixed styles)


let attrs = NSMutableAttributedString(string: "Hello World")
[Link](.foregroundColor, value: [Link],
range: NSRange(location: 0, length: 5))
[Link](.font, value: [Link](ofSize: 20),
range: NSRange(location: 6, length: 5))
[Link] = attrs

UIButton
// System button (auto-tinted)
let button = UIButton(type: .system)
[Link]("Tap Me", for: .normal)
[Link]("Loading...", for: .disabled)
[Link](.white, for: .normal)
[Link](UIImage(systemName: "[Link]"), for: .normal)
[Link] = .systemBlue
[Link] = 12

// Modern iOS 15+ button config


var config = [Link]()
[Link] = "Sign In"
[Link] = "Use your account"
[Link] = UIImage(systemName: "[Link]")
[Link] = .leading
[Link] = 8
[Link] = .medium
[Link] = config
// Target-Action (UIKit's equivalent of .onTapGesture)
[Link](self, action: #selector(loginTapped), for: .touchUpInside)

@objc private func loginTapped() {


print("Button tapped!")
}

// Disable button
[Link] = false
[Link] = 0.5

UITextField
let textField = UITextField()
[Link] = "Enter email"
[Link] = .roundedRect
[Link] = .emailAddress // .numberPad, .phonePad, .URL
[Link] = .done // .next, .search, .send
[Link] = .none
[Link] = .no
[Link] = true // For passwords
[Link] = .whileEditing
[Link] = .emailAddress // For autofill

// Left/Right padding view trick


let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 48))
[Link] = paddingView
[Link] = .always

// Delegate — handle events


[Link] = self

extension MyViewController: UITextFieldDelegate {


func textFieldShouldReturn(_ textField: UITextField) -> Bool {
[Link]() // Dismiss keyboard
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) { /* focused */ }
func textFieldDidEndEditing(_ textField: UITextField) { /* unfocused */ }
func textField(_ textField: UITextField, shouldChangeCharactersIn range:
NSRange,
replacementString string: String) -> Bool {
// Validate input — return false to block characters
return true
}
}
UIImageView
let imageView = UIImageView()

// From assets
[Link] = UIImage(named: "profile_photo")

// SF Symbols (iOS 13+)


[Link] = UIImage(systemName: "[Link]")
[Link] = .systemYellow

// Content modes — how image fits


[Link] = .scaleAspectFill // Fill, may crop
[Link] = .scaleAspectFit // Fit, shows letterbox
[Link] = .center // No scaling, centered
[Link] = true // Clip overflow

// Async image loading (manual — use SDWebImage/Kingfisher in practice)


[Link](with: url) { data, _, _ in
guard let data = data, let image = UIImage(data: data) else { return }
[Link] {
[Link] = image
}
}.resume()

Other Common Controls


// UISwitch
let toggle = UISwitch()
[Link] = true
[Link] = .systemGreen
[Link](self, action: #selector(switchChanged(_:)), for: .valueChanged)
@objc func switchChanged(_ sender: UISwitch) { print([Link]) }

// UISlider
let slider = UISlider()
[Link] = 0
[Link] = 100
[Link] = 50
[Link] = .systemBlue
[Link](self, action: #selector(sliderMoved(_:)), for: .valueChanged)
@objc func sliderMoved(_ sender: UISlider) { print([Link]) }

// UISegmentedControl
let seg = UISegmentedControl(items: ["Day", "Week", "Month"])
[Link] = 0
[Link](self, action: #selector(segChanged(_:)), for: .valueChanged)
@objc func segChanged(_ sender: UISegmentedControl)
{ print([Link]) }

// UIActivityIndicatorView
let spinner = UIActivityIndicatorView(style: .large)
[Link] = .systemBlue
[Link] = true // Auto-hides when not animating
[Link]()
[Link]()

// UIProgressView
let progress = UIProgressView(progressViewStyle: .default)
[Link] = 0.7 // 0.0 to 1.0
[Link](0.9, animated: true)
6. UITableView — The Workhorse of UIKit
UITableView is the most commonly used view in UIKit apps. Virtually every list, feed, menu, and
settings screen in iOS is a UITableView. It uses a delegate+dataSource pattern you MUST master.

The Two Required Protocols


class ProductListVC: UIViewController {

private let tableView = UITableView()


private var products: [Product] = []

override func viewDidLoad() {


[Link]()

// 1. Register the cell type


[Link]([Link],
forCellReuseIdentifier: "ProductCell")

// 2. Set delegate and dataSource


[Link] = self
[Link] = self

// 3. Add to view and constrain


[Link](tableView)
[Link] = false
[Link]([
[Link](equalTo:
[Link]),
[Link](equalTo: [Link]),
[Link](equalTo: [Link]),
[Link](equalTo: [Link]),
])
}
}

// MARK: — UITableViewDataSource (REQUIRED)


extension ProductListVC: UITableViewDataSource {

// How many rows?


func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return [Link]
}

// What cell for this row?


func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = [Link](
withIdentifier: "ProductCell", for: indexPath
)
let product = products[[Link]]
// iOS 14+ way
var content = [Link]()
[Link] = [Link]
[Link] = "৳\([Link])"
[Link] = content
return cell
}

// Optional: number of sections


func numberOfSections(in tableView: UITableView) -> Int { return 1 }
}

// MARK: — UITableViewDelegate (OPTIONAL but often needed)


extension ProductListVC: UITableViewDelegate {

// Row tapped
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
[Link](at: indexPath, animated: true) // ← Always do this!
let product = products[[Link]]
let detailVC = ProductDetailVC(product: product)
navigationController?.pushViewController(detailVC, animated: true)
}

// Row height
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
}

Custom UITableViewCell
class ProductCell: UITableViewCell {
static let identifier = "ProductCell" // ← Best practice: static constant

private let nameLabel = UILabel()


private let priceLabel = UILabel()
private let thumbImageView = UIImageView()

// Called when cell is created from code/nib


override init(style: [Link], reuseIdentifier: String?) {
[Link](style: style, reuseIdentifier: reuseIdentifier)
setupUI()
setupConstraints()
}
required init?(coder: NSCoder) { fatalError() }

// MARK: — Called every time cell is reused with new data


func configure(with product: Product) {
[Link] = [Link]
[Link] = "৳\([Link])"
}

// IMPORTANT: Reset state when cell is reused


override func prepareForReuse() {
[Link]()
[Link] = nil
[Link] = nil
}

private func setupUI() {


[Link] = .systemFont(ofSize: 16, weight: .semibold)
[Link] = .systemFont(ofSize: 14)
[Link] = .secondaryLabel
[Link] = .scaleAspectFill
[Link] = true
[Link] = 8
[Link](nameLabel)
[Link](priceLabel)
[Link](thumbImageView)
}

private func setupConstraints() {


[nameLabel, priceLabel, thumbImageView].forEach {
$[Link] = false
}
[Link]([
[Link](equalTo:
[Link], constant: 16),
[Link](equalTo:
[Link]),
[Link](equalToConstant: 48),
[Link](equalToConstant: 48),
[Link](equalTo:
[Link], constant: 12),
[Link](equalTo: [Link],
constant: 12),
[Link](equalTo:
[Link], constant: -16),
[Link](equalTo: [Link]),
[Link](equalTo: [Link],
constant: 4),
[Link](equalTo: [Link],
constant: -12),
])
}
}

// Register custom cell:


[Link]([Link], forCellReuseIdentifier:
[Link])

// Dequeue custom cell:


let cell = [Link](
withIdentifier: [Link], for: indexPath) as! ProductCell
[Link](with: products[[Link]])

Self-Sizing Cells (Dynamic Height)


// In viewDidLoad — enables automatic height calculation
[Link] = [Link]
[Link] = 72 // Approximate — helps performance

// Your cell constraints must form a VERTICAL CHAIN:


// top-of-content → topAnchor
// bottom-of-content → bottomAnchor
// Without bottom constraint, cell height = 0

Updating TableView Data


// Full reload (simple but janky — no animation)
products = newProducts
[Link]()

// Smooth animated updates


[Link]()
[Link](newProduct, at: 0)
[Link](at: [IndexPath(row: 0, section: 0)], with: .automatic)
[Link]()

// Delete row
[Link]()
[Link](at: [Link]) // ← Update data FIRST
[Link](at: [indexPath], with: .fade)
[Link]()

// Reload specific row


[Link](at: [indexPath], with: .automatic)
7. UICollectionView — Grid & Custom Layouts
UICollectionView is UITableView's more powerful sibling. It supports grids, horizontal scrolling, waterfall
layouts, and complex custom layouts. Think of it as a 2D version of UITableView.

Basic Grid Setup


class PhotoGridVC: UIViewController {

private var photos: [UIImage] = []


private var collectionView: UICollectionView!

override func viewDidLoad() {


[Link]()
setupCollectionView()
}

private func setupCollectionView() {


// 1. Create layout
let layout = UICollectionViewFlowLayout()
[Link] = 2 // Horizontal gap between items
[Link] = 2 // Vertical gap between rows
let itemWidth = ([Link] - 6) / 3 // 3-column grid
[Link] = CGSize(width: itemWidth, height: itemWidth) // Square
cells
[Link] = .vertical // or .horizontal

// 2. Create collection view


collectionView = UICollectionView(frame: .zero,
collectionViewLayout: layout)
[Link] = .systemBackground

// 3. Register cell
[Link]([Link],
forCellWithReuseIdentifier: [Link])

// 4. Set delegates
[Link] = self
[Link] = self

// 5. Layout
[Link](collectionView)
[Link] = false
[Link]([
[Link](equalTo:
[Link]),
[Link](equalTo: [Link]),
[Link](equalTo:
[Link]),
[Link](equalTo: [Link]),
])
}
}

extension PhotoGridVC: UICollectionViewDataSource {


func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return [Link]
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) ->
UICollectionViewCell {
let cell = [Link](
withReuseIdentifier: [Link], for: indexPath) as!
PhotoCell
[Link](with: photos[[Link]]) // .item not .row in
CollectionView
return cell
}
}

extension PhotoGridVC: UICollectionViewDelegate {


func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
// Handle tap
}
}

// Custom cell
class PhotoCell: UICollectionViewCell {
static let identifier = "PhotoCell"
private let imageView = UIImageView()

override init(frame: CGRect) { // ← CollectionView cells use frame: init


[Link](frame: frame)
[Link] = .scaleAspectFill
[Link] = true
[Link](imageView)
[Link] = false
[Link]([
[Link](equalTo: [Link]),
[Link](equalTo: [Link]),
[Link](equalTo:
[Link]),
[Link](equalTo:
[Link]),
])
}
required init?(coder: NSCoder) { fatalError() }

func configure(with image: UIImage) { [Link] = image }


override func prepareForReuse() { [Link](); [Link] =
nil }
}
8. Navigation — Moving Between Screens
UIKit navigation works with container controllers. UINavigationController manages a stack of view
controllers (push/pop). UITabBarController manages multiple root controllers. These are the two most
common navigation patterns.

UINavigationController — Stack Navigation


// App entry point — wrap root VC in NavigationController
// In [Link]:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, ...) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let rootVC = HomeViewController()
let navController = UINavigationController(rootViewController: rootVC)
window?.rootViewController = navController
window?.makeKeyAndVisible()
}

// Push (go forward)


let detailVC = DetailViewController()
navigationController?.pushViewController(detailVC, animated: true)

// Pop (go back)


navigationController?.popViewController(animated: true)

// Pop to root (home)


navigationController?.popToRootViewController(animated: true)

// Present modally (over current context)


let profileVC = ProfileViewController()
present(profileVC, animated: true)

// Dismiss modal
dismiss(animated: true)

// Customize nav bar


title = "Products"
[Link] = UIBarButtonItem(
barButtonSystemItem: .add, target: self, action: #selector(addTapped)
)
[Link] = UIBarButtonItem(
title: "Cancel", style: .plain, target: self, action: #selector(cancelTapped)
)

// Large title (iOS 11+)


navigationController?.[Link] = true
[Link] = .always // .automatic, .never

Passing Data Between ViewControllers


// Method 1: Properties (push forward)
class ProductDetailVC: UIViewController {
var product: Product! // Set before pushing
}
let detailVC = ProductDetailVC()
[Link] = selectedProduct // ← Set BEFORE push
navigationController?.pushViewController(detailVC, animated: true)

// Method 2: Initializer (cleaner, preferred)


class ProductDetailVC: UIViewController {
private let product: Product
init(product: Product) {
[Link] = product
[Link](nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError() }
}
let detailVC = ProductDetailVC(product: selectedProduct)
navigationController?.pushViewController(detailVC, animated: true)

// Method 3: Delegate (passing data BACK from child to parent)


protocol AddProductDelegate: AnyObject {
func didAddProduct(_ product: Product)
}

class AddProductVC: UIViewController {


weak var delegate: AddProductDelegate? // ← weak to avoid retain cycle

@objc func saveTapped() {


let product = Product(name: [Link] ?? "")
delegate?.didAddProduct(product) // ← Notify parent
dismiss(animated: true)
}
}

class ProductListVC: UIViewController, AddProductDelegate {


@objc func addTapped() {
let addVC = AddProductVC()
[Link] = self // ← Assign self as delegate
present(addVC, animated: true)
}
func didAddProduct(_ product: Product) {
[Link](product)
[Link]()
}
}

UITabBarController — Tab Navigation


// In SceneDelegate:
let tabBar = UITabBarController()

let homeVC = UINavigationController(rootViewController: HomeViewController())


[Link] = UITabBarItem(
title: "Home",
image: UIImage(systemName: "house"),
selectedImage: UIImage(systemName: "[Link]")
)

let profileVC = UINavigationController(rootViewController:


ProfileViewController())
[Link] = UITabBarItem(
title: "Profile",
image: UIImage(systemName: "person"),
selectedImage: UIImage(systemName: "[Link]")
)

[Link] = [homeVC, profileVC]


[Link] = 0 // Start on first tab

// Style tab bar


[Link] = .systemBlue

window?.rootViewController = tabBar
9. The Delegation Pattern
Delegation is UIKit's #1 communication pattern. Instead of closures or @State, UIKit uses protocols to
let one object delegate behavior to another. You will use this EVERYWHERE.

How Delegation Works


// Step 1: Define the protocol (the contract)
protocol ImagePickerDelegate: AnyObject {
func imagePicker(_ picker: ImagePickerVC, didSelect image: UIImage)
func imagePickerDidCancel(_ picker: ImagePickerVC)
}

// Step 2: The delegating class holds a weak reference


class ImagePickerVC: UIViewController {
weak var delegate: ImagePickerDelegate? // ALWAYS weak!

@objc func photoSelected() {


let image = UIImage(named: "photo")!
delegate?.imagePicker(self, didSelect: image)
}

@objc func cancelTapped() {


delegate?.imagePickerDidCancel(self)
}
}

// Step 3: The parent conforms to the protocol


class ProfileVC: UIViewController {
@objc func changePhotoTapped() {
let pickerVC = ImagePickerVC()
[Link] = self // Assign self
present(pickerVC, animated: true)
}
}

extension ProfileVC: ImagePickerDelegate {


func imagePicker(_ picker: ImagePickerVC, didSelect image: UIImage) {
[Link] = image
dismiss(animated: true)
}
func imagePickerDidCancel(_ picker: ImagePickerVC) {
dismiss(animated: true)
}
}
⚠️ ALWAYS declare delegate properties as 'weak var'. Without weak, you create a retain cycle:
Parent holds Child, Child's delegate holds Parent. Neither gets deallocated. This is a memory leak.

Closures as an Alternative to Delegation


// Modern pattern — closures instead of delegate protocol
class ImagePickerVC: UIViewController {
var onImageSelected: ((UIImage) -> Void)?
var onCancel: (() -> Void)?

@objc func photoSelected() {


onImageSelected?(UIImage(named: "photo")!)
}
}

// Usage:
let pickerVC = ImagePickerVC()
[Link] = { [weak self] image in // ← [weak self] in closures!
self?.[Link] = image
self?.dismiss(animated: true)
}
present(pickerVC, animated: true)
10. Gesture Recognizers
Gesture recognizers attach to any UIView and detect touch patterns. They replace
SwiftUI's .onTapGesture, .gesture() modifiers.

// Tap
let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped(_:)))
[Link] = 1
[Link](tap)
[Link] = true // ← Required for UILabel/UIImageView

// Long Press
let longPress = UILongPressGestureRecognizer(target: self,
action: #selector(longPressed(_:)))
[Link] = 0.5
[Link](longPress)

// Swipe
let swipe = UISwipeGestureRecognizer(target: self, action: #selector(swiped(_:)))
[Link] = .left // .right, .up, .down
[Link](swipe)

// Pan (drag)
let pan = UIPanGestureRecognizer(target: self, action: #selector(panned(_:)))
[Link](pan)

// Pinch (zoom)
let pinch = UIPinchGestureRecognizer(target: self, action: #selector(pinched(_:)))
[Link](pinch)

// Rotation
let rotation = UIRotationGestureRecognizer(target: self,
action: #selector(rotated(_:)))
[Link](rotation)

// Handlers:
@objc func viewTapped(_ gesture: UITapGestureRecognizer) {
print("Tapped at: \([Link](in: view))")
}

@objc func longPressed(_ gesture: UILongPressGestureRecognizer) {


if [Link] == .began {
print("Long press started")
}
}

@objc func panned(_ gesture: UIPanGestureRecognizer) {


let translation = [Link](in: view)
let targetView = [Link]!
[Link] = CGPoint(
x: [Link].x + translation.x,
y: [Link].y + translation.y
)
[Link](.zero, in: view) // Reset each call
}

@objc func pinched(_ gesture: UIPinchGestureRecognizer) {


[Link]?.transform = [Link]!.transform
.scaledBy(x: [Link], y: [Link])
[Link] = 1.0 // Reset
}
11. Animations
UIKit animations are imperative — you tell iOS what the final state should be, and it transitions
smoothly. The core API is [Link]().

[Link] — The Essential API


// Basic fade out
[Link](withDuration: 0.3) {
[Link] = 0
}

// With completion
[Link](withDuration: 0.3, animations: {
[Link] = 0
}) { finished in
[Link] = true
}

// Spring animation (bouncy)


[Link](
withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.6, // 0=bouncy, 1=no bounce
initialSpringVelocity: 0.8,
options: [],
animations: {
[Link] = .identity // Back to original
}
)

// What can you animate? (animatable properties)


[Link]
[Link]
[Link]
[Link]
[Link]
[Link] // scale, rotate, translate
[Link] // (with CATransaction, not [Link])

// Common transforms:
[Link] = CGAffineTransform(scaleX: 0.95, y: 0.95) // Shrink
[Link] = CGAffineTransform(translationX: 0, y: -50) // Move up
[Link] = CGAffineTransform(rotationAngle: .pi / 4) // Rotate 45°
[Link] = .identity // Reset all transforms
// Slide-in from bottom animation:
func slideIn() {
let offscreen = CGAffineTransform(translationX: 0, y: [Link])
[Link] = offscreen
[Link] = false
[Link](withDuration: 0.4, delay: 0,
usingSpringWithDamping: 0.8, initialSpringVelocity: 1,
options: [], animations: {
[Link] = .identity
})
}

// Pulsing animation (repeating):


[Link](withDuration: 0.8, delay: 0,
options: [.repeat, .autoreverse], animations: {
[Link] = CGAffineTransform(scaleX: 1.1, y: 1.1)
})
12. Alerts, Action Sheets & Pickers

UIAlertController
// Basic alert
let alert = UIAlertController(
title: "Delete Item?",
message: "This cannot be undone.",
preferredStyle: .alert
)
[Link](UIAlertAction(title: "Cancel", style: .cancel))
[Link](UIAlertAction(title: "Delete", style: .destructive) { _ in
[Link]()
})
present(alert, animated: true)

// Alert with text field


let inputAlert = UIAlertController(title: "Add Name", message: nil,
preferredStyle: .alert)
[Link] { field in
[Link] = "Enter name"
[Link] = .words
}
[Link](UIAlertAction(title: "Add", style: .default) { _ in
let name = [Link]?.first?.text ?? ""
[Link](name: name)
})
[Link](UIAlertAction(title: "Cancel", style: .cancel))
present(inputAlert, animated: true)

// Action sheet (bottom sheet style)


let sheet = UIAlertController(title: "Share", message: nil,
preferredStyle: .actionSheet)
[Link](UIAlertAction(title: "Copy Link", style: .default) { _ in })
[Link](UIAlertAction(title: "Share via WhatsApp", style: .default) { _ in
})
[Link](UIAlertAction(title: "Cancel", style: .cancel))

// iPad requires source view for action sheets


if let popover = [Link] {
[Link] = sender
[Link] = [Link]
}
present(sheet, animated: true)
UIImagePickerController — Camera & Photos
import UIKit

class PhotoVC: UIViewController, UIImagePickerControllerDelegate,


UINavigationControllerDelegate {

func openCamera() {
guard [Link](.camera) else { return
}
let picker = UIImagePickerController()
[Link] = .camera
[Link] = true
[Link] = self
present(picker, animated: true)
}

func openPhotoLibrary() {
let picker = UIImagePickerController()
[Link] = .photoLibrary
[Link] = false
[Link] = self
present(picker, animated: true)
}

// Delegate — image selected


func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [[Link]:
Any]) {
let key: [Link] =
[Link] ? .editedImage : .originalImage
if let image = info[key] as? UIImage {
[Link] = image
}
dismiss(animated: true)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {


dismiss(animated: true)
}
}
13. Networking with URLSession
URLSession is UIKit's built-in networking API. It handles HTTP requests, file downloads, and
WebSocket connections. For complex apps, libraries like Alamofire are used, but knowing URLSession
is fundamental.

Basic GET Request


struct Product: Codable {
let id: Int
let name: String
let price: Double
}

func fetchProducts() {
guard let url = URL(string: "[Link] else
{ return }

[Link](with: url) { [weak self] data, response, error in


// Error check
if let error = error {
print("Error: \([Link])")
return
}

// Status code check


guard let httpResponse = response as? HTTPURLResponse,
[Link] == 200 else {
print("Bad response")
return
}

// Decode JSON
guard let data = data else { return }
do {
let products = try JSONDecoder().decode([Product].self, from: data)
// ✅ ALWAYS update UI on main thread!
[Link] {
self?.products = products
self?.[Link]()
}
} catch {
print("Decode error: \(error)")
}
}.resume() // ← Don't forget .resume()!
}
POST Request with JSON Body
func createProduct(name: String, price: Double) {
guard let url = URL(string: "[Link] else
{ return }

var request = URLRequest(url: url)


[Link] = "POST"
[Link]("application/json", forHTTPHeaderField: "Content-Type")
[Link]("Bearer \(authToken)", forHTTPHeaderField: "Authorization")

let body = ["name": name, "price": price] as [String: Any]


[Link] = try? [Link](withJSONObject: body)

[Link](with: request) { data, response, error in


guard let data = data, error == nil else { return }
if let product = try? JSONDecoder().decode([Link], from: data) {
[Link] {
[Link](product, at: 0)
[Link]()
}
}
}.resume()
}

async/await Networking (iOS 15+, Modern)


// Modern Swift concurrency — much cleaner!
func fetchProducts() async throws -> [Product] {
let url = URL(string: "[Link]
let (data, _) = try await [Link](from: url)
return try JSONDecoder().decode([Product].self, from: data)
}

// Calling it from a ViewController:


override func viewDidLoad() {
[Link]()
Task {
do {
let products = try await fetchProducts()
[Link] = products
[Link]() // Already on main actor if Task from VC
} catch {
print("Error: \(error)")
}
}
}

⚠️ Network callbacks run on a BACKGROUND thread. Any UI update ([Link],


[Link], etc.) MUST be dispatched to the main thread with
[Link] { }. Forgetting this causes crashes or visual glitches.
14. Data Persistence

UserDefaults — Simple Key-Value Storage


// Save
[Link]("john@[Link]", forKey: "userEmail")
[Link](true, forKey: "isLoggedIn")
[Link](42, forKey: "userAge")
[Link](["swift", "ios"], forKey: "skills")

// Read
let email = [Link](forKey: "userEmail") ?? ""
let isLoggedIn = [Link](forKey: "isLoggedIn")
let age = [Link](forKey: "userAge")

// Delete
[Link](forKey: "userEmail")

// Save Codable object


struct UserProfile: Codable {
var name: String
var age: Int
}
let profile = UserProfile(name: "Rahim", age: 25)
if let encoded = try? JSONEncoder().encode(profile) {
[Link](encoded, forKey: "profile")
}

// Read Codable object


if let data = [Link](forKey: "profile"),
let profile = try? JSONDecoder().decode([Link], from: data) {
print([Link])
}

FileManager — Saving Files to Disk


// Get Documents directory (persists between launches, NOT iCloud by default)
func documentsDirectory() -> URL {
[Link](for: .documentDirectory, in: .userDomainMask)[0]
}

// Save JSON data


func save(_ products: [Product]) {
let url = documentsDirectory().appendingPathComponent("[Link]")
if let data = try? JSONEncoder().encode(products) {
try? [Link](to: url)
}
}

// Load JSON data


func load() -> [Product] {
let url = documentsDirectory().appendingPathComponent("[Link]")
guard let data = try? Data(contentsOf: url) else { return [] }
return (try? JSONDecoder().decode([Product].self, from: data)) ?? []
}

// Save UIImage to disk


func saveImage(_ image: UIImage, name: String) {
let url = documentsDirectory().appendingPathComponent("\(name).jpg")
try? [Link](compressionQuality: 0.8)?.write(to: url)
}
15. NotificationCenter & Keyboard Handling
NotificationCenter broadcasts events system-wide. Any object can subscribe and respond. It's how
UIKit notifies your app about keyboard, app state, and custom events.

Keyboard Handling — The #1 UIKit Problem


class LoginViewController: UIViewController {

private var bottomConstraint: NSLayoutConstraint!

override func viewWillAppear(_ animated: Bool) {


[Link](animated)
// Subscribe when visible
[Link](
self,
selector: #selector(keyboardWillShow(_:)),
name: [Link],
object: nil
)
[Link](
self,
selector: #selector(keyboardWillHide(_:)),
name: [Link],
object: nil
)
}

override func viewWillDisappear(_ animated: Bool) {


[Link](animated)
// ALWAYS unsubscribe!
[Link](self)
}

@objc private func keyboardWillShow(_ notification: Notification) {


guard let keyboardFrame = [Link]?[
[Link]] as? CGRect else { return }
let keyboardHeight = [Link]
[Link] = -keyboardHeight - 16
[Link](withDuration: 0.3) {
[Link]()
}
}

@objc private func keyboardWillHide(_ notification: Notification) {


[Link] = -20
[Link](withDuration: 0.3) {
[Link]()
}
}

// Dismiss keyboard on tap outside


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
[Link](true) // Dismisses keyboard
}
}

// Custom Notifications:
extension [Link] {
static let userDidLogin = [Link]("userDidLogin")
}

// Post
[Link](name: .userDidLogin, object: nil,
userInfo: ["userId": 123])

// Observe
[Link](forName: .userDidLogin,
object: nil, queue: .main) { notification
in
let userId = [Link]?["userId"] as? Int
print("User \(userId ?? 0) logged in")
}
16. Storyboard vs Programmatic UI
Legacy UIKit apps typically use Storyboards (.storyboard files) for visual UI design. Modern
development often favors programmatic UI for better code review, merge conflict handling, and
flexibility. You need to understand both.

Storyboard Programmatic (Code)


Visual drag-and-drop UI Write all UI in Swift code
Hard to resolve merge conflicts Git-friendly, easy diffs
Segues for navigation Push/present in code
IBOutlet / IBAction connections Direct property references
Faster to prototype More maintainable at scale
Common in older codebases Preferred in modern teams
Can mix with .xib files Can mix with Storyboard

Working with Storyboards (Reading Existing Code)


// IBOutlet — connects UI element to property
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var submitButton: UIButton!
@IBOutlet weak var tableView: UITableView!

// IBAction — connects button/control action to function


@IBAction func submitTapped(_ sender: UIButton) {
// Handle tap
}

// ! (implicit unwrap) — safe here because Storyboard sets them before viewDidLoad
// But crash if not connected in Storyboard!

// Instantiate VC from storyboard


let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = [Link](
withIdentifier: "ProductDetailVC") as! ProductDetailVC
[Link] = selectedProduct
navigationController?.pushViewController(vc, animated: true)

// Segue — defined in Storyboard, triggered by button or code


performSegue(withIdentifier: "showDetail", sender: self)

// Pass data via prepare(for:sender:)


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if [Link] == "showDetail" {
let detailVC = [Link] as! ProductDetailVC
[Link] = selectedProduct
}
}

Full Programmatic VC Template


class ProfileViewController: UIViewController {

// MARK: — UI Properties
private let avatarImageView: UIImageView = {
let iv = UIImageView()
[Link] = .scaleAspectFill
[Link] = true
[Link] = 40
[Link] = .systemGray5
[Link] = false
return iv
}()

private let nameLabel: UILabel = {


let label = UILabel()
[Link] = .systemFont(ofSize: 22, weight: .bold)
[Link] = .center
[Link] = false
return label
}()

// MARK: — Lifecycle
override func viewDidLoad() {
[Link]()
[Link] = .systemBackground
title = "Profile"
setupSubviews()
setupConstraints()
populateData()
}

// MARK: — Setup
private func setupSubviews() {
[Link](avatarImageView)
[Link](nameLabel)
}

private func setupConstraints() {


[Link]([
[Link](
equalTo: [Link], constant: 32),
[Link](equalTo: [Link]),
[Link](equalToConstant: 80),
[Link](equalToConstant: 80),

[Link](
equalTo: [Link], constant: 16),
[Link](
equalTo: [Link], constant: 16),
[Link](
equalTo: [Link], constant: -16),
])
}

// MARK: — Data
private func populateData() {
[Link] = "Mohammad Rahim"
}
}
17. Memory Management & Retain Cycles
UIKit uses ARC (Automatic Reference Counting). Unlike Swift's value types, UIKit views and controllers
are reference types, which means retain cycles are a real danger. Understanding [weak self] and
delegate declarations is essential.

The Retain Cycle Problem


// ❌ RETAIN CYCLE — neither object can be deallocated
class NetworkManager {
var completion: (() -> Void)? // Holds reference to closure
}
class MyVC: UIViewController {
let network = NetworkManager()
func fetchData() {
[Link] = {
[Link]() // ← closure captures self STRONGLY
} // MyVC → NetworkManager → closure → MyVC = CYCLE
}
}

// ✅ FIXED — weak self breaks the cycle


[Link] = { [weak self] in
self?.updateUI() // ← self is Optional now, nil if VC was deallocated
}

// Alternative: unowned (when you're 100% sure VC will outlive network call)
[Link] = { [unowned self] in
[Link]() // ← crashes if self is nil — use with caution
}

Weak Reference Rules


Situation Use Why
Delegate properties weak var delegate Delegator shouldn't own the delegatee
Closures capturing self [weak self] Closure in class that captures containing class
IBOutlets @IBOutlet weak var View hierarchy owns views, not the VC
Timer callbacks [weak self] Timer holds strong reference to target
Notification observers [weak self] in block NotificationCenter holds strong ref to closure
Checking for Memory Leaks
// Add deinit to verify deallocation:
class ProfileViewController: UIViewController {
deinit {
print("✅ ProfileViewController deallocated")
// If this never prints after dismissing — you have a leak!
}
}

// Use Xcode Instruments > Leaks to find retain cycles


// Use Xcode Memory Graph Debugger (Debug menu > Memory Graph)
18. UIStackView — Auto Layout's Best Friend
UIStackView is a container that automatically arranges its children in a row or column. It's the closest
UIKit equivalent to SwiftUI's HStack/VStack and dramatically reduces the number of constraints you
need to write.

// Vertical stack (like VStack)


let vStack = UIStackView()
[Link] = .vertical
[Link] = 16
[Link] = .fill // .leading, .trailing, .center, .fill
[Link] = .fill // .fillEqually, .equalSpacing, .equalCentering
[Link] = false

[Link](titleLabel) // ← addArrangedSubview, not addSubview!


[Link](subtitleLabel)
[Link](actionButton)

[Link](vStack)
[Link]([
[Link](equalTo: [Link],
constant: 24),
[Link](equalTo: [Link], constant: 20),
[Link](equalTo: [Link], constant: -20),
])

// Horizontal stack (like HStack)


let hStack = UIStackView()
[Link] = .horizontal
[Link] = 12
[Link] = .center

// Nested stacks (very common pattern)


let outerStack = UIStackView() // Vertical
[Link] = .vertical
[Link](headerLabel)
[Link](hStack) // Nested horizontal inside vertical

// Custom spacing between specific items


[Link](32, after: titleLabel)

// Show/hide items (stack auto-adjusts layout)


[Link] = true // Stack collapses the space
[Link] = false // Stack expands to fit it

// Add separator line between items


let separator = UIView()
[Link] = .separator
[Link](equalToConstant: 0.5).isActive = true
[Link](separator)
19. UIScrollView — Scrollable Content
UIScrollView is the base class for all scrollable content — including UITableView and UICollectionView.
When you need a scrollable form or long content that isn't a list, you use UIScrollView directly.

Programmatic ScrollView with Content


class SignUpViewController: UIViewController {

private let scrollView = UIScrollView()


private let contentView = UIView() // Container inside scroll view

override func viewDidLoad() {


[Link]()
setupScrollView()
}

private func setupScrollView() {


[Link](scrollView)
[Link](contentView) // Content goes inside contentView

[Link] = false
[Link] = false

[Link]([
// ScrollView fills the parent view
[Link](equalTo:
[Link]),
[Link](equalTo: [Link]),
[Link](equalTo: [Link]),
[Link](equalTo: [Link]),

// ContentView pins to scroll view's content area


[Link](equalTo:
[Link]),
[Link](equalTo:
[Link]),
[Link](equalTo:
[Link]),
[Link](equalTo:
[Link]),

// CRITICAL: Set contentView width = scrollView width (for vertical


scrolling)
[Link](equalTo:
[Link]),
])

// Add content to contentView


let stack = UIStackView()
[Link] = .vertical
[Link] = 16
[Link] = false
[Link](stack)

// Add fields, labels, etc. to stack...

[Link]([
[Link](equalTo: [Link], constant:
20),
[Link](equalTo: [Link],
constant: 20),
[Link](equalTo: [Link],
constant: -20),
[Link](equalTo: [Link],
constant: -20),
])
}
}
20. Quick Reference Cheatsheet

System Colors (Adaptive Dark/Light Mode)


// Semantic colors — automatically adapt to dark mode
.label // Primary text
.secondaryLabel // Secondary text (gray)
.tertiaryLabel // Tertiary text
.systemBackground // Main background (white / dark gray)
.secondarySystemBackground // Cards, list rows
.tertiarySystemBackground // Grouped table headers
.separator // Thin separator lines
.systemFill // Control backgrounds

// System palette colors (also dark-mode aware)


.systemBlue, .systemGreen, .systemRed, .systemOrange
.systemPurple, .systemPink, .systemYellow, .systemTeal
.systemIndigo, .systemMint, .systemCyan, .systemBrown

// Creating custom colors


UIColor(red: 0.2, green: 0.5, blue: 0.8, alpha: 1.0)
UIColor(white: 0.9, alpha: 1.0)
UIColor(named: "PrimaryBlue") // From [Link]

System Fonts
// Dynamic type — respects user's font size setting
[Link](forTextStyle: .title1) // Large title
[Link](forTextStyle: .title2)
[Link](forTextStyle: .headline) // Semibold body
[Link](forTextStyle: .body) // Standard body
[Link](forTextStyle: .subheadline)
[Link](forTextStyle: .caption1) // Small caption
[Link](forTextStyle: .caption2)

// Weight
[Link](ofSize: 16, weight: .ultraLight)
[Link](ofSize: 16, weight: .light)
[Link](ofSize: 16, weight: .regular)
[Link](ofSize: 16, weight: .medium)
[Link](ofSize: 16, weight: .semibold)
[Link](ofSize: 16, weight: .bold)
[Link](ofSize: 16, weight: .heavy)
[Link](ofSize: 16, weight: .black)

// Monospace
[Link](ofSize: 14, weight: .regular)

Essential UIKit Patterns Summary


SwiftUI Equivalent UIKit Approach
.onTapGesture { } [Link](self, action: #selector(f),
for: .touchUpInside)
@State var text = "" var text = "" + manual UI update in setter
@Binding Delegate protocol with weak var delegate
NavigationLink navigationController?.pushViewController(vc, animated: true)
.sheet(isPresented:) present(modalVC, animated: true)
LazyVStack / List UITableView with dequeue
LazyVGrid UICollectionView with UICollectionViewFlowLayout
HStack/VStack UIStackView with .horizontal/.vertical axis
ScrollView UIScrollView with content view inside
.padding() NSLayoutConstraint constant values
.frame(width:height:) widthAnchor/heightAnchor constraints
Text("hello") UILabel with [Link] = "hello"
Image("photo") UIImageView with [Link] = UIImage(named:)
TextField(_, text: $var) UITextField with delegate
.animation() [Link](withDuration:)
onAppear { } viewDidAppear(_:)
onDisappear { } viewDidDisappear(_:)
.alert() UIAlertController with .alert style
.confirmationDialog() UIAlertController with .actionSheet style

Common Crash Causes & Fixes


Crash / Issue Cause Fix
UI update crash Updating UI on background Wrap in [Link] { }
thread
Force unwrap crash nil IBOutlet Check Storyboard connection
Constraint conflict Missing Always set it to false
translatesAutoresizingMask =
false
Zero height cell Missing bottom constraint in Add bottom pin to contentView
custom cell
Memory leak Strong delegate reference Declare delegate as weak var
Gesture not working isUserInteractionEnabled = false Enable on UILabel/UIImageView
Black view on rotation Constraint not covering bounds Use edges of superview correctly
Corner radius not showing clipsToBounds not set Set clipsToBounds = true

You might also like