iPhone
activity indicator
iOS development
UI design
mobile app development

How to display activity indicator in middle of the iphone screen?

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

A centered loading indicator is a small UI detail, but it often exposes bigger quality problems in iOS apps such as thread misuse, inconsistent cleanup, and blocked interaction. The real job is not just putting a spinner in the middle of the screen. It is managing loading state safely across success, failure, and cancellation paths.

Center a Spinner with Auto Layout in UIKit

For UIKit screens, use UIActivityIndicatorView with constraints. Avoid manual frame calculations because they drift on rotation and dynamic layout changes.

swift
1import UIKit
2
3final class OrdersViewController: UIViewController {
4    private let spinner = UIActivityIndicatorView(style: .large)
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        view.backgroundColor = .systemBackground
9
10        spinner.translatesAutoresizingMaskIntoConstraints = false
11        spinner.hidesWhenStopped = true
12        view.addSubview(spinner)
13
14        NSLayoutConstraint.activate([
15            spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
16            spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor)
17        ])
18    }
19
20    func showLoading() {
21        spinner.startAnimating()
22    }
23
24    func hideLoading() {
25        spinner.stopAnimating()
26    }
27}

This keeps the spinner centered across device sizes, rotation changes, and later layout adjustments.

Add a Full Screen Overlay for Blocking Operations

If users should not interact while loading, place the spinner inside a dimmed overlay view. This communicates that the screen is temporarily busy.

swift
1import UIKit
2
3final class LoadingOverlay {
4    private let container = UIView()
5    private let spinner = UIActivityIndicatorView(style: .large)
6
7    init(parent: UIView) {
8        container.translatesAutoresizingMaskIntoConstraints = false
9        container.backgroundColor = UIColor.black.withAlphaComponent(0.2)
10        container.isHidden = true
11
12        spinner.translatesAutoresizingMaskIntoConstraints = false
13        spinner.hidesWhenStopped = true
14
15        parent.addSubview(container)
16        container.addSubview(spinner)
17
18        NSLayoutConstraint.activate([
19            container.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
20            container.trailingAnchor.constraint(equalTo: parent.trailingAnchor),
21            container.topAnchor.constraint(equalTo: parent.topAnchor),
22            container.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
23            spinner.centerXAnchor.constraint(equalTo: container.centerXAnchor),
24            spinner.centerYAnchor.constraint(equalTo: container.centerYAnchor)
25        ])
26    }
27
28    func show() {
29        container.isHidden = false
30        spinner.startAnimating()
31    }
32
33    func hide() {
34        spinner.stopAnimating()
35        container.isHidden = true
36    }
37}

An overlay is useful for short operations. For long operations, combine with progress text or a cancel action.

Keep Loading State Main Thread Safe

UI changes must be on the main actor. Also ensure hiding logic runs even if network calls fail.

swift
1import Foundation
2
3@MainActor
4func loadOrders(using overlay: LoadingOverlay) async {
5    overlay.show()
6    defer { overlay.hide() }
7
8    do {
9        let url = URL(string: "https://example.com/orders")!
10        _ = try await URLSession.shared.data(from: url)
11    } catch {
12        print("Request failed: \(error.localizedDescription)")
13    }
14}

defer is important here because it protects cleanup in all control paths.

SwiftUI Version with ProgressView

In SwiftUI, use a layered layout where loading state controls overlay visibility.

swift
1import SwiftUI
2
3struct OrdersScreen: View {
4    @State private var isLoading = false
5
6    var body: some View {
7        ZStack {
8            List(1...20, id: \.self) { index in
9                Text("Order \(index)")
10            }
11
12            if isLoading {
13                Color.black.opacity(0.2).ignoresSafeArea()
14                ProgressView("Loading")
15                    .padding()
16                    .background(.ultraThinMaterial)
17                    .clipShape(RoundedRectangle(cornerRadius: 10))
18            }
19        }
20        .task {
21            isLoading = true
22            try? await Task.sleep(nanoseconds: 900_000_000)
23            isLoading = false
24        }
25    }
26}

This approach is concise and works well with async tasks and state driven UI updates.

Accessibility and UX Considerations

Loading indicators should be informative, not decorative. In critical flows:

  • Announce loading changes for assistive technologies.
  • Prevent infinite spinners by enforcing timeouts.
  • Offer retry if a request fails.
  • Preserve user context after loading completes.

A spinner alone does not explain failure, so pair it with error messaging where needed.

Common Pitfalls

  • Starting animation and forgetting to stop it on failed requests.
  • Updating spinner state from background threads.
  • Creating multiple overlapping spinners from repeated taps.
  • Using a blocking overlay for long tasks with no status text.
  • Hiding the spinner too early before dependent UI data is ready.

Summary

  • Center indicators with Auto Layout in UIKit and state driven overlays in SwiftUI.
  • Use a full screen overlay when interaction must be paused.
  • Keep UI updates on the main actor and use defer for reliable cleanup.
  • Add accessible loading feedback for critical user journeys.
  • Treat loading UI as part of error handling and screen state design, not a standalone widget.

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.