如何使用SwiftUI 5进行单选列表

问题描述 投票:0回答:1

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


问题:

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

如果没有。如何完成当前的解决方案?


我的目标:

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

我当前的解决方案如下:List view with second item selected

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

所以我开始自己实施选择,并在此处停止:当单击item(Button)时,我当前的解决方案不更新视图。 (仅外出并返回页面更新视图)。而且仍然可以选择多个项目。

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数组并将其绑定到MIGHT更新视图的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"
 ]
list selection swiftui ios13
1个回答
0
投票

选择

SwiftUI当前没有内置的方法来选择列表的一行并相应地更改其外观。但是您实际上非常接近您的答案。实际上,您的选择实际上已经在起作用,但是没有任何使用。

为了说明,请在ModuleCell(...)中的ForEach之后添加以下行:

.background(i == self.selectionKeeper ? Color.red : nil)

换句话说,“如果我当前行(i)与selectionKeeper中存储的值匹配,则将单元格涂成红色,否则,使用默认颜色。”您会看到,当您点击不同的行时,红色跟随您的点击,表明底层的选择实际上正在改变。

取消选择

如果要启用取消选择,可以传入Binding<Int?>作为选择,并在点击当前选定的行时将其设置为nil

struct ModuleList: View {
    var modules: [Module] = []
    // this is new ------------------v
    @Binding var selectionKeeper: Int?
    var Action: () -> Void

    // this is new ----------------------------v
    init(list: [Module], selection: Binding<Int?>, action: @escaping () -> Void) {

    ...

    func changeSelection(index: Int){
        if selectionKeeper != index {
            selectionKeeper = index
        } else {
            selectionKeeper = nil
        }
        self.Action()
    }
}

去重复状态和关注点分离

从结构上讲,在使用像SwiftUI这样的声明性UI框架时,您真的想要一个真实的来源,并将视图与模型完全分开。目前,您有重复的状态-selectionKeeper中的ModuleListisSelected中的Module都跟踪是否选择了给定的模块。

此外,isSelected实际上应该是视图(ModuleCell)的属性,而不是模型(Module)的属性,因为它与视图的外观有关,而不是每个模块的固有数据。] >

因此,您的ModuleCell应该看起来像这样:

struct ModuleCell: View {
    var module: Module
    var isSelected: Bool // Added this
    var Action: () -> Void

    // Added this -------v
    init(module: Module, isSelected: Bool, action: @escaping () -> Void) {
        UITableViewCell.appearance().backgroundColor = .clear
        self.module = module
        self.isSelected = isSelected  // Added this
        self.Action = action
    }

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

并且您的ForEach看起来像

ForEach(0..<modules.count) { i in
    ModuleCell(module: self.modules[i],
               isSelected: i == self.selectionKeeper,
               action: { self.changeSelection(index: i) })
}
© www.soinside.com 2019 - 2024. All rights reserved.