iOS development
presentModalViewController
transparent view
iPhone app design
UIKit

How to use presentModalViewController to create a transparent view

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

To present a transparent modal view controller in iOS, set the presented view controller's modalPresentationStyle to .overFullScreen (or .overCurrentContext) and give its view a semi-transparent background color. The older presentModalViewController:animated: method was deprecated in iOS 6 — use present(_:animated:completion:) instead. The key is .overFullScreen, which keeps the presenting view controller's view in the hierarchy so it is visible behind the transparent overlay.

Basic Transparent Modal (Swift)

swift
1class OverlayViewController: UIViewController {
2    override func viewDidLoad() {
3        super.viewDidLoad()
4        view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
5
6        let label = UILabel()
7        label.text = "Tap to dismiss"
8        label.textColor = .white
9        label.translatesAutoresizingMaskIntoConstraints = false
10        view.addSubview(label)
11
12        NSLayoutConstraint.activate([
13            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
14            label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
15        ])
16
17        let tap = UITapGestureRecognizer(target: self, action: #selector(dismissSelf))
18        view.addGestureRecognizer(tap)
19    }
20
21    @objc private func dismissSelf() {
22        dismiss(animated: true)
23    }
24}
25
26// Presenting the transparent overlay
27let overlay = OverlayViewController()
28overlay.modalPresentationStyle = .overFullScreen
29overlay.modalTransitionStyle = .crossDissolve
30present(overlay, animated: true)

The .overFullScreen style tells UIKit to keep the presenting view controller's view in the hierarchy. Without this, UIKit removes it after the transition, and the background turns black.

Why .fullScreen Does Not Work

swift
1// THIS SHOWS A BLACK BACKGROUND — not transparent
2let overlay = OverlayViewController()
3overlay.modalPresentationStyle = .fullScreen  // Removes presenting VC's view
4present(overlay, animated: true)
5// After animation, presenting VC's view is removed from the window
6// Background is the window's backgroundColor (usually black)

With .fullScreen, UIKit removes the presenting view controller's view from the window hierarchy after the presentation animation completes. Even though the overlay's background is semi-transparent, there is nothing behind it to show — just the window's background color.

Using .overCurrentContext

swift
1// .overCurrentContext works within container VCs (tab bar, navigation)
2let overlay = OverlayViewController()
3overlay.modalPresentationStyle = .overCurrentContext
4overlay.modalTransitionStyle = .crossDissolve
5present(overlay, animated: true)

.overCurrentContext is similar to .overFullScreen but respects the presenting view controller's definesPresentationContext property. In a tab bar or navigation controller setup, it overlays only the current content area, not the full screen.

Custom Transition Animation

swift
1class FadeTransition: NSObject, UIViewControllerAnimatedTransitioning {
2    let isPresenting: Bool
3
4    init(isPresenting: Bool) {
5        self.isPresenting = isPresenting
6    }
7
8    func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval {
9        return 0.3
10    }
11
12    func animateTransition(using context: UIViewControllerContextTransitioning) {
13        let key: UITransitionContextViewControllerKey = isPresenting ? .to : .from
14        guard let controller = context.viewController(forKey: key) else { return }
15
16        if isPresenting {
17            context.containerView.addSubview(controller.view)
18            controller.view.alpha = 0
19        }
20
21        UIView.animate(withDuration: transitionDuration(using: context), animations: {
22            controller.view.alpha = self.isPresenting ? 1 : 0
23        }) { _ in
24            if !self.isPresenting {
25                controller.view.removeFromSuperview()
26            }
27            context.completeTransition(!context.transitionWasCancelled)
28        }
29    }
30}
31
32// Presenting VC conforms to UIViewControllerTransitioningDelegate
33class PresentingVC: UIViewController, UIViewControllerTransitioningDelegate {
34    func showOverlay() {
35        let overlay = OverlayViewController()
36        overlay.modalPresentationStyle = .overFullScreen
37        overlay.transitioningDelegate = self
38        present(overlay, animated: true)
39    }
40
41    func animationController(forPresented presented: UIViewController,
42                              presenting: UIViewController,
43                              source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
44        return FadeTransition(isPresenting: true)
45    }
46
47    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
48        return FadeTransition(isPresenting: false)
49    }
50}

