Как динамически добавлять данные в UITableView при нажатии на заголовки разделов - PullRequest
0 голосов
/ 19 сентября 2018

Возникла проблема с динамической загрузкой данных в разделы UITableView.Моим бизнес-требованием является то, что у меня есть ViewController с именем "Courses", в этом представлении у меня есть tableView с различными разделами, для которых я использовал TableViewHeaderFooterView, для каждого заголовка у него будет соответствующее название курса, количество глав вэтот курс и количество заданий для этого курса, и я получаю все эти данные из вызова API. Я могу заполнить заголовки tableView этими данными, а также я получу 'id' для каждого курса, который я добавил кактег для каждого заголовка.Теперь, когда я нажимаю на любой заголовок, я должен сделать еще один вызов API, отправив значение тега заголовка, который является courseID, поэтому я получу источник данных для tableView, и он должен расширить раздел с показом строк и данных.в строках, поступающих из источника данных.

Я могу сделать все это со статическими данными, где у меня есть источник данных, прежде чем нажать на заголовок, но я не знаю, как это сделать, если данные должнычтобы быть добавленным динамически при нажатии заголовков.

Я пытался сделать это, когда я нажимаю на любой заголовок в первый раз, он показывает данные для этого раздела, но если я нажимаю на тот же заголовокснова или любой другой заголовок Я получаю сбой с

Завершение приложения из-за необработанного исключения «NSInternalInconsistencyException», причина: «Неверное обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся всуществующий раздел после обновления (0) должен быть равен количеству строк, содержащихся в этом разделедо обновления (1), плюс или минус количество строк, вставленных или удаленных из этого раздела (0 вставлено, 0 удалено) и плюс или минус количество строк, перемещенных в или из этого раздела (0 перемещено, 0 перемещено).

Я публикую свои модели и код здесь:

Модель для названий курсов:

struct CourseNamesModel {

var courseName: String!
var courseNameLetter: String!
var numberOfChaptersAndAssignments: String!
var chapterCount: Int!
var courseId: Int!
var opened: Bool!

init(courseName: String, courseNameLetter: String, numberOfChaptersAndAssignments: String, chapterCount: Int, courseId: Int ,opened: Bool) {

    self.courseName = courseName
    self.courseNameLetter = courseNameLetter
    self.numberOfChaptersAndAssignments  = numberOfChaptersAndAssignments
    self.chapterCount = chapterCount
    self.courseId = courseId
    self.opened = opened
  }
}

Модельдля данных после нажатия на заголовок:

struct CourseDataModel {

var chapterName: String!
var documentAndAssignmentCount: String!

init(chapterName: String, documentAndAssignmentCount: String!) {

    self.chapterName = chapterName
    self.documentAndAssignmentCount = documentAndAssignmentCount
  }
}

код для моего viewController и TableView

import UIKit
import Alamofire

class CoursesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, ExpandableHeaderViewDelegate {

@IBOutlet weak var tableView: UITableView!
var sectionData = [CourseNamesModel]()
var tableData = [CourseDataModel]()

var selectedIdexPath: IndexPath!
override func viewDidLoad() {
    super.viewDidLoad()

    self.setFontFamilyAndSize()
    self.title = "Courses"
    selectedIdexPath = IndexPath(row: -1, section: -1)
    tableView.register(UINib(nibName: "ExpandableHeaderView", bundle: nil), forHeaderFooterViewReuseIdentifier: "expandableHeaderView")
   getCourseNames()
}

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

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

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return tableView.frame.size.height/8.2
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

    if sectionData[indexPath.section].opened {
        return tableView.frame.size.height/8.48275862069
    } else {
        return 0
    }
}

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {

    return 1
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "expandableHeaderView") as! ExpandableHeaderView
    headerView.customInit(courseName: sectionData[section].courseName, letterSign: sectionData[section].courseNameLetter, numberOfChaptersAndAssignments: sectionData[section].numberOfChaptersAndAssignments, section: section, delegate: self)
    headerView.tag = sectionData[section].courseId
    return headerView
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "dataCell") as! DataCell
    cell.chapterName.text = tableData[indexPath.row].chapterName
    cell.numberOfDocumentsAndAssignments.text = tableData[indexPath.row].documentAndAssignmentCount
    return cell
}

