Swift 3 - Expandable Table View Cells with first cell already expanded

12,770

Solution 1

I feel like you do not fully understand your own code but since you did put a lot of effort into your question I will give you a hint.

In your UITableViewController somewhere at the top you initialise selectedIndexPath which should look something like

var selectedIndexPath: IndexPath?

You can set that to a default value like this

var selectedIndexPath: IndexPath? = IndexPath(row: 0, section: 0)

So cell at (row: 0, section: 0) will expand on default.

Solution 2

Yesterday I have completed similar feature with reference to this sample: https://github.com/justinmfischer/SwiftyAccordionCells

As per your implementation, you are tracking the current expanded cell using "selectedIndexPath". So when your view is loaded you have to set "selectedIndexPath" row and section value to 0 as you are using only one section.

Hope this is helpful!

Solution 3

In viewDidLoad set selectedIndexPath = IndexPath(row: 0, section: 0)

That should "auto-expand" the first row.

Share:
12,770
noblerare
Author by

noblerare

Updated on June 05, 2022

Comments

  • noblerare
    noblerare almost 2 years

    I am using Swift 3.

    I've followed this tutorial to get it so that I can tap on a table view cell which will expand revealing more information.

    https://www.youtube.com/watch?v=VWgr_wNtGPM&t=294s

    My question is: how do I do it so that the first cell is expanded when the view loads already (i.e. the user doesn't have to click to see that cell expand) but all other behavior remains the same (e.g. if it's clicked again, it de-collapses)?

    UITableViewCell:

    import UIKit
    
    class ResultsCell: UITableViewCell {
    
        @IBOutlet weak var introPara : UITextView!
        @IBOutlet weak var section_heading : UILabel!
    
        class var expandedHeight : CGFloat { get { return 200.0 } }
        class var defaultHeight : CGFloat { get { return 44.0 } }
        var frameAdded = false
    
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        override func awakeFromNib() {
            super.awakeFromNib()
            section_heading.translatesAutoresizingMaskIntoConstraints = false
        }
    
        func checkHeight() {
            introPara.isHidden = (frame.size.height < ResultsCell.expandedHeight)
        }
    
        func watchFrameChanges() {
            if(!frameAdded) {
                addObserver(self, forKeyPath: "frame", options: .new, context: nil)
                checkHeight()
            }
        }
    
        func ignoreFrameChanges() {
            if(frameAdded){
                removeObserver(self, forKeyPath: "frame")
            }
        }
    
        deinit {
            print("deinit called");
            ignoreFrameChanges()
        }
    
        // when our frame changes, check if the frame height is appropriate and make it smaller or bigger depending
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            if keyPath == "frame" {
                checkHeight()
            }
        }
    
    }
    

    UITableViewController

    // class declaration and other methods above here...
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    // number of rows in the table view
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return section_heading.count
    }
    
    // return the actual view for the cell
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let resultcell = tableView.dequeueReusableCell(withIdentifier: "resultCellTemplate", for: indexPath) as! ResultsCell
        resultcell.section_heading.text = section_heading[indexPath.row]
        resultcell.introPara.attributedText = contentParagraphs[indexPath.row]
    
        return resultcell
    }
    
    // when a cell is clicked
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let previousIndexPath = selectedIndexPath
    
        // the row is already selected, then we want to collapse the cell
        if indexPath == selectedIndexPath {
            selectedIndexPath = nil
        } else { // otherwise, we expand that cell
            selectedIndexPath = indexPath
        }
    
        var indexPaths : Array<IndexPath> = []
    
        // only add a previous one if it exists
        if let previous = previousIndexPath {
            indexPaths.append(previous)
        }
        if let current = selectedIndexPath {
            indexPaths.append(current)
        }
    
        // reload the specific rows
        if indexPaths.count > 0 {
            tableView.reloadRows(at: indexPaths, with: .automatic)
        }
    }
    
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        (cell as! ResultsCell).watchFrameChanges()
    }
    
    override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        (cell as! ResultsCell).ignoreFrameChanges()
    
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath == selectedIndexPath {
            return ResultsCell.expandedHeight
        } else {
            return ResultsCell.defaultHeight
        }
    }
    

    So this works as intended.

    enter image description here

    But how do I make it so that the first cell is already expanded?

    Thanks for your help.