列表样式 SwiftUI

问题描述

如何做如图所示的列表? (编辑:列表标题

enter image description here

这是我尝试过的 - 列表和表单:

NavigationView {
    vstack {
        List { // or Form here
            // Text("New") <- this also isn't it
            Section(header: Text("New")) {
                ForEach(... ... .indices,id: \.self) { index in
                    ...
                    ...
                }
            }
            
            Section(header: Text("Done")) {
                ForEach(...) { ...
                    ...
                }
            }
     }
}
            // On the pictures below - `List` on the left; `Form` on the right

enter image description here

enter image description here

解决方法

它看起来像一个带有部分的表单,可以配置部分标题(如下所示),行内容根据需要组成

demo

NavigationView {
    Form {
        Section(header: Text("New").bold().font(.title).foregroundColor(.black)) {

编辑:.textCase(.none) 允许源文本大写“Xxxx”

Section(header: Text("New").bold().font(.title).textCase(.none).foregroundColor(.black))
,

这是使用滚动视图、自定义部分和自定义列表项的解决方案。每个部分都有自己的标题、颜色和总数。每个项目都有一个图标、标签和开关。切换按钮可在部分总计中添加或删除项目的价格。

[Custom Section with]

struct CustomListSectionView: View {
    @ObservedObject var listSection : CustomListSection

    var body: some View {
        VStack(spacing: 0) {
            headerView
                .padding(8)
            listView
                .padding()
                .background(RoundedRectangle(cornerRadius: 25.0).foregroundColor(.white))
                .padding(.horizontal)
            footerView
                .padding(8)
        }
        .background(Color.secondary)
    }
    
    var headerView : some View {
        HStack {
            Text(listSection.header)
                .font(.system(size: 24,weight: .medium,design: .rounded))
                .foregroundColor(.primary)
            Spacer()
        }
    }
    
    var listView : some View {
        VStack(spacing: 3) {
            ForEach(Array(listSection.items.enumerated()),id: \.offset) { index,item in
                CustomListItemView(color: listSection.color).environmentObject(item)
                if index < listSection.items.count - 1 {
                    Divider()
                }
            }
        }
    }

    var footerView : some View {
        HStack {
            Spacer()
            Text("Sum:")
                .font(.system(size: 18,weight: .semibold,design: .rounded))
                .foregroundColor(.secondary)
            Text(CustomListSectionView.formatter().string(from: NSNumber(value: listSection.total)) ?? "-")
                .font(.system(size: 18,design: .rounded).monospacedDigit())
                .foregroundColor(.primary)
        }
    }
    
    static func formatter() -> NumberFormatter {
        let currencyFormatter = NumberFormatter()
        currencyFormatter.locale = Locale.current
        currencyFormatter.numberStyle = .currency
        currencyFormatter.usesGroupingSeparator = true
        return currencyFormatter
    }
}

struct CustomListItemView: View {
    @EnvironmentObject var listItem : CustomListItem
    let color : Color
    
    var body: some View {
        HStack {
            ZStack {
                Image(systemName: listItem.symbol)
                    .font(.headline)
                    .foregroundColor(.white.opacity(0.8))
                    .padding(6)
                    .background(Circle().foregroundColor(color))
            }
            .shadow(radius: 3)
            Text(listItem.name)
            Spacer()
            Button(action: { listItem.select.toggle() }) {
                Image(systemName: listItem.select ? "largecircle.fill.circle" : "circle")
                    .foregroundColor(.accentColor)
                    .padding(6)
            }.buttonStyle(PlainButtonStyle())
        }
    }
}

class CustomListSection: ObservableObject,Hashable {
    // hashable so id: \.self work in ForEach's
    let id = UUID()
    let header : String
    let color : Color
    @Published var items : [CustomListItem] = []
    @Published var total : Double = 0
    
    func add(_ name: String,_ symbol: String,_ amount: Double) {
        items.append(CustomListItem(name,symbol,amount,calc))
    }
    
    func calc() {
        total = items.map({ $0.price }).reduce(0,+)
    }
    
    init(_ header: String,_ color: Color) {
        self.header = header
        self.color = color
    }
    
    public func hash(into hasher: inout Hasher) {
            hasher.combine(id)
    }
    
    static func == (lhs: CustomListSection,rhs: CustomListSection) -> Bool {
        lhs.id == rhs.id
    }
}

class CustomListItem: ObservableObject,Hashable {
    @Published var select : Bool = false { didSet {
        withAnimation { calc() }
    } }
    let name : String
    let symbol : String
    @Published var amount : Double
    let calc : () -> Void

    init(_ name: String,_ amount: Double,_ calc: @escaping () -> Void) {
        self.name = name
        self.symbol = symbol
        self.amount = amount
        self.calc = calc
    }

    var price : Double { select ? amount : 0 }
    
    // hashable so id: \.self work in ForEach's
    let id = UUID()

    public func hash(into hasher: inout Hasher) {
            hasher.combine(id)
    }
    
    static func == (lhs: CustomListItem,rhs: CustomListItem) -> Bool {
        lhs.id == rhs.id
    }
}

struct CustomListSectionView_Previews: PreviewProvider {
    static var previews: some View {
        let section = CustomListSection("Section Name",.purple)
        section.add("Alpha Item","a.circle",1000)
        section.add("Delta Item","d.circle",100)
        section.add("Omega Item","z.circle",1)
        return CustomListSectionView(listSection: section)
    }
}