List and LazyVStack are two fundamental lazy containers in the SwiftUI ecosystem. They provide developers with strong assistance when it comes to displaying massive quantities of data. Developers aren’t always sure which one to choose because of how similarly they behave in different contexts. In order to assist you in making a more informed choice, this article will examine the features and benefits of these two components. Let’s understand List and LazyVStack in SwiftUI from A to Z.
Take Note
This article mostly mentions LazyVStack in relation to its combined use with ScrollView and ForEach, which are often used to dynamically supply data. It’s important to note that the characteristics of LazyVStack that are covered here may be applied to LazyHStack and, to a lesser extent, to other containers in the Lazy family. Rather than getting into the nitty-gritty of API use, we’ll be comparing and discussing things at a high level.
List and LazyVStack have both contributed to SwiftUI but in different ways and at separate points in time. As an old hand at lazy containers since SwiftUI 1.0, List was not only the only official lazy component but also the one that laid the groundwork for how many others loaded data in later versions. On the other hand, LazyVStack and other lazy containers like LazyHStack made their appearance in the second year. Despite sharing certain commonalities in their output, the two parts’ fundamental designs couldn’t be more distinct.
At its core, List is Apple’s astute encapsulation of UIKit and AppKit components. It used to be that it used the UITableView from iOS 13–iOS 15, but with iOS 16 it switched to the more versatile UICollectionView. On the other hand, containers from the Lazy+ family, like LazyVStack, are native SwiftUI implementations. Their internal workings are not dependent on any particular UIKit or AppKit components.
Their dissimilar performance in several critical areas stems from this underlying implementation variance, which is more than simply a matter of technique. Due to their different underlying architectures, List and LazyVStack display various features in terms of efficiency, feature richness, customization flexibility, and layout logic. For developers, knowing this is essential when selecting and using these two components.
Just like its cousin VStack, LazyVStack prioritizes layout as its primary purpose. It arranges subviews in a logical fashion without randomly applying additional effects, as it is a pure layout container in SwiftUI and follows all of the predefined layout constraints. Developers have the most flexibility with this basic design.
List, on the other hand, is based on a very different design concept. As a user interface component, it offers more than simply a container. Developers may easily swap between various styles using listStyle since List comes with many predefined visual layouts. Nevertheless, List’s benefits extend beyond only its visual impacts; it also provides a range of distinctive interaction features:
List is definitely the way to go when you need functionality like swipe menus or the ability to remove and rearrange items, just like the system. It is very well-optimized for the whole Apple ecosystem and has very little code. Particularly for newcomers or those working on prototypes, List’s robust building methods—which include data binding and integration with ForEach—greatly improve development productivity.
But there are restrictions brought on by List’s predefined settings as well. As of iOS 18, Apple still hasn’t made it possible to fully customize List styles, even though SwiftUI has added more predefined themes for List. While LazyVStack does not come with any pre-set settings, it is essentially a blank canvas onto which developers may unleash their imaginations.
To sum up, Apple has a very different stance on these two components: While LazyVStack is only a layout tool, List is a versatile container with preset styles and behaviors.
As a lazy container in SwiftUI, LazyVStack has its own set of layout-specific features, most notably for dealing with subview heights. Unlike VStack, LazyVStack utilizes the ideal sizes of subviews when their height is unknown. This characteristic stands out when put into action:
Instead of occupying the available area as it would in a VStack, the Rectangle will only appear as a 10-height rectangle in the code above. This is the default optimal size for shapes.
The algorithm for determining size is in sync with ScrollView with respect to the scrollable direction’s layout. Rectangles retain their 10-inch height even when nestled in VStacks:
Thanks to LazyVStack’s status as a pure layout container, developers have a lot of leeway to accurately recreate design styles. However, there are a number of variables that affect how List appears, such as the chosen style, the container environment, and the OS platform. Due to its formulaic arrangement and lack of developer control over its style, List is not very user-friendly.
Using Apple’s specific view decorators is the main way to change List’s look and behavior. Newer versions of SwiftUI improve these decorators, but older versions of the language may make it hard to do more complicated layouts or need workaround approaches.
List isn’t perfect at dealing with dynamic changes to row height because of implementation incompatibilities. To illustrate:
Use caution when dealing with List in situations where the height might vary dynamically.
Additionally, List imposes limitations on the types of subview (row) transition animations. Typically, LazyVStack offers more flexibility and higher performance for situations that call for unique animations and transition effects.
Some devs aren’t happy with how List is implemented because they think it doesn’t fully meet their needs. This makes them think about wrapping UIKit/AppKit components themselves to make better options. Apple has put a lot of work into making List compatible with several platforms and tailored to certain purposes, so although this technique may suit some specialized needs better, it shouldn’t be overlooked.
Context awareness is a crucial yet hidden technique in SwiftUI. Depending on the current situation, official containers and components may automatically change their display style because of their clever environmental sensing capabilities. List has many benefits over LazyVStack, one of which is this special functionality. List is compatible with navigation containers without any issues:
Not only do these capabilities make development much easier, but they also make it easier to interact with the platform than LazyVStack. The problem is that this understanding of context and default action isn’t always problem-free:
In conclusion, List is clearly the superior option for developers who want to make full use of the autonomous sensing capabilities of system components in order to design interfaces that adhere to the system style. In addition to facilitating more code reuse between platforms, it gives consumers a more natural interactive experience.
Apple has introduced a slew of new APIs to SwiftUI that greatly improve scroll control capabilities. Nevertheless, ScrollView is the primary goal of these new capabilities, whereas List is significantly behind in this regard. List only officially supports ScrollViewReader as a scroll control technique at the moment.
Still, developers have an option with List as its core implementation is built on established UIKit components. Developers may get access to the APIs of underlying UIKit components directly with third-party libraries like SwiftUI-Introspect, which opens up additional control choices. You may customize display styles and utilize this way for scroll control as well.
Even so, with iOS 17, the ScrollView + LazyVStack combo offers far better scroll control capabilities and is much more convenient than List. When it comes to precise subview scrolling or modifying visual effects based on subview placements, LazyVStack stands out due to its inherent advantages in layout flexibility and animation support.
Despite SwiftUI’s six-year existence, List and LazyVStack might still need some work when it comes to processing massive datasets. Due to variations in underlying implementations, both show different performance characteristics even with medium-sized datasets.
By preserving a full container height (the sum of the subview heights + spacing), LazyVStack effectively becomes a VStack with lazy loading capabilities. To accomplish lazy loading, it uses a mechanism for dynamically estimating height, which takes into account the number and height of subviews close to the viewable region to determine the total height. There are two major problems that result from this mechanism:
List, on the other hand, does not keep track of total content height at the SwiftUI level. It greatly improves the performance of scrolling and jumping by automatically selecting the relevant subviews for instantiation and height computation during quick scrolling or long hops.
When utilizing the id modifier in List, subviews may not be able to retain their slow loading capabilities. However, this is not an issue with LazyVStack. For this reason, you shouldn’t use the id modifier as a scroll control tag if the data type you’re giving as a List source is compliant with both the Identifiable and Hashable protocols. In most cases, List outperforms LazyVStack when dealing with the same quantity of data.
“Everything exists for a reason” is a concept that is well-reflected in the design philosophies of List and LazyVStack. Each of these two components offers something special to SwiftUI developers, and they complement one another well. Several important elements must be carefully considered by developers before deciding which one to use:
Performance: Cases involving large leaps and noticeable variations in subview heights need extra care.
Scroll Control: Accuracy and the capacity to identify the position of subviews.
Layout Flexibility: Being able to adjust to intricate user interface designs while being compatible with transitions and animations.
Preset Functionality: The need to use the system’s built-in functionality.
Compatibility across multiple Apple platforms: How well it works in different Apple environments.
Cooperation with Other SwiftUI Elements: Essentially for data binding and navigation.
The ideal option often varies according to the unique needs of the project and the possible outcomes of its growth; there is no universally applicable answer. When developers have a thorough grasp of the pros and cons of these components, they will be more equipped to tackle unique problems.
In reality, there can be instances when you need both, or you might want to utilize each one on its own page to make the most of its qualities. Adapting the selection and implementation of these tools to meet the demands of individual applications, user groups, and performance standards is crucial.
You should expect these components’ features and use cases to vary as SwiftUI develops further. To keep SwiftUI development efficient and innovative, it is important to be educated about new features and best practices.