Swift and SwiftUI tutorials for Swift Developers

ScrollView vs List in SwiftUI

As an iOS Developer, one of the most frequent architectural and design decisions you will face when building interfaces is how to present collections of data that exceed the screen size. In the UIKit days, the answer was almost always UITableView or UICollectionView. Today, modern Swift programming offers us a fascinating declarative paradigm.

Both components allow scrolling, both can display iterated data, and both are supported by Xcode for development across iOS, macOS, and watchOS.

In this extensive tutorial, we are going to break down the similarities, differences, advantages, and use cases of each, ScrollView vs List in SwiftUI, so you can perfectly master building interfaces with SwiftUI.


1. Understanding the Basics: What do List and ScrollView solve?

Before diving into the ScrollView vs List in SwiftUI comparison, it is crucial to understand the philosophy behind each component. Both exist to solve the problem of limited screen space, but they approach it from very different angles.

The Anatomy of List

List is the direct equivalent of UITableView (on iOS), NSTableView (on macOS), and WKInterfaceTable (on watchOS). It is a high-level component, strongly standardized by Apple, which applies native styles by default.

Key features of List:

  • “Out-of-the-box” styling: It comes with cell separators, margins, and grouping behaviors (like InsetGroupedListStyle) predefined by the operating system.
  • Native interaction management: It supports actions like swipe to delete (.onDelete), reorder (.onMove), and context menu buttons with minimal effort.
  • Lazy by default: A List does not load all its rows into memory at the same time. Much like cell recycling in UIKit, it only renders what is on screen (or about to be).

The Anatomy of ScrollView

ScrollView, on the other hand, is a blank canvas. It is the equivalent of a basic UIScrollView. It has no opinions on what your data should look like; it simply gives you infinite space (vertical or horizontal) to place views and allows the user to scroll through them.

Key features of ScrollView:

  • Absolute flexibility: You can build complex grids, horizontal carousels, or detail views with expanding header images.
  • No default styles: There are no separators, no predefined list margins, and no gray backgrounds. What you design is exactly what you get.
  • Loads everything into memory (if using VStack or HStack): By default, a ScrollView combined with a standard VStack will instantiate all child views immediately. To achieve the “lazy” behavior of a List, you must explicitly use LazyVStack or LazyHStack.

2. Similarities: Where Both Worlds Converge

Despite their differences, any iOS Developer will quickly notice that Swift programming treats them similarly on the surface.

  • Support for ForEach: Both containers can use ForEach to iterate over collections of data that conform to the Identifiable protocol.
  • Cross-platform: Both List and ScrollView compile seamlessly in Xcode for iOS, macOS, tvOS, and watchOS.
  • Scrolling Engine: Both integrate Apple’s physics engine for scrolling, including the bounce effect when reaching the edges and support for scroll indicators.

3. Key Differences: ScrollView vs List in SwiftUI

This is where the decision becomes critical for the performance and User Experience (UX) of your application.

A. Memory Management and Performance (Lazy Loading)

Memory handling is perhaps the most important technical aspect when evaluating ScrollView vs List in SwiftUI.

With List:
The List is smart. If you have an array of 10,000 items, SwiftUI will only create views for the 10 or 15 items that fit on the screen. As you scroll, it recycles and renders on demand.

import SwiftUI

struct ListExample: View {
    let items = Array(1...10000)

    var body: some View {
        // List is "lazy" by default. Fast and efficient loading.
        List(items, id: \.self) { item in
            Text("Row \(item)")
        }
    }
}

With ScrollView:
If you place 10,000 items in a ScrollView using a standard VStack, your app will likely crash or freeze, because SwiftUI will try to draw all 10,000 views before showing the screen. To match the performance of the List, you must use a LazyVStack.

import SwiftUI

struct ScrollViewExample: View {
    let items = Array(1...10000)

    var body: some View {
        ScrollView {
            // LazyVStack is mandatory here for massive collections
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    Text("Row \(item)")
                        // You have to add your own padding and styling
                        .padding() 
                }
            }
        }
    }
}

B. Interactivity and Data Editing

If your application requires the user to manipulate data, the scale tips heavily towards the List.

  • List: It has native integration with the editing environment (EditMode). Modifiers like .onDelete (for swipe-to-delete) and .onMove (for drag and drop) work only inside a List or a ForEach backed by a List.
  • ScrollView: It does not support .onDelete natively. If you want to implement swipe-to-delete in a ScrollView, as an iOS Developer you will have to write complex custom gestures (DragGesture) and manual animations for each row.

