我正在为 swift 开发第 3 方框架,所以我不能自己使用
UICollectionViewDelegate
的委托方法,但我确实需要它们来实现一些自定义逻辑。
尝试了多种方法让它发挥作用,包括 method swizzling 但最后我觉得它对我正在做的事情来说太老套了。
现在我正在子类化
UICollectionView
并将委托设置为内部(我的)委托。
这很好用,除非UIViewController
没有实现该方法。
现在我的代码是这样的:
fileprivate class UICollectionViewDelegateInternal: NSObject, UICollectionViewDelegate {
var userDelegate: UICollectionViewDelegate?
override func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) || userDelegate?.responds(to: aSelector) == true
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if userDelegate?.responds(to: aSelector) == true {
return userDelegate
}
return super.forwardingTarget(for: aSelector)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didEnd(item: indexPath.item)
userDelegate?.collectionView?(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
}
class CustomCollectionView: UICollectionView {
private let internalDelegate: UICollectionViewDelegateInternal = UICollectionViewDelegateInternal()
required init?(coder: NSCoder) {
super.init(coder: coder)
super.delegate = internalDelegate
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
super.delegate = internalDelegate
}
func didEnd(item: Int) {
print("internal - didEndDisplaying: \(item)")
}
override var delegate: UICollectionViewDelegate? {
get {
return internalDelegate.userDelegate
}
set {
self.internalDelegate.userDelegate = newValue
super.delegate = nil
super.delegate = self.internalDelegate
}
}
}
在
ViewController
中,我只是使用委托方法进行了简单设置 didEndDisplaying
未实现
是否可以在没有实现 ViewController 的情况下收听
didEndDisplaying
?
编辑1:
这是 ViewController 的代码,让我更清楚我在做什么
class ViewController: UIViewController {
@IBOutlet weak var collectionView: CustomCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
1000
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
// func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print("view controller - did end displaying: \(indexPath.item)")
// }
}
didEndDisplaying
的CustomCollectionView
仅在我取消注释didEndDisplaying
中的ViewController
方法时触发。
我正在寻找的是如果
didEndDisplaying
中的CustomCollectionView
方法未实现,则也触发didEndDisplaying
的ViewController
。
希望现在更清楚一点
编辑2:
发现上面的代码有一些错误,导致复制无法按我的预期进行。更新了上面的代码。 还制作了一个github页面,以便于在这里重现: https://github.com/mees-vdb/InternalCollectionView-Delegate
我对这种方法做了一些阅读,似乎它应该有效 - 但显然,它没有。
玩了一点,这 might 是你的解决方案。
我对您现有的代码做了很少的更改(从 GitHub 上获取 - 如果您想将我添加为协作者,我可以推送一个新分支 [GitHub 上的相同 DonMag ID])。
首先,我实施了
didSelectItemAt
以使其更容易调试(一次只有一个事件)。
ViewController类
class ViewController: UIViewController {
@IBOutlet weak var collectionView: CustomCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
1000
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
// DonMag - comment / un-comment these methods
// to see the difference
//func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print("view controller - did end displaying: \(indexPath.item)")
//}
//func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print("view controller - didSelectItemAt", indexPath)
//}
}
UICollectionViewDelegate内部类
fileprivate class UICollectionViewDelegateInternal: NSObject, UICollectionViewDelegate {
var userDelegate: UICollectionViewDelegate?
override func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) || userDelegate?.responds(to: aSelector) == true
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if userDelegate?.responds(to: aSelector) == true {
return userDelegate
}
return super.forwardingTarget(for: aSelector)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didEnd(item: indexPath.item)
userDelegate?.collectionView?(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didSel(p: indexPath)
userDelegate?.collectionView?(collectionView, didSelectItemAt: indexPath)
}
}
CustomCollectionView 类
// DonMag - conform to UICollectionViewDelegate
class CustomCollectionView: UICollectionView, UICollectionViewDelegate {
private let internalDelegate: UICollectionViewDelegateInternal = UICollectionViewDelegateInternal()
required init?(coder: NSCoder) {
super.init(coder: coder)
super.delegate = internalDelegate
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
super.delegate = internalDelegate
}
func didEnd(item: Int) {
print("internal - didEndDisplaying: \(item)")
}
func didSel(p: IndexPath) {
print("internal - didSelectItemAt", p)
}
// DonMag - these will NEVER be called,
// whether or not they're implemented in
// UICollectionViewDelegateInternal and/or ViewController
// but, when implemented here,
// it allows (enables?) them to be called in UICollectionViewDelegateInternal
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("CustomCollectionView - didEndDisplaying", indexPath)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("CustomCollectionView - didSelectItemAt", indexPath)
}
override var delegate: UICollectionViewDelegate? {
get {
// DonMag - return self instead of internalDelegate.userDelegate
return self
//return internalDelegate.userDelegate
}
set {
self.internalDelegate.userDelegate = newValue
super.delegate = nil
super.delegate = self.internalDelegate
}
}
}
第 1 部分,我以“其他”方式进行。自然代表是您的代表,您跟踪“外部”代表。
在这个例子中,我拦截了
delegate
和dataSource
,但我认为你可以根据需要只拦截一个或另一个。
class TrickCollection: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
weak var outsideDelegate: UICollectionViewDelegate?
weak var outsideDataSource: UICollectionViewDataSource?
override var delegate: UICollectionViewDelegate? {
set { outsideDelegate = newValue }
get { return outsideDelegate }
}
override var dataSource: UICollectionViewDataSource? {
set { outsideDataSource = newValue }
get { return outsideDataSource }
}
第 2 部分,在 bringup 中,劫持代表。
required init?(coder: NSCoder) {
super.init(coder: coder)
common()
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
common()
}
func common() {
super.delegate = self
super.dataSource = self
}
第 3 部分,您现在拥有对代表的完全权力。
然而重要的是要意识到......见下文......
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let k = outsideDataSource?.collectionView(self, numberOfItemsInSection: section) ?? 0
print("yo, i intercepted the count \(k)")
return k
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = outsideDataSource?.collectionView(self, cellForItemAt: indexPath) as! YourCellClass
print("yo, i intercepted a cell \(cell.yourData?.headline)")
return cell
}
然而,重要的是要意识到这是一个“你拥有完全控制权”的设置。 除非您希望,否则不会将任何内容传递给傻瓜“外部”代表。
对我个人来说,其实很懂事很酷。 TrickCollection 不是 UIViewCollection,它是不同的。消费程序员(可能是你自己 :))不应该期望它的行为相同。
不要忘记,从哲学上讲,您在任何时候都输入过像“layoutSubviews”这样常见的东西,您可以选择是否调用 super-layoutSubviews,而且至关重要的是,您在代码中的什么时候调用它.
所以对我来说这不是问题,事实上它是正确的,在此处给出的方法中,所有委托调用在上游都不可用(除非您专门这样做)。
权衡的结果是代码简单而干净。
请注意,这确实与...相同https://stackoverflow.com/a/75997746/294884
如果这种方法对任何人都很方便,那就太好了!
关于集合视图/表视图。我发现,实际上,在现实世界中我从来没有必要! 以这种方式拦截集合/表视图。当使用集合视图时,应用程序不可避免地需要与运动同步的“其他东西”,并且您不想弄乱上游的消费者代码。无论如何。
为方便起见,将整个内容复制/粘贴。
class TrickCollection: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
weak var outsideDelegate: UICollectionViewDelegate?
weak var outsideDataSource: UICollectionViewDataSource?
override var delegate: UICollectionViewDelegate? {
set { outsideDelegate = newValue }
get { return outsideDelegate }
}
override var dataSource: UICollectionViewDataSource? {
set { outsideDataSource = newValue }
get { return outsideDataSource }
}
required init?(coder: NSCoder) {
super.init(coder: coder)
common()
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
common()
}
func common() {
super.delegate = self
super.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let k = outsideDataSource?.collectionView(self, numberOfItemsInSection: section) ?? 0
print("yo, i intercepted the count \(k)")
return k
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = outsideDataSource?.collectionView(self, cellForItemAt: indexPath) as! YourCellClass
print("yo, i intercepted a cell \(cell.yourData?.headline)")
return cell
}
}
同样,您通常只想要两个代表中的一个。