【问题标题】:Stuck understanding how to create a table with multiple columns in iOS Swift无法理解如何在 iOS Swift 中创建具有多列的表
【发布时间】:2015-06-02 19:19:56
【问题描述】:

到目前为止,我花了大半天的时间研究并试图了解如何制作一个包含多列的表格。令人尴尬的是,我对 Swift 和编程还是很陌生,所以我读过和发现的很多东西对我没有太大帮助。

我基本上已经找到了我想用这位绅士的 blo 创造的东西: http://www.brightec.co.uk/blog/uicollectionview-using-horizontal-and-vertical-scrolling-sticky-rows-and-columns

但是,即使使用他的 Github,我仍然感到困惑。似乎他根本没有使用 Storyboard(对于我的项目,我一直在使用 Storyboard)。我的假设是否正确?

到目前为止,我拥有的是嵌入在导航控制器中的 UICollectionView。从这里,我在 CollectionView 中创建了一个新的可可触摸类文件。但从这里是我不完全确定去哪里的地方。

如果我能对从这里去哪里或如何正确设置它有一些指导,我将不胜感激。

提前非常感谢!

【问题讨论】:

  • SO 不是“告诉我如何做”类型问题的好网站。它更适合“这是我的代码,我对这一行有问题”类型的问题。你应该做更多的研究,想出一个方法,然后发布关于你提议的方法的问题。
  • 我认为这是其他人将来可能会提出的一个好问题,但我建议将您当前的 UICollectionView 代码作为起点发布,然后描述您当前的代码无法正常工作的原因。 (类似于邓肯所说的。)

标签: ios swift collectionview


【解决方案1】:

一种方法是在 tableviewcontroller 中使用自定义单元格。您的故事板由一个表格组成,其中单元格是一个自定义单元格,其中 UILabels 用于彼此相邻布置的列(具有正确定义的约束)。

控制器的示例代码如下所示:

import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 3
    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as TableViewCell
        cell.column1.text = "1" // fill in your value for column 1 (e.g. from an array)
        cell.column2.text = "2" // fill in your value for column 2

        return cell
    }

}

和:

import UIKit

class TableViewCell: UITableViewCell {

    @IBOutlet weak var column1: UILabel!
    @IBOutlet weak var column2: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

【讨论】:

  • 赛义德,谢谢您的回复。我注意到您在 tableViewController 类下编写了 cell.column1.text ="1" 和 cell.column2.text = "2"。我的问题是我要在这个文件类中声明变量 column1 和 column2 吗?
  • 没有。您在从 UITableViewCell 子类化为 IBOutlets 的类的声明中声明它(参见第二个控制器代码)。通常,您 (1) 在自定义单元格的情节提要中为 column1 和 column2 创建标签。 (2) 创建一个新文件(我将其称为 TableViewCell 子类到 UITableViewCell) (3) 在辅助编辑器中将其设为文件 (4) 从 Storyboard 中的标签控制拖动到文件并命名 IBOutlets column1 和分别为第 2 列。
  • 嗨 Syed,你知道我将如何在 column1 和 column2 中输入值,这样每一行就不会显示相同的值吗?例如,在您提供的 Dropbox 文件中(非常感谢!),它将在 column1 中重复显示值“1”,以表示我返回的行数。我试图改变一些东西,比如把它们放在一个数组中,但我得到一个错误......我一定做错了什么。我试过:cell.column1.[Double] = [1,2,3]
  • 我修改了代码以从数组中读取。根据您的需要,有多种方法可以实现这一点。在示例中,我使用了一个 2 索引数组。每列一个双精度。如果您有更复杂的计算,您可以使用一个函数,将行作为参数传递给该函数,并让该函数返回 2 个输出 - 每列一个。我还意识到,在自动布局中,您需要使用正确的约束来获得正确的列宽度。 (我没有正确地做到这一点)。希望这会有所帮助
  • 赛义德,我收到错误 UITableViewCell is not convertible to 'TableViewCell';你是不是要使用 'as!'强迫沮丧?但即使我这样做了,并且在语法上它有效,当你实际执行行时,你会得到错误无法将类型'UITableViewCell'(0x109132540)的值转换为'myApp.TableViewCell'(0x106bd44d0)。你知道为什么会发生这种情况吗?谢谢!
【解决方案2】:

在 IB 中,我设置了一个表格视图并在内容视图中添加了一个堆栈视图(可以通过编程方式完成)。标签是通过编程设置的,因为它允许我将每列的宽度设置为单元格宽度的一部分。另外,我承认应该将表格视图 cellForRow 方法中的一些计算移出。

 import UIKit

class tableViewController: UITableViewController {
var firstTime = true
var width = CGFloat(0.0)
var height = CGFloat(0.0)
var cellRect = CGRectMake(0.0,0.0,0.0,0.0)

let colors:[UIColor] = [
    UIColor.greenColor(),
    UIColor.yellowColor(),
    UIColor.lightGrayColor(),
    UIColor.blueColor(),
    UIColor.cyanColor()
]

override func viewDidLoad() {
    super.viewDidLoad()
// workaround to get the cell width 
    cellRect = CGRectMake(0, 0, self.tableView.frame.size.width ,44);
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()

}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 3
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}

var cellWidth = CGFloat(0.0)
var cellHeight = CGFloat(0.0)
let widths = [0.2,0.3,0.3,0.2]
let labels = ["0","1","2","3"]

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let v = cell.contentView.subviews[0] // points to stack view
        // Note: using w = v.frame.width picks up the width assigned by xCode.
        cellWidth = cellRect.width-20.0 // work around to get a right width
        cellHeight = cellRect.height