func getCourseNames() {

    sectionData = []

    let courseNamesURL = "\(WebAPI.baseURL2 + WebAPI.coursesAPI)"

    Alamofire.request(courseNamesURL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { response in

        switch response.result {
        case .success:

            let responseData = response.result.value as? [[String: Any]]
            guard let courseNamesData = responseData else {return}

            for courseDetail in courseNamesData {
                let courseName = courseDetail["CourseName"] as! String
                let courseNameLetter = String(courseName.first!)
                let chaptersCount = courseDetail["Chapterscount"] as! Int
                let assignmentsCount = courseDetail["AssignmentCount"] as! Int
                let chaptersAndAssignemntsCount = "\(chaptersCount) Chapters, \(assignmentsCount) Assignments"
                let courseId = courseDetail["CourseId"] as! Int
                self.sectionData.append(CourseNamesModel(courseName: courseName, courseNameLetter: courseNameLetter, numberOfChaptersAndAssignments: chaptersAndAssignemntsCount, chapterCount: chaptersCount, courseId: courseId, opened: false))
            }
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        case .failure(let error):
            print(error.localizedDescription)
        }
     }
   }
}

код для toggleSection (развернуть /Свернуть) Функция делегата:

func toggleSection(header: ExpandableHeaderView, section: Int) {
    sectionData[section].opened = !sectionData[section].opened
    tableData = []
    let courseChaptersURL = "\(WebAPI.baseURL2 + WebAPI.courseChaptersAPI)\(header.tag)"
    Alamofire.request(courseChaptersURL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON {response in

        switch response.result {
        case .success:

            let responseData = response.result.value as? [[String : Any]]
            guard let courseChaptersData = responseData else {return}

            for chapterDetail in courseChaptersData {
                let chapterName = chapterDetail["ChapterName"] as! String
                let documentsCount = chapterDetail["Documentscount"] as! Int
                let assignmentsCount = chapterDetail["AssignmentCount"] as! Int
                let documentsAndAssignmentsCount = "\(documentsCount) Documents, \(assignmentsCount) Assignments"
                //                        let isMaterialPathDelete = chapterDetail["IsDeleteMaterialPath"] as! Bool
                self.tableData.append(CourseDataModel(chapterName: chapterName, documentAndAssignmentCount: documentsAndAssignmentsCount))
            }
            print(self.tableData.count)
        case .failure(let error):
            print(error.localizedDescription)
        }
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    tableView.beginUpdates()
    tableView.endUpdates()
    print("Selected Section Index is : \(section)")
}

Это все, что у меня есть, я пробовал это в течение последних 2 дней, я не могу понять это.

Ответы [ 3 ]

0 голосов
/ 19 сентября 2018

Это число строк, одинаковое для всех разделов.

Попробуйте следующий код:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if sectionData[section].opened {
        return tableData.count
    }
    return 0
}

и нет необходимости:

tableView.beginUpdates()
tableView.endUpdates()

в toggleSection Я думаю.

РЕДАКТИРОВАТЬ

Попробуйте еще один подход:

В fun toggleSection:

for courseNamesModel in sectionData {
    courseNamesModel.opened = false
}
sectionData[section].opened = !sectionData[section].opened

поскольку вы должны установить false для ранее открытых заголовков.

0 голосов
/ 19 сентября 2018

Вы должны изменить дизайн вашей модели.Модель должна отражать ваш пользовательский интерфейс.Так как раздел содержит N строк, значит, ваша модель раздела должна иметь Array из Row Model.Таким образом, вы можете легко подать список строк для конкретного Section.Управление Section & Row в двух разных Array является головной болью для управления.

Например.

struct SectionModel {
    var opened: Bool!
    var yourTableRowModels = [RowModel]()
}

struct RowModel {
   var someAttribute: String!
}

Теперь в ваших TableViewDataSource методах используйте подход ниже.

class YourViewController: UIViewController {
    var sections = [SectionModel]()

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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section].yourTableRowModels.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let rowModel = sections[indexPath.section].yourTableRowModels[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "dataCell") as! DataCell
        cell.chapterName.text = rowModel.someAttribute
        cell.numberOfDocumentsAndAssignments.text = rowModel.someAttribute
        return cell
    }
}
0 голосов
/ 19 сентября 2018

У вас есть несовместимый источник данных для табличного представления. Каждый раздел должен иметь свой собственный var tableData = [CourseDataModel](), поэтому в nubmerOfRowsInSection вы должны иметь:

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

Всегда обновляйте источник данных в главной очереди. Сделайте это прямо перед вызовом reloadData, поэтому он должен выглядеть следующим образом:

DispatchQueue.main.async {
    self.sectionData.append...
    // or self.tableData.append...
    self.tableView.reloadData()
    // or reload section
}
...