在 UITableViewController 中以编程方式切换 UITableView

Posted

技术标签:

【中文标题】在 UITableViewController 中以编程方式切换 UITableView【英文标题】:Programmatically switch UITableView in UITableViewController 【发布时间】:2017-12-28 20:46:18 【问题描述】:

注意:我知道这是重新发布;几天前我发布了同样的问题,无代码,可以理解的是,它已关闭。我编辑了问题以添加一些相关的 sn-ps,但问题没有重新打开,所以我在这里重新发布。如果这不是正确的方法,请告诉我!

我有一个具有两种模式的应用程序,每种模式的设置屏幕略有不同(其中一种模式中的一个附加部分、行数的一些差异、不同的 UserDefaults 键等)。过去,我使用 switch 和 if 语句实现了这一点,但为了使事情更易于维护,我正在研究将这两种模式拆分为各自独立类的方法。最初我考虑制作两个单独的UITableViewController 子类,但是我很难考虑如何将其与故事板等一起使用。然后我想使用两个单独的UITableView 子类,并根据viewDidLoad 中的模式选择显示哪一个。

但是,我在使用这种方法时遇到了问题。我进行了设置,以便控制器的 cellForRow 方法调用 TableView 的 cellForRow 方法,但这就是问题所在。尝试执行dequeueReusableCell 时,应用程序崩溃,并在该行出现模糊的“EXC_BAD_INSTRUCTION”错误。

以下是一些相关代码:

ViewController.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
        ...
        

有什么想法会导致这种情况吗?我的方法是否正确?有没有更好的方法来做到这一点?

【问题讨论】:

【参考方案1】:

您可以保留一个 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 的值取决于应用程序运行的模式,它决定了将使用哪个类作为数据源表格视图。

情节提要设置很简单:一个场景 SettingsViewController,以及该场景中的一个库存 UITableView。

请注意,上面的SettingsViewController 是 UIViewController 的子类,而不是 UITableViewController,因为数据源和委托协议在不同的类中实现并在运行时确定。这将需要您手动连接情节提要中的tableView 插座。但是,您不会在情节提要中连接 UITableView 的数据源和委托出口。相反,它是在运行时完成的,如上面的示例代码所示。

请注意,您可能不需要实现 UITableViewDelegate,在这种情况下,您可以忽略上面示例代码中对 UITableViewDelegate 及其方法的引用。或者,如果你的 UITableViewDelegate 实现(例如 didSelectRow 方法)对于你的应用程序可以运行的两种模式是相同的,你可以在你的视图控制器类中实现它,在这种情况下你可以连接delegate在情节提要中,将您的表格视图直接输出到您的视图控制器。

【讨论】:

【参考方案2】:

您对 UITableView 和 UITableViewController 如何协同工作存在误解。 UITableView 需要一个 UITableViewDataSource 来为其提供底层数据的详细信息(部分数、行数和实际单元格等)。这就是 UITableViewController 所做的(它符合 UITableViewDataSource)。因此,如果您为 tableView 调用 cellForRow,那么它将调用它的数据源 cellForRow 方法来获取。

所以当你这样做时,在你的代码中: return (tableView?.cellForRow(at: indexPath))!

您的表格视图调用它的数据源,即您的 UITableViewController 并调用表格视图 cellForRow 等等。您刚刚进入了一个递归循环,该循环最终因您看到的错误而被终止。

至于你的整体方法,我会沿着两条 UITableViewControllers 路线走,因为这将两者之间的不同逻辑分开,从而更容易理解和维护,也允许更多的重用。

至于故事板如何工作,很大程度上取决于您如何在两种模式之间切换,但本质上您可以设置 segue 以在两个控制器之间切换。

【讨论】:

以上是关于在 UITableViewController 中以编程方式切换 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

在导航栏中使用 UITableViewController

在 UINavigationController 中显示 UITableViewController

在 UIPopoverController 中显示 UItableViewController

在 UITableViewController 中没有调用 viewWillAppear?

如何在 UITableViewController 中显示 MSMutableArray 数据?

没有在 UITableViewController 中调用 DidSelectRowAt