UICollectionView:如何在委托方法中设置特定项目后获取特定项目的项目大小

Sar*_*dag 12 objective-c ios6 uicollectionview

我正在摆弄新的UICollectionView和UICollectionViewLayout类.我创建了一个自定义布局,子类化UICollectionViewFlowLayout.

我的单元格大小正在动态变化,我使用下面的委托方法设置项目大小

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

      NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
      return CGSizeMake(80, 80);
}
Run Code Online (Sandbox Code Playgroud)

现在,在我的自定义UICollectionViewFlowLayout类的prepareLayout方法下,我需要访问这些大小变量,以便我可以计算如何放置它们并为layoutAttributesForItemAtIndexPath缓存它们.

但是,我似乎无法在UICollectionView或UICollectionViewFlowLayout下找到任何属性来达到我在委托方法中设置的自定义项目大小.

Sar*_*dag 13

自己找到了.

实现自定义类,而不是省略 UICollectionViewDelegateFlowLayout

@interface SECollectionViewCustomLayout : UICollectionViewFlowLayout 
                                          <UICollectionViewDelegateFlowLayout>
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话

CGSize size = [self collectionView:self.collectionView 
                            layout:self 
            sizeForItemAtIndexPath:indexPath];
Run Code Online (Sandbox Code Playgroud)

  • -1使布局成为自己的委托是一个奇怪的模式.即使你确实想要这样做,你也应该通过调用`self.collectionView.delegate`来获取大小信息,而不是假设`self`将永远是委托.如果您以后决定另一个对象应该是委托怎么办?你最终可能会遇到一些代码,这些代码会向实际代表返回不同的大小.即使删除与委托协议的一致性也不会导致编译器警告,因为`self`无论如何都会实现该方法. (4认同)

Stu*_*art 8

查看各种UICollectionView...头文件,并观看WWDC 2012 Session 219 - 高级集合视图和构建自定义布局视频(从大约6:50开始),似乎可扩展委托模式利用动态类型来确保布局可以正确访问它的扩展委托方法.


简而言之...

  1. 如果使用自己的委托定义自定义布局,请在布局的头文件中定义该委托协议.
  2. 您的委托对象(通常UI(Collection)ViewController是管理集合视图的对象)应声明自己支持此自定义协议.
    • 如果你的布局只是它的一个UICollectionViewFlowLayout或子类,这只是意味着声明符合UICollectionViewDelegateFlowLayout.
    • .m如果您不想#import将布局标题放入委托的界面,请随意在文件的类扩展中执行此操作.
  3. 要从布局访问委托方法,请调用集合视图的委托.
    • 使用layout的collectionView属性,并将委托转换为符合所需协议的对象以说服编译器.
    • respondsToSelector:在调用可选的委托方法之前,不要忘记像往常一样检查委托.实际上,如果您愿意,对所有方法执行此操作没有任何害处,因为类型转换意味着没有运行时保证委托甚至可以实现所需的方法.


在代码中......

因此,如果您实现一个需要委托来获取其某些信息的自定义布局,那么您的标题可能如下所示:

@protocol CollectionViewDelegateCustomLayout <UICollectionViewDelegate>
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface CustomLayout : UICollectionViewLayout
// ...
@end
Run Code Online (Sandbox Code Playgroud)


你的代表声明了一致性(我在这里的实现文件中已经这样做了):

#import "CustomLayout.h"

@interface MyCollectionViewController () <CollectionViewDelegateCustomLayout>
@end

@implementation
// ...
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath
{
    return [self canDoSomethingMindblowing];
}
// ...
@end
Run Code Online (Sandbox Code Playgroud)


在您的布局实现中,您可以访问以下方法:

BOOL blowMind;
if ([self.collectionView.delegate respondsToSelector:@selecor(collectionView:layout:shouldDoSomethingMindblowingAtIndexPath:)]) {
    blowMind = [(id<CollectionViewDelegateCustomLayout>)self.collectionView.delegate collectionView:self.collectionView
                                                                                             layout:self
                                                            shouldDoSomethingMindblowingAtIndexPath:indexPath];
} else {
    // Perhaps the layout also has a property for this, if the delegate
    // doesn't support dynamic layout properties...?
    // blowMind = self.blowMind;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在这里进行类型转换是安全的,因为我们正在检查委托事先是否事先响应了该方法.


证据...

这只是猜测,但我怀疑Apple是如何管理UICollectionViewDelegateFlowLayout协议的.

  • delegate流布局中没有属性,因此调用必须通过集合视图的委托进行.
  • UICollectionViewController 不公开符合扩展流布局委托(我怀疑它在另一个私有标头中这样做).
  • UICollectionViewdelegate属性仅声明符合"基础" UICollectionViewDelegate协议.同样,我怀疑UICollectionView流布局正在使用私有子类/类别以防止需要进行类型转换.为了进一步增加这一点,苹果公司不鼓励UICollectionView在文档中进行子类化(适用于iOS的集合视图编程指南:创建自定义布局):

避免继承UICollectionView.集合视图很少或没有自己的外观.相反,它从数据源对象中提取其所有视图,并从布局对象中提取所有与布局相关的信息.

我们去了.并不复杂,但值得知道如何做到范式友好的方式.


tou*_*bun 6

有一个快速版本:

self.collectionView(self.collectionView, layout: self.collectionView.collectionViewLayout, sizeForItemAtIndexPath: indexPath)
Run Code Online (Sandbox Code Playgroud)