フタなしカンヅメ

徒然なるままに @happytar0

PSCollectionViewでハマる

Pinterest風の表示にしたいと思って色々ライブラリをあさって見たところ、PSCollectionViewという便利そうなライブラリを発見した。
iOS6以上であれば、UICollectionViewというそのものズバリのものがあるようだ。

heightForRowAtIndexメソッドの戻り値に個々の高さを指定してやることで、高さを個別に設定することも可能だった。
戻り値にCGFloatで値を返すのだけど、小数部の桁数が増えると誤差が生じて、上手くタップを認識しなくなることがあるみたい。

調べてみると、セルの大きさをNSStringFromCGRectでNSString化して管理しているようだ。

// Calculate index to rect mapping
self.colWidth = floorf((self.width - kMargin * (self.numCols + 1)) / self.numCols);
for (NSInteger i = 0; i < numViews; i++) {
    NSString *key = PSCollectionKeyForIndex(i);
    
    // Find the shortest column
    NSInteger col = 0;
    CGFloat minHeight = [[colOffsets objectAtIndex:col] floatValue];
    for (int i = 1; i < [colOffsets count]; i++) {
        CGFloat colHeight = [[colOffsets objectAtIndex:i] floatValue];
        
        if (colHeight < minHeight) {
            col = i;
            minHeight = colHeight;
        }
    }
    
    CGFloat left = kMargin + (col * kMargin) + (col * self.colWidth);
    CGFloat top = [[colOffsets objectAtIndex:col] floatValue];
    CGFloat colHeight = [self.collectionViewDataSource collectionView:self heightForRowAtIndex:i];
    
    CGRect viewRect = CGRectMake(left, top, self.colWidth, colHeight);
    
    // Add to index rect map
    [self.indexToRectMap setObject:NSStringFromCGRect(viewRect) forKey:key];
    
    // Update the last height offset for this column
    CGFloat heightOffset = colHeight > 0 ? top + colHeight + kMargin : top;
    
    [colOffsets replaceObjectAtIndex:col withObject:[NSNumber numberWithFloat:heightOffset]];
}

セルのタップは、UITapGestureRecognizerを使っているようで、shouldReceiveTouchメソッドで、どのセルがタップされたか判定して、didSelectメソッドが呼びされるようだ。
heightForRowAtIndexで返される高さの小数部が増えてくると、下記のrectStringと値が一致しなくなり、タップを認識しなくなる。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if (![gestureRecognizer isMemberOfClass:[PSCollectionViewTapGestureRecognizer class]]) return YES;
    
    NSString *rectString = NSStringFromCGRect(gestureRecognizer.view.frame);
    NSArray *matchingKeys = [self.indexToRectMap allKeysForObject:rectString];
    NSString *key = [matchingKeys lastObject];
    
    if ([touch.view isMemberOfClass:[[self.visibleViews objectForKey:key] class]]) {
        return YES;
    } else {
        return NO;
    }
}

解決方法は簡単で、heightForRowAtIndexで値を返す際に、小数部が増えないようにしたり、丸めてやればいい。

- (CGFloat)collectionView:(PSCollectionView *)collectionView heightForRowAtIndex:(NSInteger)index {
    return floorf(100.123456789f);
}

投稿しようとして気づいたのだけど、Objective-C(Cocoa)ネタ書いたの初めてだった。
iPhoneアプリを作っていると、ブログに書くようなネタがなかなか見つからない…
ちょっとググれば情報がたくさん見つかるし書くまでもないかなーとか思っちゃうせいだろうか。