我有一些枚举,它们的案例名称与类名称相对应。我用枚举填充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的类型安全性。我猜有一种快速(惯用的)方法可以实现这一目标。
以下是一个更改部分的示例—> 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")
}
}
我进行了与我的类具有相似结构的相关枚举。
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类型,以为其特定的可配置参数呈现…”
答案 0 :(得分:0)
这种切换和强制向下转换为许多可能的类型之一是一种严重的代码异味,这表明错过了应用面向对象设计的机会。
在我看来,您将从DressCustomization
协议中受益匪浅,您的各种定制都遵循该协议。然后,创建一组符合该协议的类。每个“类别”自定义项都需要一个DressCustomization
类。 “类别”是指一组共享相似的可配置参数,用例和UI的定制。例如
DressCustomization
协议是一种统一协议,可让您以统一的方式存储/处理这些自定义项中的任何一项。例如,它应该提供一种填充NSView或表单元格的方法。这样,每个类都可以定义自己的UI类型,以为其特定的可配置参数呈现。例如。由ElongateCustomization
出售的视图显示了一个连衣裙部分的选择器,以及一个输入长度的数字。由ColorCustomization
出售的视图为礼服部分提供了一个选择器,并为输入提供了一个调色板。
这些DressCustomization
对象也是数据模型的一部分。您的各种表单元格可以存储对它们的引用(对于每个DressCustomization
类,您将有一个对应的UITableViewCell
子类),并且可以直接对其执行操作。无需铸造。
从根本上讲,几乎在任何情况下,您都发现自己依赖于多个值之一,并从许多可能的事情中做一件事情,这充分说明您可以利用多态性。这样,您可以轻松定义新的自定义设置,而不必在代码库中四处寻找新的开关案例,强制转换和其他类似的废话。
答案 1 :(得分:0)
问题:我不知道如何填充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: [
//...
]),
//...
]