ios-swift
/

UIViewController – The Heart of iOS Logic

Last Sync: Today

On this page

5
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

ios-swift

UIViewController – The Heart of iOS Logic

The Role of the View Controller

A UIViewController manages a single 'screen' or a significant portion of a screen's interface. It is responsible for loading the view, handling user interactions (taps, swipes), and coordinating with the data layer. As an Engineering Manager, you should ensure your team follows the Single Responsibility Principle, preventing the 'Massive View Controller' anti-pattern by offloading business logic to ViewModels or Services.

  1. The View Lifecycle

Unlike Flutter widgets which are rebuilt constantly, a View Controller is an object with a persistent lifecycle. Understanding the sequence of events is critical for performance—for example, you should fetch data from your Python API in viewDidLoad, but update the UI visibility in viewWillAppear.

  • viewDidLoad(): Called once when the view is first loaded into memory. Perfect for one-time setup and initializing UI components.
  • viewWillAppear(): Called every time the view is about to appear on screen. Use this for tasks that need to happen every time the user returns to the screen.
  • viewDidAppear(): Called after the view is fully visible. Ideal for starting animations or GPS tracking.
  • viewWillDisappear(): Called when the user navigates away. Use this to hide the keyboard or cancel pending network requests.
  • viewDidDisappear(): Called after the view is gone. Final cleanup happens here.

  1. View Management

Every View Controller has a root view property. You build your interface by adding subviews to this root. In modern iOS development, you define the layout of these views using Auto Layout (constraints) to ensure they adapt to different iPhone screen sizes.

SWIFTRead-only
1
class ProjectViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupUI()
    }
    
    private func setupUI() {
        let label = UILabel()
        label.text = "Revochamp Engine Active"
        label.center = view.center
        view.addSubview(label)
    }
}

  1. Container View Controllers

Architects use 'Container' controllers to manage multiple child controllers. Common examples include UINavigationController (for stacks) and UITabBarController (for tabs). You can also create custom containers to swap between a 'Loading' state controller and a 'Data' state controller.

Controller vs. Widget

FeatureUIViewController (Native)StatefulWidget (Flutter)
NaturePersistent ObjectImmutable/Rebuilt
Logic PlacementMethods (viewDidLoad, etc.)initState / build
UI DefinitionImperative (addSubview)Declarative (Widget Tree)
MemoryManaged via ARCManaged via Dart GC
LayoutAuto Layout / ConstraintsFlexbox / Box Constraints

Test Your Knowledge

Q1
of 3

Which lifecycle method is called exactly once when the controller's view is first created?

A
viewWillAppear
B
viewDidAppear
C
viewDidLoad
D
viewWillLayoutSubviews
Q2
of 3

What is the root property that every UIViewController uses to host its visual content?

A
rootView
B
container
C
view
D
window
Q3
of 3

Which method should be used to stop a repeating timer when the user leaves a screen?

A
viewDidLoad
B
viewWillAppear
C
viewWillDisappear
D
didReceiveMemoryWarning

Frequently Asked Questions

What is the difference between loadView and viewDidLoad?

You should rarely override 'loadView' unless you are building your entire UI programmatically without Storyboards or XIBs. 'viewDidLoad' is where the vast majority of your setup code belongs because the view is guaranteed to exist by then.

How do I communicate between two View Controllers?

For one-way communication (Forward), you inject data into properties before pushing. For two-way communication (Backward), Architects use the Delegate Pattern or Closures to pass information back to the previous screen.

Why is my UIViewController not being released from memory?

This is usually caused by a Strong Reference Cycle. If a closure or a delegate holds a 'strong' reference back to the controller, it will never be deallocated. Always use '[weak self]' in closures to prevent this.

Previous

ios navigation

Next

ios networking

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.