我正在尝试使用tableview创建可折叠菜单。调用return self.sideBars[section].movies!.count
时,我在func numberOfRowsInSection中收到错误。我没有得到的是当我选择配置文件(其中一个选项卡)return self.sideBars[section].movies!.count
被调用并且它完美运行并显示数组时。但是当我选择设置或选项(其他选项卡)时,我得到一个错误索引超出范围。我不确定为什么会这样。我尝试过使用不同的数组进行设置和选项选项卡,但我仍然让索引超出范围。帮助将非常感谢!提前谢谢
这是发生错误的地方
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> (Int) {
let it = String(describing: items[section])
var item = items[section]
guard item.isCollapsible else {
if (it == "ProfileViewModelAboutItem") {
return self.sideBars[section].movies!.count
}
if (it == "ProfileViewModelsettingsItem") {
return self.sideBars[section].movies!.count
}
if (it == "ProfileViewModelOptionsItem") {
return self.sideBars[section].movies!.count
}
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
if (it == "ProfileViewModelAboutItem") {
return self.sideBars[section].movies!.count // This works fine
}
if (it == "ProfileViewModelsettingsItem") {
return self.sideBars[section].movies!.count // This is where error occurs: Index out of range
}
if (it == "ProfileViewModelOptionsItem") {
return self.sideBars[section].movies!.count // Also here, fatal error: Index out of range
}
return item.rowCount
}
}
以下整个代码可以获得更多理解
import Foundation
enum ProfileViewModelItemType {
case profile
case settings
case options
}
protocol ProfileViewModelItem {
var type: ProfileViewModelItemType { get }
var sectionTitle: String { get }
var rowCount:(Int) { get }
var isCollapsible: Bool { get }
var isCollapsed: Bool { get set }
}
extension ProfileViewModelItem {
var rowCount: Int {
return 1
}
var isCollapsible: Bool {
return true
}
}
class ProfileViewModel: NSObject {
var items = [ProfileViewModelItem]()
var sideBars = [sideBar]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
let profile = sideBar(movies: [“Followers”, “Following”, “bye”], count: [“1”,”2”,“3”])
let profileItems = ProfileViewModelAboutItem(sideBars: [profile])
self.items.append(profileItems)
let about = sideBar(movies: ["Delete Account", “test”], count: [“1”,”2”])
let settingItem = ProfileViewModelsettingsItem(sideBars: [about])
self.items.append(settingItem)
let option = sideBar(movies: ["about", "FAQ"], count: [“1”,”2”])
let optionsItem = ProfileViewModelOptionsItem(sideBars: [option])
self.items.append(optionsItem)
}
}
extension ProfileViewModel: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> (Int) {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> (Int) {
let it = String(describing: items[section])
var item = items[section]
guard item.isCollapsible else {
if (it == "ProfileViewModelAboutItem") {
return self.sideBars[section].movies!.count
}
if (it == "ProfileViewModelsettingsItem") {
return self.sideBars[section].movies!.count
}
if (it == "ProfileViewModelOptionsItem") {
return self.sideBars[section].movies!.count
}
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
if (it == "ProfileViewModelAboutItem") {
return self.sideBars[section].movies!.count // This works fine
}
if (it == "ProfileViewModelsettingsItem") {
return self.sideBars[section].movies!.count // This is where error occurs: Index out of range
}
if (it == "ProfileViewModelOptionsItem") {
return self.sideBars[section].movies!.count // Also here, fatal error: Index out of range
}
return item.rowCount
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.section]
switch item.type {
case .profile:
if let item = item as? ProfileViewModelAboutItem, let cell = tableView.dequeueReusableCell(withIdentifier: myProfileCell.identifier, for: indexPath) as? myProfileCell {
let title = item.sideBars[indexPath.section].movies[indexPath.row]
cell.profileLabel.text = title
return cell
}
case .settings:
if let item = item as? ProfileViewModelsettingsItem, let cell = tableView.dequeueReusableCell(withIdentifier: settingsCell.identifier, for: indexPath) as? settingsCell {
let title = item.sideBars[indexPath.section].movies[indexPath.row]
cell.settingsLabel.text = title
return cell
}
case .options:
if let item = item as? ProfileViewModelOptionsItem, let cell = tableView.dequeueReusableCell(withIdentifier: optionsCell.identifier, for: indexPath) as? optionsCell {
let title = item.sideBars[indexPath.section].movies[indexPath.row]
cell.optionLabel.text = title
return cell
}
}
return UITableViewCell()
}
}
extension ProfileViewModel: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: HeaderView.identifier) as? HeaderView {
let item = items[section]
headerView.item = item
headerView.section = section
headerView.delegate = self
return headerView
}
return UIView()
}
}
extension ProfileViewModel: HeaderViewDelegate {
func toggleSection(header: HeaderView, section: Int) {
var item = items[section]
if item.isCollapsible {
// Toggle collapse
let collapsed = !item.isCollapsed
item.isCollapsed = collapsed
header.setCollapsed(collapsed: collapsed)
reloadSections?(section)
}
}
}
class ProfileViewModelAboutItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .profile
}
var sectionTitle: String {
return "My Profile"
}
var isCollapsed = true
var sideBars = [sideBar]()
var rowCount: Int {
return self.sideBars.count
}
init(sideBars: [sideBar]) {
self.sideBars = sideBars
}
}
class ProfileViewModelsettingsItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .settings
}
var sectionTitle: String {
return "Settings"
}
var isCollapsed = true
var sideBars = [sideBar]()
var rowCount: Int {
return self.sideBars.count
}
init(sideBars: [sideBar]) {
self.sideBars = sideBars
}
}
class ProfileViewModelOptionsItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .options
}
var sectionTitle: String {
return “Options”
}
var isCollapsed = true
var sideBars = [sideBar]()
var rowCount: Int {
return self.sideBars.count
}
init(sideBars: [sideBar]) {
self.sideBars = sideBars
}
}
sideBar struct
struct sideBar {
var movies: [String]
var count: [String]
init(movies: [String], count: [String]){
self.movies = movies
self.count = count
}
}
答案 0 :(得分:0)
一个非常简单的调试技术将是:
而不是:
return self.sideBars[section].movies!.count
使用:
let tempSideBarsCount = self.sideBars.count
它可以帮助您了解数据和单元格之间是否存在错位。
答案 1 :(得分:0)
问题是您只填充子类的sideBars
数组。 sideBars
类的ProfileViewModel
数组永远不会填充数据,因为您只会在子类的初始值设定项内改变数组。但是,在填充数据时使用超级类的sideBars
,这是一个空数组。
override init() {
super.init()
let profile = sideBar(movies: [“Followers”, “Following”, “bye”], count: [“1”,”2”,“3”])
let profileItems = ProfileViewModelAboutItem(settings: [profile])
self.items.append(profileItems)
self.sideBars.append(profile)
let about = sideBar(movies: ["Delete Account", “test”], count: [“1”,”2”])
let settingItem = ProfileViewModelsettingsItem(sideBars: [about])
self.items.append(settingItem)
self.sideBars.append(about)
let option = sideBar(movies: ["about", "FAQ"], count: [“1”,”2”])
let optionsItem = ProfileViewModelOptionsItem(sideBars: [option])
self.items.append(optionsItem)
self.sideBars.append(option)
}
一些一般建议:请遵循Swift
命名约定,即使用大写字母开始输入名称,例如struct SideBar
,并使用小写的camelcase命名变量名称,例如{{ 1}}。如果我是你,我也会重构我的整个模型,它似乎过于复杂。