Is it possible to have a List with an index on the right hand side, like the example below in SwiftUI?
<
See the solution provided on this page by DirectX and please consider giving it an upvote. It is the correct answer.
I've taken his View and created a ViewModifier you can use with any view that consists of a SwiftUI List with Sections (tableview).
Just be sure to provide a list of header (section) titles that corresponds to the headers in the view you are adding the index to. Click on the letter to scroll to that section of the list. Notice that I only provide the indexes I can actually scroll to when calling the view modifier.
Use like any view modifier:
SimpleDemoView().modifier(VerticalIndex(indexableList: contacts))
Here's the code for the modifier:
struct VerticalIndex: ViewModifier {
let indexableList: [String]
func body(content: Content) -> some View {
var body: some View {
ScrollViewReader { scrollProxy in
ZStack {
content
VStack {
ForEach(indexableList, id: \.self) { letter in
HStack {
Spacer()
Button(action: {
withAnimation {
scrollProxy.scrollTo(letter)
}
}, label: {
Text(letter)
.font(.system(size: 12))
.padding(.trailing, 7)
})
}
}
}
}
}
}
return body
}
}
Here's what it looks like using the sample provided by DirectX:
For completeness, here's code to reproduce the display:
struct SimpleDemo_Previews: PreviewProvider {
static var previews: some View {
SimpleDemoView().modifier(VerticalIndex(indexableList: contacts))
}
}
struct SimpleDemoView: View {
var body: some View {
List {
ForEach(alphabet, id: \.self) { letter in
Section(header: Text(letter).id(letter)) {
ForEach(contacts.filter({ (contact) -> Bool in
contact.lastName.prefix(1) == letter
})) { contact in
HStack {
Image(systemName: "person.circle.fill").font(.largeTitle).padding(.trailing, 5)
Text(contact.firstName)
Text(contact.lastName)
}
}
}
}
}
.navigationTitle("Contacts")
.listStyle(PlainListStyle())
}
}
Here's the sample data used to provide the demo (modified from DirectX's solution):
let alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] //swiftlint:disable comma
let contacts: [Contact] = {
var contacts = [Contact]()
contacts.append(Contact(firstName: "Chris", lastName: "Ryan"))
contacts.append(Contact(firstName: "Allyson", lastName: "Ryan"))
contacts.append(Contact(firstName: "Jonathan", lastName: "Ryan"))
contacts.append(Contact(firstName: "Brendan", lastName: "Ryaan"))
contacts.append(Contact(firstName: "Jaxon", lastName: "Riner"))
contacts.append(Contact(firstName: "Leif", lastName: "Adams"))
contacts.append(Contact(firstName: "Frank", lastName: "Conors"))
contacts.append(Contact(firstName: "Allyssa", lastName: "Bishop"))
contacts.append(Contact(firstName: "Justin", lastName: "Bishop"))
contacts.append(Contact(firstName: "Johnny", lastName: "Appleseed"))
contacts.append(Contact(firstName: "George", lastName: "Washingotn"))
contacts.append(Contact(firstName: "Abraham", lastName: "Lincoln"))
contacts.append(Contact(firstName: "Steve", lastName: "Jobs"))
contacts.append(Contact(firstName: "Steve", lastName: "Woz"))
contacts.append(Contact(firstName: "Bill", lastName: "Gates"))
contacts.append(Contact(firstName: "Donald", lastName: "Trump"))
contacts.append(Contact(firstName: "Darth", lastName: "Vader"))
contacts.append(Contact(firstName: "Clark", lastName: "Kent"))
contacts.append(Contact(firstName: "Bruce", lastName: "Wayne"))
contacts.append(Contact(firstName: "John", lastName: "Doe"))
contacts.append(Contact(firstName: "Jane", lastName: "Doe"))
return contacts.sorted()
}()
let indexes = Array(Set(contacts.compactMap({ String($0.lastName.prefix(1)) }))).sorted()