【问题标题】:SwiftUI - How to add "letters sections" and alphabet jumper in a Form?SwiftUI - 如何在表单中添加“字母部分”和字母跳线?
【发布时间】:2020-12-07 16:03:56
【问题描述】:

如何制作一个表单,其中元素根据其首字母自动分成多个部分,并在右侧添加字母跳线以显示以所选字母开头的元素(就像联系人应用程序一样)?

我还注意到一个奇怪的事情,我不知道如何重新创建:并非所有字母都显示,其中一些显示为“•”。但是,当您点击它们时,它们无论如何都会将您带到相应的字母。我尝试在 ZStack 中使用 ScrollView(.vertical) 并将 .scrollTo(selection) 添加到按钮的操作中,但是 1) 它没有滚动到我想要的选择 2) 当我点击“•”时,就好像我在点击所有这些,因为他们都做了点击动画 3) 我无法按照我的意愿划分列表。 我有这个:

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)
                   }
                }
               }
           }
   }
}

但我想要这样:

【问题讨论】:

    标签: menu swiftui scrollview alphabet


    【解决方案1】:
    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()
        }
    }
    

    【讨论】:

    • NP 刚刚加了点东西
    • 太棒了!谢谢!
    【解决方案2】:

    为字母跳线添加上下滑动动作,这样我们就可以得到类似UILocalizedIndexCollation 的滑动效果,它适用于查看和添加模式,但删除,我猜是由于 SwiftUI 的 UI刷新机制。

    extension String {
        static var alphabeta: [String] {
            var chars = [String]()
            for char in "abcdefghijklmnopqrstuvwxyz#".uppercased() {
                chars.append(String(char))
            }
            return chars
        }
    }
    
    struct Document: View {
        @State var items = ["Alpha", "Ash", "Aman", "Alisia", "Beta", "Baum", "Bob", "Bike", "Beeber", "Beff", "Calipha", "Cask", "Calf", "Deamon", "Deaf", "Dog", "Silk", "Seal", "Tiger", "Tom", "Tan", "Tint", "Urshinabi", "Verizon", "Viber", "Vein", "Wallet", "Warren", "Webber", "Waiter", "Xeon", "Young", "Yoda", "Yoga", "Yoger", "Yellow", "Zeta"]
        var body: some View {
            ScrollViewReader { scrollView in
                HStack {
                    List {
                        ForEach(String.alphabeta, id: \.self){ alpha in
                            let subItems = items.filter({$0.starts(with: alpha)})
                            if !subItems.isEmpty {
                                Section(header: Text(alpha)) {
                                    ForEach(subItems, id: \.self) { item in
                                        Text(item)
                                    }.onDelete(perform: { offsets in
                                        items.remove(at: offsets.first!)
                                    })
                                }.id(alpha)
                            }
    
                        }
                    }
                    
                    VStack{
                        VStack {
                            SectionIndexTitles(proxy: scrollView, titles: retrieveSectionTitles()).font(.footnote)
                        }
                        .padding(.trailing, 10)
                    }
                }
            }
    //        .navigationBarTitleDisplayMode(.inline)
    //        .navigationBarHidden(true)
        }
        
        func retrieveSectionTitles() ->[String] {
            var titles = [String]()
            titles.append("@")
            for item in self.items {
                if !item.starts(with: titles.last!){
                    titles.append(String(item.first!))
                }
            }
            titles.remove(at: 0)
            if titles.count>1 && titles.first! == "#" {
                titles.append("#")
                titles.removeFirst(1)
            }
            return titles
        }
    }
    
    struct Document_Previews: PreviewProvider {
        static var previews: some View {
            Document()
        }
    }
    
    struct SectionIndexTitles: View {
        class IndexTitleState: ObservableObject {
            var currentTitleIndex = 0
            var titleSize: CGSize = .zero
        }
        
        let proxy: ScrollViewProxy
        let titles: [String]
        @GestureState private var dragLocation: CGPoint = .zero
        @StateObject var indexState = IndexTitleState()
        
        var body: some View {
            VStack {
                ForEach(titles, id: \.self) { title in
                    Text(title)
                        .foregroundColor(.blue)
                        .modifier(SizeModifier())
                        .onPreferenceChange(SizePreferenceKey.self) {
                            self.indexState.titleSize = $0
                        }
                        .onTapGesture {
                            proxy.scrollTo(title, anchor: .top)
                        }
                }
            }
            .gesture(
                DragGesture(minimumDistance: indexState.titleSize.height, coordinateSpace: .named(titles.first))
                    .updating($dragLocation) { value, state, _ in
                        state = value.location
                        scrollTo(location: state)
                    }
            )
        }
        
        private func scrollTo(location: CGPoint){
            if self.indexState.titleSize.height > 0{
                let index = Int(location.y / self.indexState.titleSize.height)
                if index >= 0 && index < titles.count {
                    if indexState.currentTitleIndex != index {
                        indexState.currentTitleIndex = index
                        print(titles[index])
                        DispatchQueue.main.async {
                            let impactMed = UIImpactFeedbackGenerator(style: .medium)
                                impactMed.impactOccurred()
    //                        withAnimation {
                                proxy.scrollTo(titles[indexState.currentTitleIndex], anchor: .top)
    //                        }
                        }
                    }
                }
            }
        }
        
    }
    
    struct SizePreferenceKey: PreferenceKey {
        static var defaultValue: CGSize = .zero
    
        static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
            value = nextValue()
        }
    }
    
    struct SizeModifier: ViewModifier {
        private var sizeView: some View {
            GeometryReader { geometry in
                Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size)
            }
        }
    
        func body(content: Content) -> some View {
            content.background(sizeView)
        }
    }
    

    【讨论】:

    • 已编辑:将@StateObject 添加到var indexState 以强制刷新跳线。
    • 非常感谢,这是我在互联网上找到的唯一一个实际使用 SwiftUI 并使拖动正常工作的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 2019-05-23
    • 1970-01-01
    • 2012-12-26
    相关资源
    最近更新 更多