iOS 这种情况的最佳设计解决方案是什么?
What is the best design solution for this situation in iOS?
我 UITableView
有两个静态单元格。当我在单元格中填写文本字段时,每个单元格都有自定义 class 并独立验证帐户名称。 (这部分代码我按原样得到,不允许重写)。如果验证对委托 (SocialFeedSelectCellDelegate
) 正确,则单元格委托更改。本来这个tableView只在SignUpViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, SocialFeedSelectCellDelegate
出现过。
问题:同一个UITableView
应该出现在两个不同的地方(SignUpViewController
和SettingsViewController
)。另外 SignUpViewController
和 SettingsViewController
应该知道帐户验证的成功或失败。
我尝试了什么:我为带有两个单元格的 tableView 创建了 SocialFeedTableViewController: UITableViewController, SocialFeedSelectCellDelegate
。将 SocialFeedTableViewController
中的视图设置为 SignUpViewController
和 SettingsViewController
的容器视图。我使用了第二次委托(从 SocialFeedTVC 到注册和设置)来通知注册和设置有关验证更改的信息。我认为这是个坏主意,因为双重授权。队友跟我说很难懂
问题:问题的最佳和简单设计解决方案是什么?
为什么双重授权是个问题?据我所知,您有 2 个 table 视图,每个控制器 1 个。然后每个控制器将每个 table 视图的委托设置为自己。即使不是,在运行时更改对象的委托也很常见。具有相同协议的 2 个委托属性也很正常,只是为了能够将消息转发给 2 个或更多对象。
还有很多选择。您可以使用默认的通知中心,并能够通过这种方式转发消息。唯一的坏处是您需要从通知中心显式退出通知侦听器。
在您的案例中,另一个更有趣的过程是创建一个模型(class),该模型保存来自 table 视图的数据,并且还实现来自单元格的协议。然后模型应该作为 属性 转发到新的视图控制器。如果视图控制器仍然需要在 table 视图之外刷新,那么模型应该为视图控制器本身包含另一个协议。
举个例子:
protocol ModelProtocol: NSObjectProtocol {
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?)
}
class DelegateSystem {
class Model: NSObject, UITableViewDelegate, UITableViewDataSource, ModelProtocol {
// My custom cell class
class MyCell: UITableViewCell {
weak var modelDelegate: ModelProtocol?
var indexPath: NSIndexPath?
func onTextChanged(field: UITextField) { // just an example
modelDelegate?.cellDidUpdateText(self, text: field.text) // call the cell delegate
}
}
// some model values
var firstTextInput: String?
var secondTextInput: String?
// a delegate method from a custom protocol
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?) {
// update the appropriate text
if cell.indexPath?.row == 0 {
self.firstTextInput = text
} else {
self.secondTextInput = text
}
}
// table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = MyCell() // create custom cell
cell.indexPath = indexPath // We want to keep track of the cell index path
// assign from appropriate text
if cell.indexPath?.row == 0 {
cell.textLabel?.text = self.firstTextInput
} else {
cell.textLabel?.text = self.secondTextInput
}
cell.modelDelegate = self // set the delegate
return cell
}
}
// The first view controller class
class FirstViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
let model = Model() // generate the new model
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when second view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// probably from some button or keyboard done pressed
func presentSecondController() {
let controller = SecondViewController() // create the controller
controller.model = model // assign the same model
self.navigationController?.pushViewController(controller, animated: true) // push it
}
}
// The second view controller class
class SecondViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
var model: Model? // the model assigned from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when third view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// from back button for instance
func goBack() {
self.navigationController?.popViewControllerAnimated(true)
}
}
}
这里的 2 个视图控制器将与同一个对象通信,该对象也实现了 table 视图协议。我不建议你把所有这些都放在一个文件中,但正如你所看到的,两个视图控制器都非常干净,模型接管了所有繁重的工作。该模型可能有另一个委托,然后由视图控制器本身使用它来转发其他信息。当视图确实出现时,控制器应该 "steal" 来自模型的委托槽。
我希望这可以帮助您了解委托并不是一维的,并且可以用它们做很多事情。
我 UITableView
有两个静态单元格。当我在单元格中填写文本字段时,每个单元格都有自定义 class 并独立验证帐户名称。 (这部分代码我按原样得到,不允许重写)。如果验证对委托 (SocialFeedSelectCellDelegate
) 正确,则单元格委托更改。本来这个tableView只在SignUpViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, SocialFeedSelectCellDelegate
出现过。
问题:同一个UITableView
应该出现在两个不同的地方(SignUpViewController
和SettingsViewController
)。另外 SignUpViewController
和 SettingsViewController
应该知道帐户验证的成功或失败。
我尝试了什么:我为带有两个单元格的 tableView 创建了 SocialFeedTableViewController: UITableViewController, SocialFeedSelectCellDelegate
。将 SocialFeedTableViewController
中的视图设置为 SignUpViewController
和 SettingsViewController
的容器视图。我使用了第二次委托(从 SocialFeedTVC 到注册和设置)来通知注册和设置有关验证更改的信息。我认为这是个坏主意,因为双重授权。队友跟我说很难懂
问题:问题的最佳和简单设计解决方案是什么?
为什么双重授权是个问题?据我所知,您有 2 个 table 视图,每个控制器 1 个。然后每个控制器将每个 table 视图的委托设置为自己。即使不是,在运行时更改对象的委托也很常见。具有相同协议的 2 个委托属性也很正常,只是为了能够将消息转发给 2 个或更多对象。
还有很多选择。您可以使用默认的通知中心,并能够通过这种方式转发消息。唯一的坏处是您需要从通知中心显式退出通知侦听器。
在您的案例中,另一个更有趣的过程是创建一个模型(class),该模型保存来自 table 视图的数据,并且还实现来自单元格的协议。然后模型应该作为 属性 转发到新的视图控制器。如果视图控制器仍然需要在 table 视图之外刷新,那么模型应该为视图控制器本身包含另一个协议。
举个例子:
protocol ModelProtocol: NSObjectProtocol {
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?)
}
class DelegateSystem {
class Model: NSObject, UITableViewDelegate, UITableViewDataSource, ModelProtocol {
// My custom cell class
class MyCell: UITableViewCell {
weak var modelDelegate: ModelProtocol?
var indexPath: NSIndexPath?
func onTextChanged(field: UITextField) { // just an example
modelDelegate?.cellDidUpdateText(self, text: field.text) // call the cell delegate
}
}
// some model values
var firstTextInput: String?
var secondTextInput: String?
// a delegate method from a custom protocol
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?) {
// update the appropriate text
if cell.indexPath?.row == 0 {
self.firstTextInput = text
} else {
self.secondTextInput = text
}
}
// table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = MyCell() // create custom cell
cell.indexPath = indexPath // We want to keep track of the cell index path
// assign from appropriate text
if cell.indexPath?.row == 0 {
cell.textLabel?.text = self.firstTextInput
} else {
cell.textLabel?.text = self.secondTextInput
}
cell.modelDelegate = self // set the delegate
return cell
}
}
// The first view controller class
class FirstViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
let model = Model() // generate the new model
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when second view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// probably from some button or keyboard done pressed
func presentSecondController() {
let controller = SecondViewController() // create the controller
controller.model = model // assign the same model
self.navigationController?.pushViewController(controller, animated: true) // push it
}
}
// The second view controller class
class SecondViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
var model: Model? // the model assigned from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when third view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// from back button for instance
func goBack() {
self.navigationController?.popViewControllerAnimated(true)
}
}
}
这里的 2 个视图控制器将与同一个对象通信,该对象也实现了 table 视图协议。我不建议你把所有这些都放在一个文件中,但正如你所看到的,两个视图控制器都非常干净,模型接管了所有繁重的工作。该模型可能有另一个委托,然后由视图控制器本身使用它来转发其他信息。当视图确实出现时,控制器应该 "steal" 来自模型的委托槽。
我希望这可以帮助您了解委托并不是一维的,并且可以用它们做很多事情。