致命错误:numberOfRowsInSection中的索引输出范围

时间:2017-08-15 08:33:15

标签: ios swift uitableview

我正在尝试使用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        
    }
}

2 个答案:

答案 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}}。如果我是你,我也会重构我的整个模型,它似乎过于复杂。