iOS
viewWillAppear
viewDidAppear
UIViewController
Swift Development

View frame changes between viewWillAppear and viewDidAppear

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If a view's frame changes between viewWillAppear and viewDidAppear, the usual reason is that Auto Layout and container transitions are still settling the final geometry. In other words, viewWillAppear happens before the view is fully onscreen and before the final post-layout frame is guaranteed.

Why the Frame Can Change

Several things can still affect layout after viewWillAppear runs:

  • Auto Layout constraint resolution
  • navigation bar or tab bar adjustments
  • safe area inset changes
  • container controller transitions
  • animated presentations

That means code like this can print different values at different lifecycle points:

swift
1override func viewWillAppear(_ animated: Bool) {
2    super.viewWillAppear(animated)
3    print("willAppear frame:", view.frame)
4}
5
6override func viewDidAppear(_ animated: Bool) {
7    super.viewDidAppear(animated)
8    print("didAppear frame:", view.frame)
9}

The later callback often reflects the final onscreen geometry more accurately.

The Better Layout Hook: viewDidLayoutSubviews

If your goal is "I need the final laid-out size," viewDidLayoutSubviews is usually the better lifecycle hook than viewDidAppear.

swift
1override func viewDidLayoutSubviews() {
2    super.viewDidLayoutSubviews()
3    print("laid out frame:", view.frame)
4    print("laid out bounds:", view.bounds)
5}

This method runs after Auto Layout has laid out the subviews, so it is the right place for frame-dependent layout adjustments.

viewWillAppear Versus viewDidAppear

Use viewWillAppear for:

  • refreshing content
  • updating navigation items
  • configuring state before the screen becomes visible

Use viewDidAppear for:

  • starting animations that require visible presentation
  • kicking off work that depends on the view actually being onscreen
  • final checks where transition completion matters

If the issue is specifically geometry, viewDidLayoutSubviews is often more precise than either.

Example: Positioning a Subview Based on Final Size

Suppose you want to size a banner to match the safe area width:

swift
1final class DemoViewController: UIViewController {
2    private let banner = UIView()
3
4    override func viewDidLoad() {
5        super.viewDidLoad()
6        banner.backgroundColor = .systemBlue
7        view.addSubview(banner)
8    }
9
10    override func viewDidLayoutSubviews() {
11        super.viewDidLayoutSubviews()
12
13        let inset = view.safeAreaInsets
14        banner.frame = CGRect(
15            x: inset.left + 20,
16            y: inset.top + 20,
17            width: view.bounds.width - inset.left - inset.right - 40,
18            height: 60
19        )
20    }
21}

Doing this in viewWillAppear is riskier because safe-area values and final bounds may still change.

Container Controllers Make This More Noticeable

When a controller is inside:

  • a UINavigationController
  • a UITabBarController
  • a custom container
  • a sheet or popover presentation

the final frame is even more likely to shift after viewWillAppear. Bars, insets, and transition coordinators can all alter the effective visible area.

That is why layout-sensitive code often behaves inconsistently when moved between different presentation styles.

Frames Versus Bounds

Another source of confusion is looking at frame when bounds is really the value you care about.

  • 'frame is the view's rectangle in its superview's coordinate system'
  • 'bounds is the view's own internal coordinate rectangle'

For many internal layout calculations, bounds is the more stable and relevant measurement:

swift
print(view.frame)
print(view.bounds)

If you are laying out subviews inside self.view, prefer bounds unless you explicitly need superview coordinates.

This distinction becomes even more important if transforms or nontrivial container hierarchies are involved, because frame can reflect relationships outside the view's own coordinate system.

Common Pitfalls

The biggest mistake is doing frame-dependent work in viewWillAppear and assuming the numbers are final. That assumption can break as soon as Auto Layout, safe areas, or animated presentation enters the picture.

Another issue is using viewDidAppear for one-time frame setup when viewDidLayoutSubviews is the real layout callback. viewDidAppear runs after visibility, not specifically after every layout pass.

Finally, avoid hard-coding frame math if constraints can express the layout cleanly. In many modern UIKit screens, the best answer is not "pick the right lifecycle method" but "let Auto Layout own the geometry."

Summary

  • Frame changes between viewWillAppear and viewDidAppear usually come from layout and transition timing.
  • 'viewWillAppear is not a guarantee that final geometry has settled.'
  • 'viewDidLayoutSubviews is usually the best place for frame-dependent layout code.'
  • 'viewDidAppear is better for visible-state actions than for pure layout.'
  • Prefer bounds for internal subview layout and Auto Layout when possible.

Course illustration
Course illustration

All Rights Reserved.