Search code examples
menuswiftuiscrollviewalphabet

SwiftUI - How to add "letters sections" and alphabet jumper in a Form?


How can I make a Form in which the elements are automatically divided into sections based on their first letter and add to the right the alphabet jumper to show the elements starting by the selected letter (just like the Contacts app)?

I also noted a strange thing that I have no idea how to recreate: not all letters are shown, some of them appear as "•". However, when you tap on them, they take you to the corresponding letter anyway. I tried using a ScrollView(.vertical) inside a ZStack and adding .scrollTo(selection) into the action of the Buttons, however 1) It didn't scroll to the selection I wanted 2) When I tapped on the "•", it was as if I was tapping on all of them because they all did the tapping animation 3) I wasn't able to divide the List as I wanted to. I have this:

import SwiftUI

struct ContentView: View {

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"]
let values = ["Avalue", "Bvalue", "Cvalue", "Dvalue"]

var body: some View {
           ScrollViewReader{ scrollviewr in
               ZStack {
                   ScrollView(.vertical) {
                       VStack {
                           ForEach(alphabet, id: \.self) { letters in
                               Button(letters){
                                   withAnimation {
                                    scrollviewr.scrollTo(letters)
                                   }
                               }
                           }
                       }
                   }.offset(x: 180, y: 120)

                VStack {
                    
                ForEach(values, id: \.self){ vals in
                               Text(vals).id(vals)
                   }
                }
               }
           }
   }
}

But I'd want it like this:

enter image description here


Solution

  • import SwiftUI
    
    struct AlphabetSort2: View {
        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"]
        let values = ["Avalue", "Bvalue", "Cvalue", "Dvalue", "Mvalue", "Zvalue"]
        var body: some View {
            ScrollView {
                ScrollViewReader { value in
                    ZStack{
                        List{
                            ForEach(alphabet, id: \.self) { letter in
                                Section(header: Text(letter)) {
                                    ForEach(values.filter { $0.hasPrefix(letter) }, id: \.self) { vals in
                                        Text(vals).id(vals)
                                    }
                                }.id(letter)
                            }
                        }
                        HStack{
                            Spacer()
                            VStack {
                                ForEach(0..<alphabet.count, id: \.self) { idx in
                                    Button(action: {
                                        withAnimation {
                                            value.scrollTo(alphabet[idx])
                                        }
                                    }, label: {
                                        Text(idx % 2 == 0 ? alphabet[idx] : "\u{2022}")
                                    })
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    struct AlphabetSort2_Previews: PreviewProvider {
        static var previews: some View {
            AlphabetSort2()
        }
    }