SwiftUI List with Section Index on right hand side?

前端 未结 4 1050
旧时难觅i
旧时难觅i 2021-02-03 13:23

Is it possible to have a List with an index on the right hand side, like the example below in SwiftUI?

\"Example\"<

相关标签:
4条回答
  • 2021-02-03 13:42

    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()
    
    0 讨论(0)
  • 2021-02-03 13:44

    I was looking for a solution to the same question , but it currently the only option that we might have right now is using UITableView as View.

    import SwiftUI
    import UIKit
    
    struct TableView: UIViewRepresentable {
    
        func makeUIView(context: Context) -> UITableView {
            let tableView =  UITableView(frame: .zero, style: .plain)
            tableView.delegate = context.coordinator
            tableView.dataSource  = context.coordinator
            return tableView
        }
    
        func updateUIView(_ uiView: UITableView, context: Context) {
    
        }
    
        func makeCoordinator() -> Coordinator {
            Coordinator()
        }
    
        final class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
            func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                2
            }
    
            func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cellId = "cellIdentifier"
                let cell = tableView.dequeueReusableCell(withIdentifier: cellId) ?? UITableViewCell(style: .default, reuseIdentifier: cellId)
    
                cell.textLabel?.text = "\(indexPath)"
                return cell
            }
    
            func sectionIndexTitles(for tableView: UITableView) -> [String]? {
                ["a", "b"]
            }
        }
    

    0 讨论(0)
  • 2021-02-03 13:54

    I did this in SwiftUI

        //
    //  Contacts.swift
    //  TestCalendar
    //
    //  Created by Christopher Riner on 9/11/20.
    //
    
    import SwiftUI
    
    struct Contact: Identifiable, Comparable {
        static func < (lhs: Contact, rhs: Contact) -> Bool {
            return (lhs.lastName, lhs.firstName) < (rhs.lastName, rhs.firstName)
        }
        
        var id = UUID()
        let firstName: String
        let lastName: String
    }
    
    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"]
    
    struct Contacts: View {
        @State private var searchText = ""
        
        var contacts = [Contact]()
        
        var body: some View {
            VStack {
                ScrollViewReader { scrollProxy in
                    ZStack {
                        List {
                            SearchBar(searchText: $searchText)
                                .padding(EdgeInsets(top: 0, leading: -20, bottom: 0, trailing: -20))
                            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())
                        .resignKeyboardOnDragGesture()
                       
                        VStack {
                            ForEach(alphabet, id: \.self) { letter in
                                HStack {
                                    Spacer()
                                    Button(action: {
                                        print("letter = \(letter)")
                                        //need to figure out if there is a name in this section before I allow scrollto or it will crash
                                        if contacts.first(where: { $0.lastName.prefix(1) == letter }) != nil {
                                            withAnimation {
                                                scrollProxy.scrollTo(letter)
                                            }
                                        }
                                    }, label: {
                                        Text(letter)
                                            .font(.system(size: 12))
                                            .padding(.trailing, 7)
                                    })
                                }
                            }
                        }
                    }
                }
            }
        }
        
        init() {
            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"))
            contacts.sort()
        }
    }
    
    struct Contacts_Previews: PreviewProvider {
        static var previews: some View {
            Contacts()
        }
    }
    
    0 讨论(0)
  • 2021-02-03 13:54

    I've made a couple of changes to @Mozahler's and @DirectX's code, refining the result.

    1. I didn't want the main list to include headers with no content, so in the implementation the line under List { becomes:

      ForEach(indexes, id: \.self) { letter in
      

      rather than

      ForEach(alphabet, id: \.self) { letter in
      
    2. Setting a background and uniform width for the index column sets it off from any background and unifies the result:

      Text(letter)
          .frame(width: 16)
          .foregroundColor(Constants.color.textColor)
          .background(Color.secondary.opacity(0.5))
          .font(Constants.font.customFootnoteFont)
          .padding(.trailing, 7)
      
    0 讨论(0)
提交回复
热议问题