2

How can I identify the first and last rows of each section in a dynamic tableview and make a view inside the cell class hidden.

For each first cell in each section I need to hide the topView, for each last row of each section, I need to hide the bottomView.

For example I have the following Class:

class cell: UITableViewCell {
    @IBOutlet weak var topView: UIView!
    @IBOutlet weak var bottomView: UIView!
    
}

I have tried to identify the last row of each section by doing the following, but it simple does not hide the correct bottomView, except for in the last section. Is there a way to identify the rows correctly?

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! cell
    
    let item = sections[indexPath.section].items[indexPath.row]
    structure = sections[indexPath.section].items
   
    
    let totalRow = tableView.numberOfRows(inSection: indexPath.section)
    
    if(indexPath.row == totalRow - 1)
    {
        cell.bottomView.isHidden = true
    }
    return cell
    
}

var sections = [mySections]()
var structure = [myStructure]()

Fetching Data:

private func fetchJSON() {
    
    guard let url = URL(string: "test.com")
    else { return }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = "id=\1".data(using: .utf8)
    

URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
                
do {
  let decoder = JSONDecoder()
  self.structure.sort { $0. datestamp > $1.datestamp }
  let res = try decoder.decode([myStructure].self, from: data)
  let grouped = Dictionary(grouping: res, by: { $0. datestamp })
  let keys = grouped.keys.sorted()
self.sections = keys.map({mySections(date: $0, items: grouped[$0]!
                    
)})
  DispatchQueue.main.async {
  self.tableView.reloadData()
}
        }
        
        catch {
            print(error)
        }
    }.resume()
 }

Struct:

struct mySections {
    let date : String
    var items : [myStructure]
}


struct myStructure: Decodable {
    
    let recordid: Int
    let testname: Int
    let datestamp: String
}

Example of Data:

[
  { 
    "recordid": 1,
    "testname": "Jen",
    "datestamp": "2021-11-3"
  },
  {
    "recordid": 1,
    "testname": "Jake",
    "datestamp": "2021-11-2"
  }
]

Setting Up Sections:

 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let section = sections[section]
        return section.items.count
    }
    
  override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sections[section].date
    }
John
  • 965
  • 8
  • 16
  • There may be better ways, but a "brute force" way is to tag things in your data. Since a table view is array-based, simply add a flag to it. This way, you can also reorder things with logic to indicate changes. Let the background data rule - like it should. –  Nov 05 '21 at 23:36
  • I see, there must be a better way to identify the index path of these rows - the way I am attempting to do it seems very close – John Nov 05 '21 at 23:44
  • It *is* close, but never forget that a table view is really data-based. You really have't told us what that data is - but can your code "tell" the table cell that "hey, this is the last index in this section"? That way your table cell code doesn't need to figure it out - it can simply hide the view if the data says to do it. –  Nov 05 '21 at 23:51
  • I wonder if it is just cell recycling getting you. What happens If you add an else clause that explicitly sets isHidden to false if it is not the last item? – jnpdx Nov 06 '21 at 01:25

1 Answers1

0

When you are in your delegate creating cells, you are in the process of telling the table view what rows and sections it has. That means that the table view hasn't finished setting up sections so it's not the right time to call tableView.numberOfRows(inSection:).

You're already pulling your data out of the model... in this case it looks like your model has an array of sections and each section has an array of rows, so ask the model whether or not the cell your building is at the beginning or end of its section:

import UIKit
import SwiftUI
import PlaygroundSupport

class CustomCell : UITableViewCell {
    static let identifier = "CustomCell"
}

class DataSource : NSObject, UITableViewDataSource {
    let sections = [
        [
            "Cow",
            "Duck",
            "Chicken"
        ],

        [
            "Lion",
            "Zebra",
            "Oryx"
        ],
    ]

    func numberOfSections(in tableView: UITableView) -> Int {
        sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let newCell = tableView.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath)
        if let cell = newCell as? CustomCell {
            cell.textLabel?.text = sections[indexPath.section][indexPath.row]

            if indexPath.row == 0 {
                cell.textLabel?.backgroundColor = UIColor.yellow
            }

            if indexPath.row == sections[indexPath.section].count - 1 {
                cell.textLabel?.backgroundColor = UIColor.gray
            }
        }

        return newCell
    }
}

let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
tableView.register(CustomCell.self, forCellReuseIdentifier: CustomCell.identifier)
let dataSource = DataSource()
tableView.dataSource = dataSource

PlaygroundSupport.PlaygroundPage.current.liveView = tableView
Scott Thompson
  • 22,629
  • 4
  • 32
  • 34
  • I am getting, Type of expression is ambiguous without more context for the following line: `if(indexPath.row == sections[indexPath.section].count - 1) {` – John Nov 06 '21 at 01:18
  • Without knowing more about what `sections` is, there's not a lot that can be gleaned from what you've posted. – Scott Thompson Nov 06 '21 at 01:26
  • Also I noticed the second cell of each section is also hiding the topView for some reason - I am doing `if(indexPath.row == 0) {` – John Nov 06 '21 at 01:26
  • Please see my updated post – John Nov 06 '21 at 01:31
  • I updated my answer with a complete playground example. It changes the color of the cells, but you could just as easily hide views – Scott Thompson Nov 06 '21 at 01:53
  • While I am of course getting the same result from your playground code, I am not getting that result in my code - instead still getting the same error message `Type of expression is ambiguous without more context` – John Nov 06 '21 at 19:22
  • In your case it looks like you want `if(indexPath.row == sections[indexPath.section].items.count - 1) {` – Scott Thompson Nov 07 '21 at 01:40