Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stuck understanding how to create a table with multiple columns in iOS Swift

I've spent the better half of the day so far researching and trying to understand how to make a table with multiple columns. Embarrassingly, I am still quite new to Swift and programming in general so a lot of the stuff I've read and found aren't helping me too much.

I have basically found exactly what I want to create with this gentleman's blo: http://www.brightec.co.uk/blog/uicollectionview-using-horizontal-and-vertical-scrolling-sticky-rows-and-columns

However, even with his Github I'm still confused. It seems as if he did not use Storyboard at all (and for my project I've been using storyboard a lot). Am I correct in assuming this?

What I have so far is a UICollectionView embedded in a navigation controller. From here, I have created a new cocoa touch class file subclassed in the CollectionView. But from here is where I'm not entirely sure where to go.

If I can have some direction as to where to go from here or how to properly set it up that would be GREATLY appreciated.

Thanks so much in advance!

like image 257
Matty Avatar asked Mar 29 '15 21:03

Matty


2 Answers

IOS 10, XCode 8, Swift 3.0

I found an awesome tutorial on this. thanks to Kyle Andrews

I created a vertical table which can be scrollable on both directions by subclassing UICollectionViewLayout. Below is the code.

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

Below is my CollectionViewController Code.

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

To force CollectionView to use Custom Layout instead of UICollectionViwFlowLayout check below image.

enter image description here

Result:

Portrait mode

enter image description here

landscape mode

enter image description here

like image 67
Ashok R Avatar answered Nov 15 '22 04:11

Ashok R


One approach is to use a custom cell in a tableviewcontroller. Your story board consists of a table in which the cell is a custom cell with UILabels for columns laid out next to each other (with properly defined constraints).

Example code for the controllers looks like:

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
    }

}

and:

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
    }

}
like image 22
Syed Tariq Avatar answered Nov 15 '22 04:11

Syed Tariq