Objective-C Version

objc
1// OverlayViewController.m
2- (void)viewDidLoad {
3    [super viewDidLoad];
4    self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
5
6    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
7        initWithTarget:self action:@selector(dismissSelf)];
8    [self.view addGestureRecognizer:tap];
9}
10
11- (void)dismissSelf {
12    [self dismissViewControllerAnimated:YES completion:nil];
13}
14
15// Presenting
16OverlayViewController *overlay = [[OverlayViewController alloc] init];
17overlay.modalPresentationStyle = UIModalPresentationOverFullScreen;
18overlay.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
19[self presentViewController:overlay animated:YES completion:nil];

SwiftUI Equivalent

swift
1struct ContentView: View {
2    @State private var showOverlay = false
3
4    var body: some View {
5        Button("Show Overlay") {
6            showOverlay = true
7        }
8        .fullScreenCover(isPresented: $showOverlay) {
9            ZStack {
10                Color.black.opacity(0.5)
11                    .ignoresSafeArea()
12                    .onTapGesture { showOverlay = false }
13
14                VStack {
15                    Text("Overlay Content")
16                        .foregroundColor(.white)
17                        .padding()
18                        .background(RoundedRectangle(cornerRadius: 12).fill(.ultraThinMaterial))
19                }
20            }
21            .background(ClearBackgroundView())
22        }
23    }
24}
25
26// Helper to make fullScreenCover background transparent
27struct ClearBackgroundView: UIViewRepresentable {
28    func makeUIView(context: Context) -> UIView {
29        let view = UIView()
30        DispatchQueue.main.async {
31            view.superview?.superview?.backgroundColor = .clear
32        }
33        return view
34    }
35    func updateUIView(_ uiView: UIView, context: Context) {}
36}

Common Pitfalls

  • Using .fullScreen instead of .overFullScreen: .fullScreen removes the presenting view from the window hierarchy after the transition. Even with a transparent background, you see black (the window color). Always use .overFullScreen or .overCurrentContext for transparent overlays.
  • Setting background color before presentation instead of in viewDidLoad: If you set overlayVC.view.backgroundColor before presenting, the view may not be loaded yet and the setting is lost. Set the background color in the overlay's viewDidLoad method, which runs after the view is created.
  • Forgetting definesPresentationContext with .overCurrentContext: When presenting from a child of a container (tab bar, navigation), .overCurrentContext looks for the nearest ancestor with definesPresentationContext = true. If none is set, the presentation falls back to the root view controller. Set definesPresentationContext = true on the appropriate parent to control which content is overlaid.
  • Using the deprecated presentModalViewController:animated:: This method was deprecated in iOS 6. It does not support modern presentation styles like .overFullScreen. Use present(_:animated:completion:) to access the full range of UIModalPresentationStyle options.
  • SwiftUI fullScreenCover not supporting transparent backgrounds natively: SwiftUI's fullScreenCover uses .fullScreen presentation by default, which removes the background. Workarounds require a UIViewRepresentable to clear the hosting view's background, or using a ZStack overlay instead of a modal presentation.

Summary

  • Use .overFullScreen as the modalPresentationStyle to keep the presenting view visible behind the overlay
  • Set the overlay's background to a semi-transparent color in viewDidLoad
  • Use .crossDissolve as the modalTransitionStyle for a smooth fade-in effect
  • For SwiftUI, use a ZStack overlay or a ClearBackgroundView UIViewRepresentable workaround
  • The deprecated presentModalViewController:animated: does not support modern presentation styles — use present(_:animated:completion:) instead

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.