我面临着将数据动态加载到UITableView
节中的问题。我的业务需求是我有一个名为“课程”的ViewController
,在此视图中,我有一个tableView
,其中我使用了TableViewHeaderFooterView
的不同部分,每个标题都有相关的课程名称,该课程中的章节数量以及该课程的作业数量,我从API调用中获取所有这些数据,我可以使用此数据填充tableView
标头,并且我还将获得一个'id'对于我添加为每个标题的标签的每个课程。现在,当我点击任意一个标头时,我必须通过发送标头的tag值(即courseID)进行另一个API调用,因此我将获得tableView的数据源,并且应该使用显示行和数据的方式扩展该部分在数据源的行中。
在点击标题之前,我可以使用具有数据源的静态数据来完成所有这些操作,但是我不知道如果要在点击标题时动态添加数据,该怎么做。
我试图这样做,当我第一次单击任何标题时,它正在显示该部分的数据,但是如果再次单击相同的标题或任何其他标题,我会因< / p>
由于未捕获的异常“ 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(expand / Collapse)委托功能的代码:
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天我一直在尝试此操作,我无法弄清楚。
答案 0 :(得分:2)
您的表格视图的数据源不一致。每个部分都应该有自己的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
}
答案 1 :(得分:1)
您应该重新设计模型。
模型必须反映您的UI。由于节包含N行,因此您的节模型应具有Array
的{{1}}。因此,您可以轻松地归档特定Row Model
的行列表。在两个不同的Section
中管理Section & Row
令人头疼。
例如。
Array
现在您的struct SectionModel {
var opened: Bool!
var yourTableRowModels = [RowModel]()
}
struct RowModel {
var someAttribute: String!
}
方法中使用以下方法。
TableViewDataSource
答案 2 :(得分:0)
这是所有部分的行数。
尝试以下代码:
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。