【问题标题】:associating enum cases will class name关联枚举案例将类名
【发布时间】:2019-05-19 22:57:03
【问题描述】:

说明

我有与类名对应的案例名称的枚举。我使用枚举来填充 UITableView 的部分/行。当用户选择一行时,我想实例化相应的类并实例化该类的一个对象。

// example enum:
enum BodiceEnum: String, CaseIterable {
    case AddBraCups
    case AddOrRemoveBoning
    // other cases hidden
    }

// example classes:
class AddBraCups { // implementation hidden }
class AddOrRemoveBoning { // implementation hidden}

附加上下文

我制作了一个查找表,将“Section”枚举案例连接到其对应的详细枚举案例:

var alterationsLookupTable: [(String,[Any])] = [
    ("Bodice",BodiceEnum.allCases),
    ("Neckline",NecklineEnum.allCases),
    ("Sides",SidesEnum.allCases),
    ("Sleeves or Straps",SleevesOrStrapsEnum.allCases),
    ("Back of Dress",BackOfDressEnum.allCases),
    ("Seams",SeamsEnum.allCases),
    ("Hem",HemEnum.allCases),
    ("Skirt",SkirtEnum.allCases),
    ("Veils",VeilsEnum.allCases),
    ("Prom - Straps",PromStrapsEnum.allCases),
    ("Prom - Take in/out",PromTakeInOrOutEnum.allCases),
    ("Prom - Hem",PromHemEnum.allCases),
    ("Tux",TuxEnum.allCases),
]

当前的 UITableView 部分对应于这个 alterationsLookupTable 数组中的一个索引。

一旦获得正确的部分类型,我就会切换该部分的相应枚举案例。我正在切换 itemsTuple.0,然后使用当前 indexPath.row 值作为 itemsTuple.1 中的索引

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        //var alterationDetailsArray: [String]()
        let section = indexPath.section
        var cellText: String = ""
        let itemTuple = alterationsLookupTable[section]
        switch itemTuple.0 {
        case "Bodice":
            let items = itemTuple.1 as! [BodiceEnum]
            cellText = items[indexPath.row].readable
        case "Neckline":
             let items = itemTuple.1 as! [NecklineEnum]
            cellText = items[indexPath.row].readable
        case "Sides":
            let items = itemTuple.1 as! [SidesEnum]
            cellText = items[indexPath.row].readable
        case "Sleeves or Straps":
            let items = itemTuple.1 as! [SleevesOrStrapsEnum]
            cellText = items[indexPath.row].readable
        case "Back of Dress":
            let items = itemTuple.1 as! [BackOfDressEnum]
            cellText = items[indexPath.row].readable
        case "Seams":
            let items = itemTuple.1 as! [SeamsEnum]
            cellText = items[indexPath.row].readable
        case "Hem":
            let items = itemTuple.1 as! [HemEnum]
            cellText = items[indexPath.row].readable
        case "Skirt":
            let items = itemTuple.1 as! [SkirtEnum]
            cellText = items[indexPath.row].readable
        case "Veils":
            let items = itemTuple.1 as! [VeilsEnum]
            cellText = items[indexPath.row].readable
        case "Prom - Straps":
            let items = itemTuple.1 as! [PromStrapsEnum]
            cellText = items[indexPath.row].readable
        case "Prom - Take in/out":
            let items = itemTuple.1 as! [PromTakeInOrOutEnum]
            cellText = items[indexPath.row].readable
        case "Prom - Hem":
            let items = itemTuple.1 as! [PromHemEnum]
            cellText = items[indexPath.row].readable
        case "Tux":
            let items = itemTuple.1 as! [TuxEnum]
            cellText = items[indexPath.row].readable
        default:
            cellText = "not valid cell text"
            
        }
        cell.textLabel?.text = cellText
        return cell
    }
}

我一直在查看this,但我似乎无法让它发挥作用。我对这个问题的(一点)理解是 Swift 的类型安全。我猜有 Swifty(惯用的)方法可以实现这一点。

更新 2:

这是一个 Alteration Section 的示例 —> Hem 及其子类之一 —> AddHemLace

class Hem : Codable {
    var minCost: Float
    var maxCost: Float
    var actualCost: Float

    var name: String {
        let thisType = type(of: self)
        return String(describing: thisType)
    }

    init(minCost: Float, maxCost: Float, actualCost: Float) {
        self.minCost = minCost
        self.maxCost = maxCost
        self.actualCost = actualCost
    }

    convenience init() {
        self.init(minCost: -1, maxCost: -1, actualCost: -1)
    }
}

class AddHemLace : Hem {
    var costDetails: String?
    var costUnit: String?
    var units: Int = 1
    var secondaryCost: Float = 0.0
    var secondaryCostDetails: String?

    var totalCost : Float {
        return self.actualCost * Float(self.units) + self.secondaryCost
    }

    init() {
        let min: Float = 50.00
        let max: Float = 80.00
        let actual: Float = min
        super.init(minCost: min, maxCost: max, actualCost: actual)
    }

    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
}

问题:我不知道如何填充 UITableView

我创建了与我的类具有平行结构的相关枚举。

enum AlterationSectionsEnum: String, CaseIterable  {   
    case Hem
    // other cases
}

enum HemEnum: String, CaseIterable {
    case cutAndReplaceHem
    // other cases
}

然后我使用了一个大的 switch 语句、一个查找表和一些字符串解析 foo 来填充 UITableView。 Lots of code smell.

问题

当我试图理解这个 answer 时,我发现现在可以直接使用我的类来填充 UITableView。

