设计UITableView/Cell-iOS
我正在设计一个UITableView,使用子视图填充它的可重用单元,我希望对此有一些看法。 正如我测试过的,它工作得很好。但是,我不知道这是否是一个好的解决方案 场景是:我有一个具有不同类型单元格(布局)的tableview。当我设计时,它增长很快(我的控制器代码),因为我必须注册大量的cell并处理cellForRow。然后我想到了这个想法,为一个唯一的可重用单元实例化不同的子视图,并使用“Presenter”来处理委托/数据源。你认为这是个问题吗?这是一个好方法吗 提前谢谢 注:对于任何英语错误,我深表歉意 编辑: 以下是项目中的会话,后面是de代码: 代码位于:设计UITableView/Cell-iOS,ios,iphone,uitableview,design-patterns,tableviewcell,Ios,Iphone,Uitableview,Design Patterns,Tableviewcell,我正在设计一个UITableView,使用子视图填充它的可重用单元,我希望对此有一些看法。 正如我测试过的,它工作得很好。但是,我不知道这是否是一个好的解决方案 场景是:我有一个具有不同类型单元格(布局)的tableview。当我设计时,它增长很快(我的控制器代码),因为我必须注册大量的cell并处理cellForRow。然后我想到了这个想法,为一个唯一的可重用单元实例化不同的子视图,并使用“Presenter”来处理委托/数据源。你认为这是个问题吗?这是一个好方法吗 提前谢谢 注:对于任何英语
- OrderDetailCell
class OrderDetailCell: UITableViewCell { //MARK: Outlets @IBOutlet weak var cellHeight: NSLayoutConstraint! @IBOutlet weak var viewContent: UIView! //Variables var didUpdateLayout = false internal func setupLayoutWith(view: UIView){ cellHeight.constant = view.frame.height viewContent.frame = view.frame viewContent.addSubview(view) updateConstraints() layoutIfNeeded() didUpdateLayout = true } }
- OrderDetails子视图
class OrderDetailSubview: UIView { var type: OrderDetailsSubViewType? var height: CGFloat = 1 class func instanceFromNib(withType type: OrderDetailsSubViewType) -> OrderDetailSubview { let view = UINib(nibName: type.rawValue, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! OrderDetailSubview switch type { case .OrderDetailSubviewStatus: view.height = 258 case .OrderDetailSubViewItem: view.height = 129 case .OrderDetailSubViewStoreInformation: view.height = 317 case .OrderDetailSubViewEvaluation: view.height = 150 } view.updateConstraints() view.layoutIfNeeded() return view } }
- OrderDetailPresenter
enum OrderDetailsSubViewType: String { case OrderDetailSubviewStatus = "OrderDetailSubviewStatus", OrderDetailSubViewItem = "OrderDetailSubViewItem", OrderDetailSubViewStoreInformation = "OrderDetailSubViewStoreInformation", OrderDetailSubViewEvaluation = "OrderDetailSubViewEvaluation" static let types = [OrderDetailSubviewStatus, OrderDetailSubViewItem, OrderDetailSubViewStoreInformation, OrderDetailSubViewEvaluation] } class OrderDetailPresenter { //Constants let numberOfSections = 4 //Variables // var order: Order? func setup(reusableCell: UITableViewCell, forRowInSection section: Int) -> OrderDetailCell { let cell = reusableCell as! OrderDetailCell for sub in cell.viewContent.subviews { sub.removeFromSuperview() } let subView = OrderDetailSubview.instanceFromNib(withType: OrderDetailsSubViewType.types[section]) cell.setupLayoutWith(view: subView) return cell } func numberOfRowsForSection(_ section: Int) -> Int { switch section { case 1: //TODO: count de offerList return 4 default: return 1 } } }
- OrderDetailViewController
class OrderDetailViewController: BaseViewController { //MARK: Outlets @IBOutlet weak var tableView: UITableView! var presenter = OrderDetailPresenter() override func setupView() { setupTableView() } } extension OrderDetailViewController: UITableViewDataSource, UITableViewDelegate { internal func setupTableView() { tableView.delegate = self tableView.dataSource = self tableView.estimatedRowHeight = 600 tableView.rowHeight = UITableViewAutomaticDimension tableView.register(UINib(nibName: "OrderDetailCell", bundle: nil), forCellReuseIdentifier: "OrderDetailCell") } func numberOfSections(in tableView: UITableView) -> Int { return presenter.numberOfSections } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return presenter.numberOfRowsForSection(section) } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let reusableCell = tableView.dequeueReusableCell(withIdentifier: "OrderDetailCell") as! OrderDetailCell let cell = presenter.setup(reusableCell: reusableCell, forRowInSection: indexPath.section) return cell } }
let dynamicCellID: String = "dynamicCellID" //One Cell ID for resuse
class dynamicCell: UITableViewCell {
var sub: UIView // you just need to specify the subview
init(sub: UIView) {
self.sub = sub
super.init(style: .default, reuseIdentifier: dynamicCellID)
self.addSubview(sub)
self.sub.frame = CGRect(x: 0, y: 0, width: sub.frame.width, height: sub.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
您需要创建一个视图数组,将该视图赋给代理中的每个单元格
let views: [UIView] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return views.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let v = views[indexPath.row]
return dynamicCell(sub: v)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let v = views[indexPath.row]
return v.frame.height + 10 //offset is 10 point
}
在这里,您希望有多个实现所需不同布局的
UITableViewCell
子类,然后在表视图数据源中选择相关的子类
class Cell1: UITableViewCell {
let label = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(label)
}
... whatever other setup/layout you need to do in the class ...
}
class Cell2: UITableViewCell {
let imageView = UIImageView()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(imageView)
}
... whatever other setup/layout you need to do in the class ...
}
然后在视图控制器中
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell1.self, forCellReuseIdentifier: "cell1Identifier")
tableView.register(Cell2.self, forCellReuseIdentifier: "cell2Identifier")
}
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 { // just alternating rows for example
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1Identifier", for: indexPath) as! Cell1
// set data on cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2Identifier", for: indexPath) as! Cell2
// set data on cell
return cell
}
}
因此,这只是一个示例,但在表视图中使用两个不同的单元格子类来交替行。如果您遵循了适当的iOS编码实践(如
将单元格出列),并且您的代码没有bug,那么很难说您的代码不好。如果您的代码存在问题(如崩溃、逻辑错误或性能问题),请编辑您的问题以包含代码并提出特定问题。如果您希望有人查看您的代码,那么您可以尝试codereview.stackexchange.com
谢谢@robotcat,我已经按照建议进行了操作。问题是,我有多种细胞类型。因此,我只创建了一个单元格,而不是创建了许多单元格,现在我正在为每一行使用相同单元格实例化一个子视图,并更新布局。我只是想听听你的意见。也要感谢codereview的建议。。从未听说过。我试试看!谢谢你,乔治!我是那样做的。但问题是,因为我有多个单元格,我不想注册所有的单元格,然后在cellForRow上建立逻辑。因此,我只创建了一个单元格,而不是创建了许多单元格,现在我正在为每一行使用相同单元格实例化一个子视图,并更新布局。如果你愿意,我可以分享我的代码。@Gehlen如果可以,请分享你的代码。这听起来像是一个糟糕的计划,因为您必须重新实现UITableView免费提供的视图重用系统。这就引出了一个问题,为什么要使用表视图,而不仅仅是滚动视图?为什么使用具有不同布局的自定义UIView
并在它们之间切换比使用不同的UITableViewCell
更简单?我已经更新了这个问题。。。我很感激你的意见!谢谢对我在做这样的事!但使用xib文件和“视图演示器”来处理行的每个实例。。问题是:这样做,是一个伟大的方法吗?我的意思是,仍在重用单元,我正在处理每个实例(也要删除)。。我只是想尽量减少代码行..如果你想重用单元以获得良好的性能。您需要尽可能减少不同的单元。就像我上面给你的解决方案。视图始终作为实例存在于阵列中。如果你有很多视图,它会占用很多内存。唯一的区别是我在cellForRow中实例化了视图,并且(为了确保)我从单元格的内容中删除了子视图,可能会稍微降低性能。。但仍然是干净的代码。。卷轴依然平滑。。我会暂时保留这种方法,直到出现问题。。谢谢