C. Styling and Separators

  • List: Gives you Apple’s official “Look and Feel”. Use .listStyle(.insetGrouped) on iOS or .listStyle(.sidebar) on macOS to get interfaces identical to system apps. Hiding list separators used to be a headache, though since iOS 15 it’s easier with .listRowSeparator(.hidden).
  • ScrollView: Gives you total freedom. You don’t have to fight against unwanted margins or system separators. It is ideal for Pinterest-style boards, product catalogs, or social media feeds.

4. Cross-Platform Behavior: iOS, macOS, and watchOS

The beauty of SwiftUI and Xcode is cross-platform development, but context matters.

On iOS and iPadOS

  • List dominates Settings screens, hierarchical navigation, and inboxes (Mail).
  • ScrollView dominates custom feeds (Instagram, TikTok), storefronts (App Store), or any view where items have dynamic and irregular sizes (using LazyVGrid or LazyHGrid).

On macOS

On the Mac, the user expects full keyboard and mouse support.

  • A List on macOS automatically handles row selection with the mouse (click and drag or using the Shift key) and keyboard arrow navigation.
  • Implementing this multiple selection and keyboard navigation in a ScrollView requires hundreds of lines of additional Swift programming.

On watchOS

The Apple Watch has a tiny screen and limited resources.

  • List is almost always the default and recommended choice by Apple for navigation in watchOS.
  • ScrollView is primarily used for single-item detail screens (for example, reading a full email or a long article).

5. Comparison Table: ScrollView vs List in SwiftUI

To summarize the technical battle, here is a quick reference table for any iOS Developer:

Feature List ScrollView (with LazyVStack)
Visual Style Native (Settings, Mail, etc.) Completely Custom
Row Separators Automatic (can be hidden) None (you must create them)
Scroll Direction Primarily Vertical Vertical or Horizontal
Memory Management Lazy by default Requires LazyVStack or LazyHStack
Swipe to Delete (.onDelete) Natively supported Not natively supported
Reordering (.onMove) Natively supported Not natively supported
Keyboard Support (macOS) Excellent (Native navigation) Poor (Requires manual implementation)
Design Flexibility Rigid (Forced margins) Absolute (Total pixel control)

6. Practical Use Cases and Code

Let’s look at two clear Swift examples of when to use each in your Xcode project.

Practical Case 1: The Settings Menu (Use List)

If you are building the settings screen for your application, don’t reinvent the wheel. Use List.

import SwiftUI

struct SettingsView: View {
    @State private var notificationsEnabled = true
    
    var body: some View {
        NavigationStack {
            List {
                Section(header: Text("Account")) {
                    Text("Profile")
                    Text("Security")
                }
                
                Section(header: Text("Preferences")) {
                    Toggle("Notifications", isOn: $notificationsEnabled)
                    Text("Appearance")
                }
            }
            .listStyle(.insetGrouped) // Native iOS style
            .navigationTitle("Settings")
        }
    }
}

Practical Case 2: Instagram-Style Image Feed (Use ScrollView)

If you are designing a visual content feed, horizontal stories, or custom cards, a List will limit you too much with its internal padding. This is where ScrollView shines.

import SwiftUI

struct SocialFeedView: View {
    let imageIDs = Array(1...50)
    
    var body: some View {
        NavigationStack {
            // We disable scroll indicators for a cleaner look
            ScrollView(showsIndicators: false) {
                // We use LazyVStack for performance
                LazyVStack(spacing: 20) {
                    ForEach(imageIDs, id: \.self) { id in
                        PostCardView(postID: id)
                    }
                }
                .padding(.vertical)
            }
            .navigationTitle("My Feed")
        }
    }
}

// Simulated helper view
struct PostCardView: View {
    let postID: Int
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Circle().frame(width: 40, height: 40)
                Text("User \(postID)").bold()
                Spacer()
            }
            .padding(.horizontal)
            
            Rectangle()
                .fill(Color.gray.opacity(0.3))
                .frame(height: 300) // Simulates a large image
            
            Text("Fascinating description for post number \(postID).")
                .padding(.horizontal)
        }
    }
}

Conclusion

The ScrollView vs List in SwiftUI debate isn’t about which one is better, but rather which is the right tool for the job.

As a general rule in Swift programming:

  • Use List when data structure and interaction (delete, move, select) are more important than a radically unique visual design. It is your ally for productivity and native consistency.
  • Use ScrollView (combined with Lazy Stacks or Lazy Grids) when you need absolute control over every pixel of your design, building rich and visually unique interfaces where system separators or default margins would ruin the aesthetics.

If you have any questions about this article, please contact me and I will be happy to help you 🙂. You can contact me on my X profile or on my Instagram profile.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Editable List in SwiftUI

Next Article

Xcode Best Practices

Related Posts