右侧带有部分索引的SwiftUI列表?

rya*_*nnn 11 uitableview ios swift swiftui

是否可以在右侧有一个带有索引的 List,就像下面 SwiftUI 中的示例一样?

例子

Far*_*ouK 12

看看Federico Zanetello 的这篇教程,它是一个 100% SwiftUI 解决方案。

\n

最终结果: \n在此输入图像描述

\n

完整代码(作者:Federico Zanetello):

\n
let database: [String: [String]] = [\n  "iPhone": [\n    "iPhone", "iPhone 3G", "iPhone 3GS", "iPhone 4", "iPhone 4S", "iPhone 5", "iPhone 5C", "iPhone 5S", "iPhone 6", "iPhone 6 Plus", "iPhone 6S", "iPhone 6S Plus", "iPhone SE", "iPhone 7", "iPhone 7 Plus", "iPhone 8", "iPhone 8 Plus", "iPhone X", "iPhone Xs", "iPhone Xs Max", "iPhone X\xca\x80", "iPhone 11", "iPhone 11 Pro", "iPhone 11 Pro Max", "iPhone SE 2"\n  ],\n  "iPad": [\n    "iPad", "iPad 2", "iPad 3", "iPad 4", "iPad 5", "iPad 6", "iPad 7", "iPad Air", "iPad Air 2", "iPad Air 3", "iPad Mini", "iPad Mini 2", "iPad Mini 3", "iPad Mini 4", "iPad Mini 5", "iPad Pro 9.7-inch", "iPad Pro 10.5-inch", "iPad Pro 11-inch", "iPad Pro 11-inch 2", "iPad Pro 12.9-inch", "iPad Pro 12.9-inch 2", "iPad Pro 12.9-inch 3", "iPad Pro 12.9-inch 4"\n  ],\n  "iPod": [\n    "iPod Touch", "iPod Touch 2", "iPod Touch 3", "iPod Touch 4", "iPod Touch 5", "iPod Touch 6"\n  ],\n  "Apple TV": [\n    "Apple TV 2", "Apple TV 3", "Apple TV 4", "Apple TV 4K"\n  ],\n  "Apple Watch": [\n    "Apple Watch", "Apple Watch Series 1", "Apple Watch Series 2", "Apple Watch Series 3", "Apple Watch Series 4", "Apple Watch Series 5"\n  ],\n  "HomePod": [\n    "HomePod"\n  ]\n]\n\nstruct HeaderView: View {\n  let title: String\n\n  var body: some View {\n    Text(title)\n      .font(.title)\n      .fontWeight(.bold)\n      .padding()\n      .frame(maxWidth: .infinity, alignment: .leading)\n  }\n}\n\nstruct RowView: View {\n  let text: String\n\n  var body: some View {\n    Text(text)\n      .padding()\n      .frame(maxWidth: .infinity, alignment: .leading)\n  }\n}\n\nstruct ContentView: View {\n  let devices: [String: [String]] = database\n\n  var body: some View {\n    ScrollViewReader { proxy in\n      ScrollView {\n        LazyVStack {\n          devicesList\n        }\n      }\n      .overlay(sectionIndexTitles(proxy: proxy))\n    }\n    .navigationBarTitle("Apple Devices")\n  }\n\n  var devicesList: some View {\n    ForEach(devices.sorted(by: { (lhs, rhs) -> Bool in\n      lhs.key < rhs.key\n    }), id: \\.key) { categoryName, devicesArray in\n      Section(\n        header: HeaderView(title: categoryName)\n      ) {\n        ForEach(devicesArray, id: \\.self) { name in\n          RowView(text: name)\n        }\n      }\n    }\n  }\n\n  func sectionIndexTitles(proxy: ScrollViewProxy) -> some View {\n    SectionIndexTitles(proxy: proxy, titles: devices.keys.sorted())\n      .frame(maxWidth: .infinity, alignment: .trailing)\n      .padding()\n  }\n}\n\nstruct SectionIndexTitles: View {\n  let proxy: ScrollViewProxy\n  let titles: [String]\n  @GestureState private var dragLocation: CGPoint = .zero\n\n  var body: some View {\n    VStack {\n      ForEach(titles, id: \\.self) { title in\n        SectionIndexTitle(image: sfSymbol(for: title))\n          .background(dragObserver(title: title))\n      }\n    }\n    .gesture(\n      DragGesture(minimumDistance: 0, coordinateSpace: .global)\n        .updating($dragLocation) { value, state, _ in\n          state = value.location\n        }\n    )\n  }\n\n  func dragObserver(title: String) -> some View {\n    GeometryReader { geometry in\n      dragObserver(geometry: geometry, title: title)\n    }\n  }\n\n  func dragObserver(geometry: GeometryProxy, title: String) -> some View {\n    if geometry.frame(in: .global).contains(dragLocation) {\n      DispatchQueue.main.async {\n        proxy.scrollTo(title, anchor: .center)\n      }\n    }\n    return Rectangle().fill(Color.clear)\n  }\n\n  func sfSymbol(for deviceCategory: String) -> Image {\n    let systemName: String\n    switch deviceCategory {\n    case "iPhone": systemName = "iphone"\n    case "iPad": systemName = "ipad"\n    case "iPod": systemName = "ipod"\n    case "Apple TV": systemName = "appletv"\n    case "Apple Watch": systemName = "applewatch"\n    case "HomePod": systemName = "homepod"\n    default: systemName = "xmark"\n    }\n    return Image(systemName: systemName)\n  }\n}\n\nstruct SectionIndexTitle: View {\n  let image: Image\n\n  var body: some View {\n    RoundedRectangle(cornerRadius: 8, style: .continuous)\n      .foregroundColor(Color.gray.opacity(0.1))\n      .frame(width: 40, height: 40)\n      .overlay(\n        image\n          .foregroundColor(.blue)\n      )\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Dir*_*ctX 5

我在 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不错的工作!看似简单。如果您过滤字母表列表以仅包含实际使用的字母,则可以消除因键入没有条目的字母而导致的任何滚动问题。这应该是公认的答案。 (2认同)
  • 真是干得好!我想知道是否有办法从 Obj-C UITableView 获取功能,我不必逐个按钮 = 逐个字母地单击“按钮”,但我可以按一次并拖动/移动其他字母以使其滚动。 (2认同)
  • @KarstenS。在这里检查我的答案:/sf/answers/4624818691/ (2认同)

NNi*_*ikN 3

我一直在寻找同一问题的解决方案,但目前我们可能拥有的唯一选择是用作UITableView视图。

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"]
        }
    }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述