DynamicStackView: A love/hate compromise

综合技术 Varvet (源链)

I have a love/hate relationship with UITableView. I also have a love/hate relationship with UIStackView. It’s like they’re perfect for the exact purpose they were built for, but once you start looking at custom solutions they’ll be quick to slap you in the face.

So, a while ago I was working on a chatbot of sorts. It had lots of nice animations and patterns that clearly would not work very well inside the confines of a UITableView. Also, the chat history was divided into chunks with question + answer, meaning I really only wanted a limited set of rows for each “Q&A”. This in itself would have been doable with multiple data sources, but the deallocation of non-visible cells was a dealbreaker. Instead I tried out a conceptual mix of both UITableView and UIStackView, dubbing it DynamicStackView.

While the concept itself is nothing new – adding views to a container programmatically – the application of UITableView mechanics to a UIStackView is perhaps a little less conventional. It gives us a simpler version of a UITableView while adding an otherwise nonexistent model-cell-table relationship to a UIStackView.

A quick rundown

Basically there are three parts to DynamicStackView:

  1. DynamicStackViewModel
    • Protocol for associating generic models with DynamicStackViewCells .
    • View for subclassing to get access to associated models.
    • Custom UIStackView containing all DynamicStackViewCells .

A simplified version of the cell creation method in our custom UIStackView (ie. DynamicStackView ) shows us the core idea of the model-cell-table relationship:

private func createCell(from model: DynamicStackViewModel) -> DynamicStackViewCell {
    let cell = model.cellType.init()
    cell.setup(model: model)
    return cell

Subclassing a DynamicStackViewCell gives us access to the setup method above, thereby also giving us access to the associated model. Then, by exposing accessor functions like…

public func append(model: DynamicStackViewModel) {
    let cell = createCell(from: model)

… it’s really convenient for the developer to simply add models to the DynamicStackView and have those delivered to the associated cell. The actual connection is handled by the DynamicStackViewModel protocol:

public protocol DynamicStackViewModel {
    var cellType: DynamicStackViewCell.Type { get }

This way, a nice and tidy extension to any generic model connects it to a subclassed DynamicStackViewCell , leaving just a tiny footprint on the code outside the framework.

An actual implementation

DynamicStackViewModel – Protocol

All models to be used with DynamicStackView has to adhere to this protocol. As stated earlier, you’ll be associating them with a DynamicStackViewCell to automatically generate views in the DynamicStackView for you:

extension Content: DynamicStackViewModel {
    var cellType: DynamicStackViewCell.Type {
        return ContentCell.self

DynamicStackViewCell – Superclass

By subclassing DynamicStackViewCell you can ( must ) override its setup method to get access to the associated DynamicStackViewModel :

override func setup(model: DynamicStackViewModel) {
    if let model = model as? Content {
        label.text = model.text

DynamicStackView – Container

Simply add a new IBOutlet :

@IBOutlet weak var dynamicStackView: DynamicStackView!

Now add your DynamicStackViewModel compatible models to it:

let content = Content(text: "My content")
dynamicStackView.append(model: content)

Or as an array:

let contentArray = [
    Content(text: "My content"),
    Content(text: "Some more content")
dynamicStackView.append(models: contentArray)

If you want to manually override the cell type, simply specify it when adding models:

let overriddenContent = Content(text: "Overridden content")
dynamicStackView.append(model: overriddenContent, cellType: OtherContentCell.self)

Note:When overriding the cell type, make sure that your subclassed DynamicStackViewCell can handle the new DynamicStackViewModel :

override func setup(model: DynamicStackViewModel) {
    if let model = model as? Content {
        label.text = model.text
    } else if let model = model as? OtherContent {
        label.text = model.text

Finally, you can get access to the tapped cell through a callback block:

dynamicStackView.didTapCell = { cell in


With a pretty modest line count we now have a quick and lightweight framework handling lists of views in an automated fashion. Want rows at the bottom instead? Simply adjust your constraints in Interface Builder. Want scroll? Wrap everything in a scroll view.

Granted, DynamicStackView won’t (and shouldn’t) replace a UITableView in most projects, but for smaller lists – or those edge case chatbots – I think it’s a decent alternative.

You can find the complete framework project at github/varvet/DynamicStackView , and a simplified example project to play with at github/varvet/DynamicStackView/Example .


UITableView+FDTemplateLayoutCell 自动计算cell高度... UITableView+FDTemplateLayoutCell 是一个由国人团队开发的优化计算 UITableViewCell 高度的轻量级框架( GitHub 地址 ),由于实现逻辑简明清晰,代码也不复杂,非常适合作为新手学习其他著名却庞大的开源项目的“入门教材”。 这个框架的目...
LPDMvvmKit 系列之 UITableView 的改造 阅读本文需要对ReactiveCocoa足够了解,也可以参阅 图解ReactiveCocoa基本函数 Cocoa Touch Framework无疑是一个很好的框架,特别是对动画的支持,在我接触过的框架中可能是最好的(当然我接触的框架可能比较少),但是就UITableView来说...
项目实例解析MVC模式下的UITableView 简介 重构项目的时候遇到一个比较有逻辑性的界面,重构前该界面是把所有的代码全堆在在了控制器,不封装,不模块化,一共有两千多行,重构后在控制器的代码量减少了4倍,整体看上去逻辑更加清晰了些,在这个1024的日子写一写~ 先附上斯坦福教授的经典图片: 先看界面,如下图1 ...
UITableView/UICollectionView使用技巧 前言 知识是无穷无尽,技术需要积累,记录一点一滴,让成长的时间轴上变得充实一些。 今天就讲讲UITableView/UICollectionView的一些使用技巧。结合自己项目情况进行展开。 Header/Footer高度、悬停设置 高度设置 有时候我们需要设置 TableView ...
IOS: Multiple prototype cells in UITable... i am working in an project in which i have to create a facebook like newsfeed view after hours of R&D i choose to use prototype cells in UITableVi...
责编内容来自:Varvet (源链) | 更多关于

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » DynamicStackView: A love/hate compromise

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录