1

I currently have an array which I am appending options to. They are they displayed in a table with 3 sections. The first 2 sections have 1 row each, but the third section has a variable number of rows depending on what is appended to the array. I essentially take the third component of my initial array (allAlbums[0].markscheme) and break it down to create multiple new items in the array.

However, when I am trying to simulate this, I get a fatal array on 'cell.textData?.text = section[indexPath.row] as! String' and I'm not sure why?

final class CaseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet var titleText: UILabel!
@IBOutlet var tableView: UITableView!

private var allAlbums = [Case]()

let kHeaderSectionTag: Int = 6900;

var expandedSectionHeaderNumber: Int = -1
var expandedSectionHeader: UITableViewHeaderFooterView!
var sectionItems: Array<Any> = []
var sectionNames: Array<Any> = []
var markschemeRows: Array<Any> = []

override func viewDidLoad() {
    super.viewDidLoad()

    allAlbums = LibraryAPI.shared.getCases()

    // Filter the main array to match a single case
    allAlbums = allAlbums.filter { $0.title == title}

    // Get data to fill in to table
    sectionNames = [ "Trainee Information", "Patient Information", "Examiner Information" ];
    sectionItems = [ [allAlbums[0].doctor], [allAlbums[0].patient], [allAlbums[0].markscheme]]

    let text = allAlbums[0].markscheme
    markschemeRows = text.components(separatedBy: " ")

    sectionItems.append(contentsOf: markschemeRows)

    // Autoresize rows
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 500

    // Remove excess row seperators
    tableView.tableFooterView = UIView()
    tableView.separatorColor = UIColor.clear

    titleText.text = title


}

func numberOfSections(in tableView: UITableView) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if (self.expandedSectionHeaderNumber == section) {

        // Header section
        let header = self.sectionNames[section] as! String

        // If markscheme, create the markscheme format
        if (header == "Examiner Information")
        {
            print(self.markschemeRows.count)
            return self.markschemeRows.count
        }
        else
        {
            let arrayOfItems = self.sectionItems[section] as! NSArray
            print(arrayOfItems.count)
            return arrayOfItems.count
        }


    } else {
        return 0;
    }
}


func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if (self.sectionNames.count != 0) {
        return self.sectionNames[section] as? String
    }
    return ""
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 44.0;
}

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat{
    return 0;
}

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    //recast your view as a UITableViewHeaderFooterView
    let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
    header.contentView.backgroundColor = UIColor.darkGray
    header.textLabel?.textColor = UIColor.white

    if let viewWithTag = self.view.viewWithTag(kHeaderSectionTag + section) {
        viewWithTag.removeFromSuperview()
    }
    let headerFrame = self.view.frame.size
    let theImageView = UIImageView(frame: CGRect(x: headerFrame.width - 32, y: 13, width: 18, height: 18));
    theImageView.image = UIImage(named: "Chevron-Dn-Wht")
    theImageView.tag = kHeaderSectionTag + section
    header.addSubview(theImageView)

    // make headers touchable
    header.tag = section
    let headerTapGesture = UITapGestureRecognizer()
    headerTapGesture.addTarget(self, action: #selector(CaseViewController.sectionHeaderWasTouched(_:)))
    header.addGestureRecognizer(headerTapGesture)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! CustomTableCell
    let section = self.sectionItems[indexPath.section] as! NSArray

    cell.textLabel?.textColor = UIColor.black

    cell.textData?.text = section[indexPath.row] as! String

    return cell
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
}

// MARK: - Expand / Collapse Methods

@objc func sectionHeaderWasTouched(_ sender: UITapGestureRecognizer) {
    let headerView = sender.view as! UITableViewHeaderFooterView
    let section    = headerView.tag
    let eImageView = headerView.viewWithTag(kHeaderSectionTag + section) as? UIImageView

    if (self.expandedSectionHeaderNumber == -1) {
        self.expandedSectionHeaderNumber = section
        tableViewExpandSection(section, imageView: eImageView!)
    } else {
        if (self.expandedSectionHeaderNumber == section) {
            tableViewCollapeSection(section, imageView: eImageView!)
        } else {
            let cImageView = self.view.viewWithTag(kHeaderSectionTag + self.expandedSectionHeaderNumber) as? UIImageView
            tableViewCollapeSection(self.expandedSectionHeaderNumber, imageView: cImageView!)
            tableViewExpandSection(section, imageView: eImageView!)
        }
    }
}

func tableViewCollapeSection(_ section: Int, imageView: UIImageView) {
    let sectionData = self.sectionItems[section] as! NSArray

    self.expandedSectionHeaderNumber = -1;
    if (sectionData.count == 0) {
        return;
    } else {
        UIView.animate(withDuration: 0.4, animations: {
            imageView.transform = CGAffineTransform(rotationAngle: (0.0 * CGFloat(Double.pi)) / 180.0)
        })
        var indexesPath = [IndexPath]()
        for i in 0 ..< sectionData.count {
            let index = IndexPath(row: i, section: section)
            indexesPath.append(index)
        }
        self.tableView!.beginUpdates()
        self.tableView!.deleteRows(at: indexesPath, with: UITableView.RowAnimation.fade)
        self.tableView!.endUpdates()
    }
}

func tableViewExpandSection(_ section: Int, imageView: UIImageView) {
    let sectionData = self.sectionItems[section] as! NSArray

    if (sectionData.count == 0) {
        self.expandedSectionHeaderNumber = -1;
        return;
    } else {
        UIView.animate(withDuration: 0.4, animations: {
            imageView.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(Double.pi)) / 180.0)
        })
        var indexesPath = [IndexPath]()

        // Header section
        let header = self.sectionNames[section] as! String

        // If markscheme, create the markscheme format
        if (header == "Examiner Information")
        {
            for i in 0 ..< markschemeRows.count {
                let index = IndexPath(row: i, section: section)
                indexesPath.append(index)
            }
        }
        else
        {
            for i in 0 ..< sectionData.count {
                let index = IndexPath(row: i, section: section)
                indexesPath.append(index)
            }
        }
        self.expandedSectionHeaderNumber = section
        self.tableView!.beginUpdates()
        self.tableView!.insertRows(at: indexesPath, with: UITableView.RowAnimation.fade)
        self.tableView!.endUpdates()
    }
}

}

1 Answer 1

4

The error is pretty clear.

In numberOfRows you return markschemeRows.count for section 2 which is the number of separated items in this line

markschemeRows = text.components(separatedBy: " ")

Then you must also get the item from markschemeRows rather than from section[indexPath.row] in cellForRow

var markschemeRows = [String]()

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! CustomTableCell
    let section = self.sectionItems[indexPath.section] as! NSArray

    cell.textLabel?.textColor = UIColor.black
    if indexPath.section == 2 {
        cell.textData?.text = markschemeRows[indexPath.row]
    } else {
        cell.textData?.text = section[indexPath.row] as! String
    }

    return cell
}

Your code is quite cumbersome. For example sectionNames and markschemeRows are clearly [String].Why do you declare the arrays as [Any]? This is Swift. Take care of the types. And don't use Foundation collection types like NSArray in Swift at all. Again take care of the types.

Sign up to request clarification or add additional context in comments.

1 Comment

That's brilliant! Thank you! Sorry I am just learning so I am tidying the code up as I go - thanks for pointing that out :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.