为什么我们需要将委托设置为自我?为什么编译器不默认它?
Why do we need to set delegate to self? Why isn't it defaulted by the compiler?
我认为我完全理解授权的概念,我的问题是当我们这样做时:
class someViewController : UIViewController, UITableViewDelegate{
}
我们是否曾经可能不想将tableView.delegate
设置为self
?
如果没有任何机会,那为什么 Xcode 强迫我们在这里做一些额外的工作?
如果有可能将 tableView.delegate
设置为 self
以外的其他值...那是什么?能否提供一些例子?
would it ever be possible that we wouldn't want to set
tableView.delegate to self
是的,如果您在自己的数据源和委托中实现 class。
why is Xcode forcing us to do some extra work here?
在我们的架构中允许尽可能多的自由。
数据源和委托的单独 classes 示例。
class TableViewDatasource: NSObject, UITableViewDataSource {
// implement datasource
}
class TableViewDelegate : NSObject, UITableViewDelegate {
// implement delegate
}
class ViewController: UIViewController {
IBOutlet weak var tableView: UITableView! {
didSet {
tableView.dataSource = tableViewDatasource
tableView.delegate = tableViewDelegate
}
}
let tableViewDatasource = TableViewDatasource()
let tableViewDelegate = TableViewDelegate()
}
如果您允许视图控制器持有委托和数据源的不同实现,这将允许更高的可重用性并有利于组合而不是继承。
你处理的是更小的 classes,它们更容易测试和维护。
甚至可以设计完整的应用程序,不需要任何视图控制器子classes。
datasource/delegate设计可以随心所欲。
作为示例,我想向您展示我的一个新项目:TaCoPopulator。它是一个框架,通过将其拆分为不同的任务以遵循 SOLID 原则,透明地填充 table 视图和集合视图:
import UIKit
class CollectionViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private var datasource: ViewControllerDataSource<UICollectionView>?
override func viewDidLoad() {
super.viewDidLoad()
self.datasource = ViewControllerDataSource(with: collectionView)
}
}
import UIKit
class TableViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private var datasource: ViewControllerDataSource<UITableView>?
override func viewDidLoad() {
super.viewDidLoad()
self.datasource = ViewControllerDataSource(with: tableView)
}
}
import TaCoPopulator
class IntDataProvider: SectionDataProvider<Int> {
override init(reuseIdentifer: @escaping (Int, IndexPath) -> String) {
super.init(reuseIdentifer: reuseIdentifer)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
self.provideElements([1,2,3,4,5,6,7,8,9])
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
self.provideElements(self.elements() + [10, 11, 12, 13, 14, 15])
}
}
}
}
import TaCoPopulator
class ViewControllerDataSource<TableOrCollectionView: PopulatorView> {
init(with populatorView: PopulatorView) {
self.populatorView = populatorView
setup()
}
var intSelected: ((Int, IndexPath) -> Void)?
var stringSelected: ((String, IndexPath) -> Void)?
let dp1 = IntDataProvider {
_ in return "Cell"
}
let dp2 = StringDataProvider {
_ in return "Cell"
}
weak var populatorView: PopulatorView?
var populator: Populator<MyViewPopulator>?
func setup(){
dp1.selected = {
[weak self] element, indexPath in
self?.intSelected?(element, indexPath)
}
dp2.selected = {
[weak self] element, indexPath in
self?.stringSelected?(element, indexPath)
}
let collectionViewCellConfig: (Any, TextCollectionViewCell, IndexPath) -> TextCollectionViewCell = {
element, cell, _ in
cell.textLabel?.text = "\(element)"
return cell
}
let tableViewViewCellConfig: (Any, UITableViewCell, IndexPath) -> UITableViewCell = {
element, cell, _ in
cell.textLabel?.text = "\(element)"
return cell
}
if let populatorView = populatorView as? UICollectionView {
let section1factory = SectionCellsFactory<Int, TextCollectionViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: collectionViewCellConfig)
let section2factory = SectionCellsFactory<String, TextCollectionViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: collectionViewCellConfig)
self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
}
if let populatorView = populatorView as? UITableView {
let section1factory = SectionCellsFactory<Int, UITableViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: tableViewViewCellConfig)
let section2factory = SectionCellsFactory<String, UITableViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: tableViewViewCellConfig)
self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
}
}
}
would it ever be possible that we wouldn't want to set tableView.delegate to self?
什么是 tableView.delegate
?您的 class someViewController
不是 UITableViewController
的子 class(并且没有 tableView
属性)。它甚至不知道您在处理 table 视图。
您的类型声明符合 UITableViewDelegate
的事实并不能说明应该在哪个实际 table 视图实例上设置它。
If there is a chance that tableView.delegate is set to something other than self...well what is that?
通常将功能拆分为多种类型以降低视图控制器的复杂性是明智的。您的 someViewController
可能 有一个 viewModel
属性 来处理与 table 视图有关的所有事情。
当在想要的 ViewController 中提到 tableView.delegate = self
或 tableView.dataSource = self
时,这意味着 ViewController 在说 "I am responsible for implementing those delegation/dataSource methods",这意味着 [=44] =] (self
) 负责提供所需的方法让 tableView 知道它应该如何 looks/behaves.
关于您的问题:
would it ever be possible that we wouldn't want to set
tableView.delegate to self?
实际上这是可能的,但这会导致 tableView 显示为空 tableView(其中没有行),因为没有人告诉它应该如何 looks/behave。
If there is a chance that tableView.delegate is set to something other
than self...well what is that? Can you please provide some examples?
是的,tableView。dataSource/delegate 不必分配给包含此 tableView 的同一个 Viewcontroller(但我发现它更具可读性和可理解性)。
例如:
在下面的代码片段中,我将 tableView 的数据源分配给另一个 class(甚至不是 UIViewController)在不同的 。swift 文件,它完全可以正常工作:
import UIKit
// ViewController File
class ViewController: UIViewController {
var handler: Handler!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
handler = Handler()
tableView.dataSource = handler
}
}
处理程序Class:
import UIKit
class Handler:NSObject, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell")
cell?.textLabel?.text = "row #\(indexPath.row + 1)"
return cell!
}
}
输出工作正常。
我认为我完全理解授权的概念,我的问题是当我们这样做时:
class someViewController : UIViewController, UITableViewDelegate{
}
我们是否曾经可能不想将tableView.delegate
设置为self
?
如果没有任何机会,那为什么 Xcode 强迫我们在这里做一些额外的工作?
如果有可能将 tableView.delegate
设置为 self
以外的其他值...那是什么?能否提供一些例子?
would it ever be possible that we wouldn't want to set tableView.delegate to self
是的,如果您在自己的数据源和委托中实现 class。
why is Xcode forcing us to do some extra work here?
在我们的架构中允许尽可能多的自由。
数据源和委托的单独 classes 示例。
class TableViewDatasource: NSObject, UITableViewDataSource {
// implement datasource
}
class TableViewDelegate : NSObject, UITableViewDelegate {
// implement delegate
}
class ViewController: UIViewController {
IBOutlet weak var tableView: UITableView! {
didSet {
tableView.dataSource = tableViewDatasource
tableView.delegate = tableViewDelegate
}
}
let tableViewDatasource = TableViewDatasource()
let tableViewDelegate = TableViewDelegate()
}
如果您允许视图控制器持有委托和数据源的不同实现,这将允许更高的可重用性并有利于组合而不是继承。
你处理的是更小的 classes,它们更容易测试和维护。
甚至可以设计完整的应用程序,不需要任何视图控制器子classes。
datasource/delegate设计可以随心所欲。
作为示例,我想向您展示我的一个新项目:TaCoPopulator。它是一个框架,通过将其拆分为不同的任务以遵循 SOLID 原则,透明地填充 table 视图和集合视图:
import UIKit
class CollectionViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private var datasource: ViewControllerDataSource<UICollectionView>?
override func viewDidLoad() {
super.viewDidLoad()
self.datasource = ViewControllerDataSource(with: collectionView)
}
}
import UIKit
class TableViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private var datasource: ViewControllerDataSource<UITableView>?
override func viewDidLoad() {
super.viewDidLoad()
self.datasource = ViewControllerDataSource(with: tableView)
}
}
import TaCoPopulator
class IntDataProvider: SectionDataProvider<Int> {
override init(reuseIdentifer: @escaping (Int, IndexPath) -> String) {
super.init(reuseIdentifer: reuseIdentifer)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
self.provideElements([1,2,3,4,5,6,7,8,9])
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
self.provideElements(self.elements() + [10, 11, 12, 13, 14, 15])
}
}
}
}
import TaCoPopulator
class ViewControllerDataSource<TableOrCollectionView: PopulatorView> {
init(with populatorView: PopulatorView) {
self.populatorView = populatorView
setup()
}
var intSelected: ((Int, IndexPath) -> Void)?
var stringSelected: ((String, IndexPath) -> Void)?
let dp1 = IntDataProvider {
_ in return "Cell"
}
let dp2 = StringDataProvider {
_ in return "Cell"
}
weak var populatorView: PopulatorView?
var populator: Populator<MyViewPopulator>?
func setup(){
dp1.selected = {
[weak self] element, indexPath in
self?.intSelected?(element, indexPath)
}
dp2.selected = {
[weak self] element, indexPath in
self?.stringSelected?(element, indexPath)
}
let collectionViewCellConfig: (Any, TextCollectionViewCell, IndexPath) -> TextCollectionViewCell = {
element, cell, _ in
cell.textLabel?.text = "\(element)"
return cell
}
let tableViewViewCellConfig: (Any, UITableViewCell, IndexPath) -> UITableViewCell = {
element, cell, _ in
cell.textLabel?.text = "\(element)"
return cell
}
if let populatorView = populatorView as? UICollectionView {
let section1factory = SectionCellsFactory<Int, TextCollectionViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: collectionViewCellConfig)
let section2factory = SectionCellsFactory<String, TextCollectionViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: collectionViewCellConfig)
self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
}
if let populatorView = populatorView as? UITableView {
let section1factory = SectionCellsFactory<Int, UITableViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: tableViewViewCellConfig)
let section2factory = SectionCellsFactory<String, UITableViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: tableViewViewCellConfig)
self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
}
}
}
would it ever be possible that we wouldn't want to set tableView.delegate to self?
什么是 tableView.delegate
?您的 class someViewController
不是 UITableViewController
的子 class(并且没有 tableView
属性)。它甚至不知道您在处理 table 视图。
您的类型声明符合 UITableViewDelegate
的事实并不能说明应该在哪个实际 table 视图实例上设置它。
If there is a chance that tableView.delegate is set to something other than self...well what is that?
通常将功能拆分为多种类型以降低视图控制器的复杂性是明智的。您的 someViewController
可能 有一个 viewModel
属性 来处理与 table 视图有关的所有事情。
当在想要的 ViewController 中提到 tableView.delegate = self
或 tableView.dataSource = self
时,这意味着 ViewController 在说 "I am responsible for implementing those delegation/dataSource methods",这意味着 [=44] =] (self
) 负责提供所需的方法让 tableView 知道它应该如何 looks/behaves.
关于您的问题:
would it ever be possible that we wouldn't want to set tableView.delegate to self?
实际上这是可能的,但这会导致 tableView 显示为空 tableView(其中没有行),因为没有人告诉它应该如何 looks/behave。
If there is a chance that tableView.delegate is set to something other than self...well what is that? Can you please provide some examples?
是的,tableView。dataSource/delegate 不必分配给包含此 tableView 的同一个 Viewcontroller(但我发现它更具可读性和可理解性)。
例如:
在下面的代码片段中,我将 tableView 的数据源分配给另一个 class(甚至不是 UIViewController)在不同的 。swift 文件,它完全可以正常工作:
import UIKit
// ViewController File
class ViewController: UIViewController {
var handler: Handler!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
handler = Handler()
tableView.dataSource = handler
}
}
处理程序Class:
import UIKit
class Handler:NSObject, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell")
cell?.textLabel?.text = "row #\(indexPath.row + 1)"
return cell!
}
}
输出工作正常。