首页 > 解决方案 > 如何使用 SwiftUI 制作单选列表

问题描述

我正在创建单个选择列表以在我的应用程序的不同位置使用。


问题:

有没有我不知道的简单解决方案?

如果没有,我该如何完成当前的解决方案?


我的目标:

  1. 始终只选择一项或一项或一项未选择的列表(取决于配置)
  2. 透明背景
  3. 在项目选择上 - 执行通过 init() 方法设置为参数的操作。(该操作需要选定的项目信息。)
  4. 以编程方式更改列表数据并将选择重置为第一项

我当前的解决方案如下所示: 选择第二项的列表视图

我不能使用 Picker,因为外部操作(目标编号 3)非常耗时。所以我认为它不会顺利进行。很可能在 SwiftUI 中有解决我的问题的方法,但是我错过了它,因为我是 swift 新手,或者据我所知,SwiftUI 中的所有内容都不能完美运行,例如:List 的透明背景(这就是为什么我需要清除init()) 中的背景。

所以我开始自己实现选择,并停在这里:单击项目(按钮)时,我当前的解决方案不会更新视图。(仅外出并返回页面更新视图)。并且仍然可以选择多个项目。

import SwiftUI

struct ModuleList: View {
    var modules: [Module] = []
    @Binding var selectionKeeper: Int
    var Action: () -> Void


    init(list: [Module], selection: Binding<Int>, action: @escaping () -> Void) {
        UITableView.appearance().backgroundColor = .clear
        self.modules = list
        self._selectionKeeper = selection
        self.Action = action
    }

    var body: some View {
        List(){
            ForEach(0..<modules.count) { i in
                ModuleCell(module: self.modules[i], action: { self.changeSelection(index: i) })
                }
        }.background(Constants.colorTransparent)
    }

    func changeSelection(index: Int){
        modules[selectionKeeper].isSelected =  false
        modules[index].isSelected = true
        selectionKeeper = index
        self.Action()
    }
}

struct ModuleCell: View {
    var module: Module
    var Action: () -> Void

    init(module: Module, action: @escaping () -> Void) {
        UITableViewCell.appearance().backgroundColor = .clear
        self.module = module
        self.Action = action
    }

    var body: some View {
        Button(module.name, action: {
            self.Action()
        })
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
            .modifier(Constants.CellSelection(isSelected: module.isSelected))
    }
}

class Module: Identifiable {
    var id = UUID()
    var name: String = ""
    var isSelected: Bool = false
    var address: Int

    init(name: String, address: Int){
        self.name = name
        self.address = address
    }
}

let testLines = [
    Module(name: "Line1", address: 1),
    Module(name: "Line2", address: 3),
    Module(name: "Line3", address: 5),
    Module(name: "Line4", address: 6),
    Module(name: "Line5", address: 7),
    Module(name: "Line6", address: 8),
    Module(name: "Line7", address: 12),
    Module(name: "Line8", address: 14),
    Module(name: "Line9", address: 11),
    Module(name: "Line10", address: 9),
    Module(name: "Line11", address: 22)
]

测试一些想法:

尝试在 ModuleList 中添加 (isSelected: Bool) 的 @State 数组并将其绑定到可能更新视图的 Module isSelected 参数...但失败然后在 init() 中填充此数组,因为 @State 数组参数在 .append( )... 也许添加函数 setList 可以解决这个问题,而我的目标是 Nr。4. 但我不确定这是否会真正更新我的观点。

struct ModuleList: View {
     var modules: [Module] = []
     @State var selections: [Bool] = []


     init(list: [String]) {
         UITableView.appearance().backgroundColor = .clear
         selections = [Bool] (repeating: false, count: list.count) // stays empty
         let test = [Bool] (repeating: false, count: list.count) // testing: works as it should
         selections = test
         for i in 0..<test.count { // for i in 0..<selections.count {
             selections.append(false)
             modules.append(Module(name: list[i], isSelected: $selections[i])) // Error selections is empty
         }
     }

     var body: some View {
         List{
             ForEach(0..<modules.count) { i in
                 ModuleCell(module: self.modules[i], action: { self.changeSelection(index: i) })
                 }
         }.background(Constants.colorTransparent)
     }
     func changeSelection(index: Int){
         modules[index].isSelected = true
     }
 }

 struct ModuleCell: View {
     var module: Module
     var Method: () -> Void

     init(module: Module, action: @escaping () -> Void) {
         UITableViewCell.appearance().backgroundColor = .clear
         self.module = module
         self.Method = action
     }

     var body: some View {
         Button(module.name, action: {
             self.Method()
         })
             .frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
             .modifier(Constants.CellSelection(isSelected: module.isSelected))
     }
 }

 struct Module: Identifiable {
     var id = UUID()
     var name: String = ""
     @Binding var isSelected: Bool

     init(name: String, isSelected: Binding<Bool>){
         self.name = name
         self._isSelected = isSelected
     }
 }

 let testLines = ["Line1","Line2","Line3","Line4"
 ]

标签: swiftlistswiftuiselectionios13

解决方案


实现这一点的最简单方法是在包含带有选择的列表的视图中使用@State,并将其作为@Binding 传递给单元格:

struct SelectionView: View {

    let fruit = ["apples", "pears", "bananas", "pineapples"]
    @State var selectedFruit: String? = nil

    var body: some View {
        List {
            ForEach(fruit, id: \.self) { item in
                SelectionCell(fruit: item, selectedFruit: self.$selectedFruit)
            }
        }
    }
}

struct SelectionCell: View {

    let fruit: String
    @Binding var selectedFruit: String?

    var body: some View {
        HStack {
            Text(fruit)
            Spacer()
            if fruit == selectedFruit {
                Image(systemName: "checkmark")
                    .foregroundColor(.accentColor)
            }
        }   .onTapGesture {
                self.selectedFruit = self.fruit
            }
    }
}

推荐阅读