What’s New in SwiftUI Lists for iOS 14?
Nested or expandable lists and a brand new view modifier
Lists are a vital component of every application, and SwiftUI List only simplified the way in which we populate our data, typically by conforming to the Identifiable
protocol — which ensures each row is unique for dynamic items.
WWDC 2020 paved the way for a few interesting updates to SwiftUI Lists in iOS 14. Let’s look at them.
SwiftUI New Nested Lists Using ‘.children’ Key Path
Up until now, creating nested or expandable lists in SwiftUI required nesting ForEach
loops within each other.
The latest iteration of SwiftUI makes building nested lists a whole lot easier by introducing a .children
key-path argument in its initialiser. In doing so, it allows us to create recursive outlined SwiftUI lists that show up in a system-standard styling way on iOS, iPadOS, and macOS.
Let’s set up a data structure for our new SwiftUI outlined lists.
struct DataModel: Identifiable{ | |
var id = UUID() | |
var name: String | |
var icon: String | |
var children : [DataModel]? | |
} |
Now, you could populate data using JSON or any assets file, but for the sake of simplicity, we’ll set a few dummy strings and SF symbols. Let’s do that by conforming our class to ObservableObject
— a type of object that announces changes through a publisher.
public class DataFetcher: ObservableObject { | |
@Published var dataModel = [DataModel]() | |
init() { | |
var nestedMenu : [DataModel] = [] | |
for i in 0...5 { | |
nestedMenu.append(DataModel(name: "Sub Item \(i)", icon: "calendar")) | |
} | |
self.dataModel = [ | |
DataModel(name: "Item 1", icon: "book.circle", children: [DataModel(name: "Sub Item 1", icon: "tv.fill", children: [DataModel(name: "Double Sub Item 1", icon: "headphones")])]), | |
DataModel(name: "Item 2", icon: "printer"), | |
DataModel(name: "Item 3", icon: "wifi"), | |
DataModel(name: "Item 4", icon: "person.circle", children: nestedMenu), | |
DataModel(name: "Item 5", icon: "hand.raised", children: nestedMenu), | |
DataModel(name: "Item 6", icon: "zzz", children: nestedMenu) | |
] | |
} | |
} |
Now that our data source is ready, let’s plug it into our SwiftUI’s View
.
struct ContentView: View { | |
@ObservedObject var fetcher = DataFetcher() | |
var body: some View { | |
List(fetcher.dataModel, children: \.children){ | |
item in | |
HStack{ | |
Image(systemName: item.icon) | |
Text(item.name) | |
} | |
} | |
} | |
} |
The children key path ensures the list builds out its content in a recursive way (provided the data model is valid).
New ‘listStyle’ Modifier
SwiftUI List provides a listStyle()
to customise the look and feel.
You can pass GroupedListStyle()
to get an iOS 12–like grouping or InsetGroupedListStyle()
to get the iOS 13 and above look with rounded corners.
Here’s a glimpse of how the latter one looks with the above SwiftUI list:
There’s also a SidebarListStyle()
that’s useful in iPadOS and macOS, especially for document-based apps that leverage sidebars.
SwiftUI Outline Lists With NavigationLink
When using the SidebarListStyle
and embedding the List in a NavigationView
, only the rows, with no child are navigatable to NavigationLink’s destination view. SwiftUI takes care of this automatically on iPadOS and macOS as shown below:
But running the same on an iOS device, it’s a little buggy(at the time of writing). Or perhaps, it’s not intended to use in that way:
As you can see, on an iOS device, it shows double disclosures. And despite having child lists, NavigationLink
is working which ideally shouldn’t be the case. Hopefully, this bug gets fixed soon.
Conclusion
We saw two cool new updates from the second iteration of SwiftUI that works on iOS 14 and Xcode 12 and above.
That’s it for this one. Thanks for reading.