【问题标题】:Designing a UITableView/Cell - iOS设计一个 UITableView/Cell - iOS
【发布时间】:2017-03-08 20:46:45
【问题描述】:

我正在设计一个 UITableView 使用子视图来填充它的可重用单元格,我希望对此有一些看法。 正如我测试过的,它运行良好。但是,我不知道这是否是一个好的解决方案。

场景是:我有一个包含不同类型单元格(布局)的表格视图。当我在设计时,它增长很快(我的控制器代码),因为我必须注册很多单元格并处理 cellForRow。然后我有了这个想法,为一个独特的可重用单元实例化不同的子视图,并使用“Presenter”来处理委托/数据源。你觉得这是个问题吗?这是一个好方法吗?

提前致谢!

Ps.: 抱歉有任何英文错误!

已编辑:

这是项目中的会话,后跟解码代码:

代码在:

  • 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
     }
    }
    
  • OrderDetailSubview

    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
    
     }
    }
    

*抱歉这里有缩进...

就是这样!你怎么看?

【问题讨论】:

  • 如果您遵循了正确的 iOS 编码实践(例如 dequeueing 一个单元格)并且您的代码没有错误,那么很难说您的代码不好。如果您的代码存在问题(如崩溃、逻辑错误或性能问题),请编辑您的问题以包含您的代码并提出特定问题。如果你想让别人检查你的代码,那么你可以试试codereview.stackexchange.com
  • 感谢@RoboticCat,我已按照建议进行操作。问题是,我有多种细胞类型。因此,我只创建了一个,而不是创建许多单元格,现在我正在为具有相同单元格的每一行实例化一个子视图,并更新布局。运行良好,我只是想要一些意见。也感谢 codereview 的建议.. 以前从未听说过。我会努力的!

标签: ios iphone uitableview design-patterns tableviewcell


【解决方案1】:

这里你想要多个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
    }
}

所以这只是一个例子,但是在表格视图中使用两个不同的单元格子类来交替行。

【讨论】:

  • 谢谢@George!我是这样做的。但问题是,因为我有多个单元格,所以我不想注册所有这些并在 cellForRow 中创建该逻辑。因此,我只创建了一个,而不是创建许多单元格,现在我正在为具有相同单元格的每一行实例化一个子视图,并更新布局。如果你愿意,我可以分享我的代码..
  • @Gehlen 如果可以,请分享您的代码。这听起来像是一个糟糕的计划,因为您将不得不重新实现 UITableView 免费提供的视图重用系统。这就引出了一个问题,为什么要使用表格视图,而不仅仅是滚动视图?为什么拥有具有不同布局的自定义UIView 并在它们之间切换比拥有不同的UITableViewCell 更简单?
  • 我已经更新了这个问题...感谢您的意见!谢谢!
【解决方案2】:
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
    }

【讨论】:

  • 是的!我正在做这样的事情!但是使用 xib 文件和“查看演示者”来处理行的每个实例。问题是:这样做是一个好方法吗?我的意思是,仍然重用单元格,我正在处理每个实例(也删除)..我只是想最小化代码行..
  • 如果您想重复使用单元以获得良好的性能。您需要尽可能减少不同的单元格。就像我上面给你的解决方案一样。您的视图始终作为实例在您的数组中。如果你有很多视图,它会占用很多内存。
  • 唯一的区别是我在 cellForRow 中实例化视图并且(可以肯定)我从单元格的内容中删除子视图,可能会降低性能一点..但仍然是干净的代码..滚动仍然流畅..我现在会保持这种方法,直到出现一些问题..谢谢!
猜你喜欢
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多