QA@IT
«質問へ戻る

Layoutの方に引数なしinitを追加

154
本文
 @end
 
 @implementation ReproductionLayout
+- (id)init { // コードで初期化する場合
+    self = [super init];
+    if (self) {
+        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
+    }
+    return self;
+}
 - (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
     self = [super initWithCoder:aDecoder];
     if (self) {

UICollectionViewLayoutのDecorationViewが再利用されない/deallocもされない

UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

  • デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
  • スクロールアウトしたビューについてもdeallocされない
  • スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、

  • UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
  • さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

問題が再現するUICollectionViewLayout実装

@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)init { // コードで初期化する場合
    self = [super init];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end

上記レイアウトを使用する DataSource (UICollectionViewControllerを継承) の実装

@interface MyViewController : UICollectionViewController
@end

@implementation MyViewController

- (id)init {
    return [super initWithCollectionViewLayout:[[ReproductionLayout alloc] init]];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}
@end

追記

DataSource側の実装を追加しました。
ただし、DecorationViewはUICollectionViewLayoutによって提供されるものであるため、
本質的にはDataSourceは無関係です。

UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

- デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
- スクロールアウトしたビューについてもdeallocされない
- スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、

- UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
- さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

### 問題が再現するUICollectionViewLayout実装
```objc
@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)init { // コードで初期化する場合
    self = [super init];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end
```

### 上記レイアウトを使用する DataSource (UICollectionViewControllerを継承) の実装
```objc
@interface MyViewController : UICollectionViewController
@end

@implementation MyViewController

- (id)init {
    return [super initWithCollectionViewLayout:[[ReproductionLayout alloc] init]];
}

- (void)viewDidLoad {
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}
@end
```

### 追記
DataSource側の実装を追加しました。
ただし、DecorationViewはUICollectionViewLayoutによって提供されるものであるため、
本質的にはDataSourceは無関係です。

DataSoruce (UICollectionViewController継承) のソースを追加しました。

154
本文
 - スクロールを戻して再表示された時にも新しいインスタンスが生成される
 
 期待としては、
+
 - UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
 - さもなくば、同じIndexPathについてだけでも…
 
 この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
 もっている人はいますでしょうか?
 
+### 問題が再現するUICollectionViewLayout実装
 ```objc
 @interface ReproductionLayout : UICollectionViewLayout
 @end
 }
 @end
 ```
+
+### 上記レイアウトを使用する DataSource (UICollectionViewControllerを継承) の実装
+```objc
+@interface MyViewController : UICollectionViewController
+@end
+
+@implementation MyViewController
+
+- (id)init {
+    return [super initWithCollectionViewLayout:[[ReproductionLayout alloc] init]];
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+	// Do any additional setup after loading the view.
+    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
+}
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
+    return 1;
+}
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
+    return 30;
+}
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
+    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
+    cell.backgroundColor = [UIColor whiteColor];
+    return cell;
+}
+@end
+```
+
+### 追記
+DataSource側の実装を追加しました。
+ただし、DecorationViewはUICollectionViewLayoutによって提供されるものであるため、
+本質的にはDataSourceは無関係です。

UICollectionViewLayoutのDecorationViewが再利用されない/deallocもされない

UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

  • デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
  • スクロールアウトしたビューについてもdeallocされない
  • スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、

  • UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
  • さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

問題が再現するUICollectionViewLayout実装

@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end

上記レイアウトを使用する DataSource (UICollectionViewControllerを継承) の実装

@interface MyViewController : UICollectionViewController
@end

@implementation MyViewController

- (id)init {
    return [super initWithCollectionViewLayout:[[ReproductionLayout alloc] init]];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}
@end

追記

DataSource側の実装を追加しました。
ただし、DecorationViewはUICollectionViewLayoutによって提供されるものであるため、
本質的にはDataSourceは無関係です。

UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

- デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
- スクロールアウトしたビューについてもdeallocされない
- スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、

- UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
- さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

### 問題が再現するUICollectionViewLayout実装
```objc
@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end
```

### 上記レイアウトを使用する DataSource (UICollectionViewControllerを継承) の実装
```objc
@interface MyViewController : UICollectionViewController
@end

@implementation MyViewController

- (id)init {
    return [super initWithCollectionViewLayout:[[ReproductionLayout alloc] init]];
}

- (void)viewDidLoad {
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}
@end
```

### 追記
DataSource側の実装を追加しました。
ただし、DecorationViewはUICollectionViewLayoutによって提供されるものであるため、
本質的にはDataSourceは無関係です。

質問を投稿

UICollectionViewLayoutのDecorationViewが再利用されない/deallocもされない

UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

  • デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
  • スクロールアウトしたビューについてもdeallocされない
  • スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、

  • UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
  • さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end
UICollectionViewのカスタムレイアウトについて、
Itemごとにデコレーションビューを配置する実装を試してみたのですが、
以下のような挙動になってしまいます。

- デコレーションビューは再利用されず、毎回インスタンス化される (prepareForReuseもコールされない)
- スクロールアウトしたビューについてもdeallocされない
- スクロールを戻して再表示された時にも新しいインスタンスが生成される

期待としては、
- UICollectionViewCellやSupplementaryViewのようにキューベースの再利用をしてほしい
- さもなくば、同じIndexPathについてだけでも…

この件について、なにか回避策、あるいはなんらかの情報(既知バグであるとか)を
もっている人はいますでしょうか?

```objc
@interface ReproductionLayout : UICollectionViewLayout
@end

@interface DecoView : UICollectionReusableView
@end

@implementation ReproductionLayout
- (id)initWithCoder:(NSCoder *)aDecoder { // Storyboardを使う場合
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerClass:[DecoView class] forDecorationViewOfKind:@"DecoView"];
    }
    return self;
}
- (CGSize)collectionViewContentSize {
    CGSize size = self.collectionView.bounds.size;
    size.height = [self.collectionView numberOfItemsInSection:0] * 100;
    return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    int count = [self.collectionView numberOfItemsInSection:0];
    for (int i=MAX(0, (int)(rect.origin.y / 100)); i<count && i*100<rect.origin.y + rect.size.height; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        [array addObject:[self layoutAttributesForDecorationViewOfKind:@"DecoView" atIndexPath:indexPath]];
    }
    return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat y = indexPath.item * 100;
    CGFloat x = (y - self.collectionView.contentOffset.y) / 2;
    attr.frame = CGRectMake(x, y, 80, 80);
    return attr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    attr.frame = CGRectMake(0, indexPath.item * 100 + 80, self.collectionView.bounds.size.width, 20);
    return attr;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
@end

@implementation DecoView
- (void)drawRect:(CGRect)rect {
    [[UIColor blueColor] set];
    [[UIBezierPath bezierPathWithRect:rect] fill];
    [[UIColor whiteColor] set];
    [[self description] drawInRect:rect withFont:[UIFont systemFontOfSize:12.0]];
}
- (void)prepareForReuse {
    NSLog(@"prepareForReuse -- NEVER CALL");
}
- (void)dealloc {
    NSLog(@"dealloc -- NEVER CALL until superview's dealloc");
}
@end
```