注意:我知道这是一个重新发布的问题;我几天前发布了相同的问题,而且,可以理解,它是关闭的。我编辑了这个问题以添加一些相关的片段,但是问题没有重新打开,所以我在这里重新发布。如果这不是正确的方法,让我知道!
我有一个有两种模式的应用程序,每种模式的设置屏幕都略有不同(其中一种模式中有一个额外的部分,行数上的一些差异,不同的用户默认键等等)。在过去,我用一大堆switch和if语句实现了这一点,但为了使事情更易于维护,我正在研究如何将这两种模式分成各自独立的类。最初,我考虑创建两个独立的UITableViewController子类,但是我很难考虑如何使用故事板等。然后我想使用两个独立的UITableView子类,并根据viewDidLoad中的模式选择要显示的子类。
不过,我对这种方法有意见。我把它设置成控制器的cellForRow方法调用TableView的cellForRow方法,但这就是问题所在。当尝试执行dequeueReusableCell时,应用程序会崩溃,并在该行出现永远模糊的“EXC_BAD_INSTRUCTION”错误。
以下是一些相关代码:
视图控制器.swift

...
override func viewDidLoad()
{
    super.viewDidLoad()

    ...

    tableView = SRScaleSettingsTableView()
}
...
override func tableView(_ tableView: UITableView?, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    return (tableView?.cellForRow(at: indexPath))!
}

SRScaleSettingsTableView.swift设置
override func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
{
    ...
        switch indexPath.section
        {
        case 0:
            ...
            let switchCell = dequeueReusableCell(withIdentifier: "SwitchCell") as! SRSwitchCell
            ^ Debugger breaks on that line with EXC_BAD_INSTRUCTION
            ...

            return switchCell
        ...
        }
}

有什么想法会导致这种情况吗?我的方法是正确的吗?有更好的方法吗?

最佳答案

您可以保留一个UITableView类(您可能根本不需要子类UITableView)和一个UIViewController子类。创建两个实现UITableViewDataSource协议的不同类(也可能是UITableViewDelegate)。这两个类可以以完全不同的方式实现各种委托/数据源方法(例如cellForRowAtIndexPath、numberOfRowsInSection、didSelectRow),以适应应用程序需要运行的不同模式。

protocol SettingsSource: UITableViewDelegate, UITableViewDataSource {
}

class SettingsSourceForModeA: NSObject, SettingsSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)...
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)...
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)...
}

class SettingsSourceForModeB: NSObject, SettingsSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)...
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)...
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)...
}

class SettingsViewController: UIViewController {
    @IBOutlet tableView: UITableView!

    var source: SettingsSource! {
        didSet {
            tableView.dataSource = source
            tableView.delegate = source
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // whatever logic is appropriate to determine the mode
        if appIsRunningInModeA() {
            source = SettingsSourceForModeA()
        } else {
            source = SettingsSourceForModeB()
        }
    }
}

上面代码中的关键细节是SettingsViewController中的source变量,source的值取决于应用程序运行的模式,它决定将哪个类用作表视图的数据源。
故事板的设置很简单:一个场景设置viewcontroller,以及该场景中的一个stock UITableView。
注意,上面的SettingsViewController是UIViewController子类,而不是UITableViewController,因为数据源和委托协议是在单独的类中实现的,并在运行时确定。这将要求您手动连接故事板中的tableView插座。但是,您不会在情节提要中连接UITableView的数据源和委托输出。相反,它是在运行时完成的,如上面的示例代码所示。
注意,您可能不需要实现UITableViewDelegate,在这种情况下,您可以忽略上面示例代码中对UITableViewDelegate及其方法的引用。或者,如果您的UITableViewDelegate实现(例如didSelectRow方法)对于您的应用程序可以运行的两种模式是相同的,那么您可以在视图控制器类中实现它,在这种情况下,您可以将表视图的delegate出口直接连接到脚本中的视图控制器。

10-08 07:31