        var x:CGFloat = 0.0
        for i in 0 ..< labels.count {
            let wl = cellWidth * CGFloat(widths[i])
            let lFrame = CGRect(origin:CGPoint(x: x,y: 0),size: CGSize(width:wl,height: cellHeight))
            let label = UILabel(frame: lFrame)
            label.textAlignment = .Center
            label.text = labels[i]
            v.addSubview(label)
            x = x + wl
            print("i = ",i,v.subviews[i])
            v.subviews[i].backgroundColor = colors[i]
        }


    return cell
}


}

【讨论】:

    【解决方案3】:

    IOS 10、XCode 8、Swift 3.0

    我在这上面找到了一个很棒的tutorial。感谢凯尔·安德鲁斯

    我通过继承 UICollectionViewLayout 创建了一个可以双向滚动的垂直表格。下面是代码。

     class CustomLayout: UICollectionViewLayout {
    
        let CELL_HEIGHT: CGFloat = 50
        let CELL_WIDTH: CGFloat = 180
    
    
        var cellAttributesDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
        var contentSize = CGSize.zero
    
        override var collectionViewContentSize: CGSize {
            get {
                return contentSize
            }
        }
    
        var dataSourceDidUpdate = true
    
        override func prepare() {
    
            let STATUS_BAR_HEIGHT = UIApplication.shared.statusBarFrame.height
            let NAV_BAR_HEIGHT = UINavigationController().navigationBar.frame.size.height
    
            collectionView?.bounces = false
    
            if !dataSourceDidUpdate {
    
                let yOffSet = collectionView!.contentOffset.y
    
                for section in 0 ..< collectionView!.numberOfSections {
                    if section == 0 {
                        for item in 0 ..< collectionView!.numberOfItems(inSection: section) {
                            let cellIndexPath = IndexPath(item: item, section: section)
                            if let attrs = cellAttributesDictionary[cellIndexPath] {
                                var frame = attrs.frame
                                frame.origin.y = yOffSet + STATUS_BAR_HEIGHT + NAV_BAR_HEIGHT
                                attrs.frame = frame
                            }
                        }
                    }
                }
               return
            }
    
            dataSourceDidUpdate = false
    
            for section in 0 ..< collectionView!.numberOfSections {
                for item in 0 ..< collectionView!.numberOfItems(inSection: section) {
                    let cellIndexPath = IndexPath(item: item, section: section)
                    let xPos = CGFloat(item) * CELL_WIDTH
                    let yPos = CGFloat(section) * CELL_HEIGHT
    
                    let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndexPath)
                    cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)
    
                    // Determine zIndex based on cell type.
                    if section == 0 && item == 0 {
                        cellAttributes.zIndex = 4
                    } else if section == 0 {
                        cellAttributes.zIndex = 3
                    } else if item == 0 {
                        cellAttributes.zIndex = 2
                    } else {
                        cellAttributes.zIndex = 1
                    }
    
                    cellAttributesDictionary[cellIndexPath] = cellAttributes
    
                }
            }
    
            let contentWidth = CGFloat(collectionView!.numberOfItems(inSection: 0)) * CELL_WIDTH
            let contentHeight = CGFloat(collectionView!.numberOfSections) * CELL_HEIGHT
            contentSize = CGSize(width: contentWidth, height: contentHeight)
        }
    
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            var attributesInRect = [UICollectionViewLayoutAttributes]()
    
            for cellAttrs in cellAttributesDictionary.values {
                if rect.intersects(cellAttrs.frame) {
                    attributesInRect.append(cellAttrs)
                }
            }
    
            return attributesInRect
        }
    
        override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            return cellAttributesDictionary[indexPath]
        }
    
        override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
            return true
        }
    }
    

    下面是我的 CollectionViewController 代码。

        import UIKit
    
    private let reuseIdentifier = "Cell"
    
    class VerticalCVC: UICollectionViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            collectionView?.isScrollEnabled = true
        }
    
        // MARK: UICollectionViewDataSource
    
        override func numberOfSections(in collectionView: UICollectionView) -> Int {
    
            return 20
        }
    
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 10
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CustomCell
    
            if indexPath.section == 0 {
                cell.backgroundColor = UIColor.darkGray
                cell.titleLabel.textColor = UIColor.white
    
    
            } else {
                cell.backgroundColor = UIColor.white
                cell.titleLabel.textColor = UIColor.black
            }
    
            cell.titleLabel.text = "section: \(indexPath.section) && row: \(indexPath.row)"
    
            return cell
        }
    }
    

    要强制 CollectionView 使用自定义布局而不是 UICollectionViwFlowLayout,请检查下图。

    结果:

    人像模式

    横向模式

    【讨论】:

    • 您的收藏视图对我来说是完美的,直到我向它添加搜索功能...如果我想允许搜索数据,我应该怎么做?使用此代码,它总是在数据重新加载时抛出异常
    • 在这种情况下如何根据集合视图内的标签文本大小修复单元格高度?
    • 这个教程做得非常好,而且方法很简单
    • 如何设置不同的列宽?
    猜你喜欢
    • 2018-11-03
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多