我不知道该从何说起:

“例如,它应该提供一种方法来填充 NSView 或表格单元格。这样,每个类都可以定义自己的 UI 类型来呈现其特定的可配置参数……”

See screenshot of UITableView

【问题讨论】:

  • 当用户选择一行时,我想实例化对应的类并实例化该类的一个对象。我不认为为此,您需要枚举。请提供更多背景信息。
  • 为什么首先有一个枚举? IE。枚举案例有什么用途,而类本身无法实现?
  • 我对帖子进行了编辑
  • 感谢更新。但是我看不到你试图在哪里实例化你的类,以及你想如何使用它们。 readable 是什么?
  • 我尝试使用 NSClassFromString(className) 实例化我的类,其中 className 是所选枚举的 rawValue。我试过这个:stackoverflow.com/questions/24030814/…

标签: swift uitableview class enums


【解决方案1】:

问题:我不知道如何填充 UITableView

为什么不创建一个直接类型来表示节行层次结构?

准备一些代表一行的协议:

protocol AlterationType {
    var name: String {get}
    //... other common properties or methods
}

并定义一个结构体代表一个节:

struct AlterationSection {
    var sectionName: String
    var alterations: [AlterationType]
}

通过以上这些,您可以将表视图的数据模型声明为AlterationSection 的数组:

var alterationSections: [AlterationSection] = [
    //...
]

使用此数组,您可以将tableView(_:cellForRowAt:) 简单地写为:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //### `dequeueReusableCell(withIdentifier:)` may return nil,
        //### You should better use `dequeueReusableCell(withIdentifier:for:)` instead.
        //### (You need to register the identifier "Cell" in any of the available ways.)
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let alterationSection = alterationSections[indexPath.section]
        cell.textLabel?.text = alterationSection.alterations[indexPath.row].name
        return cell
    }

你可以将alterationSections初始化为:

class Hem : Codable, AlterationType { //<-Add conformance to `AlterationType`
    //...
}

class AddHemLace : Hem {
    //...
}
var alterationSections: [AlterationSection] = [
    AlterationSection(sectionName: "Hem", alterations: [
        AddHemLace(),
        //...
    ]),
    AlterationSection(sectionName: "...", alterations: [
        //...
    ]),
    //...
]

【讨论】:

    【解决方案2】:

    这种切换和强制向下转换为许多可能的类型之一是一种严重的代码气味,表明错过了应用面向对象设计的机会。

    在我看来,您将从DressCustomization 协议中受益匪浅,您的各种自定义都符合该协议。然后,制作一组符合此协议的类。每个自定义“类别”都需要一个 DressCustomization 类。 “类别”是指一组共享相似的可配置参数、用例和 UI 的定制。例如

    • “ElongateCustomization”是一种只存储连衣裙某个区域的名称以及拉长它的长度的方法
    • “ColorCustomization”是一种存储连衣裙某个区域的名称以及用于该区域的颜色
    • “AddOnCustomization”是一种存储要添加的功能的名称

    DressCustomization 协议是一种统一协议,可让您以统一的方式存储/处理这些自定义项中的任何一项。例如,它应该提供一种方法来填充 NSView 或表格单元格。这样,每个类都可以定义自己的 UI 类型来呈现其特定的可配置参数。例如。 ElongateCustomization 提供的视图提供了一个连衣裙部分的选择器,以及一个用于伸长长度的数字输入。 ColorCustomization 提供的视图提供了一个用于服装部分的选择器和一个用于输入的调色板。

    这些DressCustomization 对象也是您的数据模型的一部分。您的各种表格单元格可以存储对它们的引用(对于每个DressCustomization 类,您将有一个相应的UITableViewCell 子类),并且可以直接对它们进行操作。无需投射。

    从根本上说,几乎在任何情况下,当您发现自己在多个值中的一个上出现分支,并且在许多可能的事情中做一件事情时,这都强烈表明您可以利用多态性。通过这种方式,您可以轻松定义新的自定义项,而无需在代码库中四处寻找,添加新的 switch case、强制转换和其他类似的废话。

    【讨论】:

    • 谢谢。我想我在这里有一些冗余。我确实有相关的更改类,然后是枚举中的并行结构。我要写另一个问题,把你的想法和我的结构结合起来。
    • @MattGreen 我会使用枚举来存储与“上述之一”案例相关的数据,但前提是我不需要它是可扩展的,并且不需要定义任何行为案件之间有所不同。在您的情况下,您需要每种类型的更改来拥有自己的 UI,这在行为上是一个足够显着的差异,我认为这使得传统的多态性成为更好的选择。
    • 谢谢亚历山大。我发布了一个带有更多上下文的新问题,希望对我如何做到这一点提出一些建议:stackoverflow.com/questions/56213250/…
    • 从根本上说,这个问题试图解决“如何为每个表格单元格呈现适当的 UI?”的问题。您通过实现UITableViewController.tableView(_:cellForRowAt:) 方法来做到这一点。在您的实现中,您使用 section 和 row 从您的 alterationsLookupTable 中找到适当的元组,然后您尝试决定如何针对特定情况显示 UI。根本问题在于,与其询问您的模型它需要什么样的 UI,不如尝试为它做出决定。
    • 相反,您可以有一个customizations: [DressCustomization],并且让DressCustomization 协议要求符合类型来实现像createTableViewCell -&gt; UITableViewCell 这样的方法。与其让一个单一的 tableView(_:cellForRowAt:) 尝试做出所有决定,不如让它委托给 DressCustomization 对象,并让它们为您提供单元格。
    猜你喜欢
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多