注意:我知道这是重新发布的;我几天前发布了同样的问题sans-code,可以理解的是,它已经关闭了。我编辑了问题以添加一些相关的片段,但问题没有重新打开,所以我在这里重新发布。如果这不是正确的方法,请告诉我!
我有一个具有两种模式的应用程序,并且每种模式的设置屏幕略有不同(其中一种模式中的一个附加部分,行数,不同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
...
}
}
关于会导致这种情况的任何想法?我的方法是否正确;有更好的方法吗?
答案 0 :(得分: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的dataSource和委托插座。相反,它在运行时完成,如上面的示例代码所示。
请注意,您可能不需要实现UITableViewDelegate,在这种情况下,您可以忽略上面示例代码中对UITableViewDelegate及其方法的引用。或者,如果UITableViewDelegate实现(例如didSelectRow方法)对于您的应用程序可以运行的两种模式是相同的,您可以在视图控制器类中实现它,在这种情况下,您可以连接{{1您的表的出口直接在故事板中查看您的视图控制器。
答案 1 :(得分:0)
您对UITableView和UITableViewController如何协同工作有误解。 UITableView需要一个UITableViewDataSource来为它提供底层数据的细节(部分数量,行数和实际单元格等)。这就是UITableViewController的作用(它符合UITableViewDataSource)。因此,如果为tableView调用cellForRow,那么它将调用它的数据源cellForRow方法来获取它。
所以在你的代码中执行此操作时: return(tableView?.cellForRow(at:indexPath))!
您的表视图调用其数据源,即UITableViewController并调用表视图cellForRow,依此类推。您刚刚进入了一个递归循环,最终会因您看到的错误而被终止。
至于你的整体方法,我会选择两条UITableViewControllers路由,因为它分隔了两者之间的不同逻辑,使得理解和维护更容易,并且还允许更多的重用。
至于如何使用故事板,它在很大程度上取决于你如何在两种模式之间切换,但实质上你可以设置segue在两个控制器之间切换。