Sto lavorando al selettore di valori personalizzato ispirato a UIPickerView. Sembra che: Come correggere lo scorrimento su una cella visibile in UICollectionView con layout personalizzato?
Come si può vedere, una delle caratteristiche principali di questo selettore è la cellula centrale dovrebbe essere più ampia rispetto ad altri di fare suoi vicini visibile a fianco del telaio centrale. Quando si scorre il selettore con un gesto di panoramica, è necessario modificare dinamicamente il valore centrale e regolare le celle in base alla logica sopra riportata. E funziona perfettamente:
Il problema è nel gesto di tocco. Quando l'utente seleziona un elemento sul selettore eseguendo un tocco su di esso, il selettore tenta di scorrere fino a quell'elemento. Ma poiché l'offset è stato modificato dal layout personalizzato, UIScrollView scorre nel punto sbagliato. E sembra che:
Quando provo a scorrere verso la cella fuori dallo schermo tutte le cose funzionano bene - quella cella non è stata influenzata dal layout e le sue coordinate sono corrette. Il problema sorge solo per le celle visibili. Sono completamente fuori da ogni idea su come risolverlo. È possibile trovare l'intero progetto qui: Carousel Collection View Test Project
Si prega di trovare qualche codice significativo qui sotto.
// CarouselLayout.m
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
CGRect center = CGRectMake(CGRectGetMidX(visibleRect) - 1.0, 0.0, 2.0, CGRectGetHeight(visibleRect));
CGFloat coreWidth = CGRectGetWidth(self.centralFrame)/3.0;
for (UICollectionViewLayoutAttributes *attributes in array) {
if (CGRectIntersectsRect(attributes.frame, rect)){
CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;
CGFloat offset = 0.0;
CGRect coreFrame = CGRectMake(attributes.center.x - (coreWidth/2.0), 0.0, coreWidth, CGRectGetHeight(self.centralFrame));
if (CGRectIntersectsRect(center, coreFrame)) {
if (attributes.indexPath.item % 2 == 0) {
self.centralItemOffset = (CGRectGetWidth(self.centralFrame) - CGRectGetWidth(attributes.frame) - 4.0)/2.0;
if ([self.collectionView.delegate respondsToSelector:@selector(collectionView:layout:didChangeCentralItem:)]) {
[(id <CarouselLayoutDelegate>)self.collectionView.delegate collectionView:self.collectionView layout:self didChangeCentralItem:attributes.indexPath];
}
}
}
offset = (distance > 0) ? -self.centralItemOffset : self.centralItemOffset;
attributes.center = CGPointMake(attributes.center.x + offset, attributes.center.y);
}
}
return array;
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds)/2.0);
CGRect targetRectHorizontal = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray *array = [super layoutAttributesForElementsInRect:targetRectHorizontal];
for (UICollectionViewLayoutAttributes *attributes in array) {
if (attributes.indexPath.item % 2 == 1) {
continue;
}
CGFloat itemHorizontalCenter = attributes.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
// ViewController.m
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.item % 2 == 1) {
return;
}
NSString *nextValue = [self valueAtIndexPath:indexPath];
[self scrollToValue:nextValue animated:YES];
self.currentValue = nextValue;
}
- (void)scrollToValue:(NSString *)value animated:(BOOL)animated {
NSIndexPath *targetPath = nil;
NSIndexPath *currentPath = nil;
for (NSString *item in self.itemsArray) {
if (!targetPath && [value isEqualToString:item]) {
targetPath = [NSIndexPath indexPathForItem:([self.itemsArray indexOfObject:item] * 2) inSection:0];
}
if (!currentPath && [self.currentValue isEqualToString:item]) {
currentPath = [NSIndexPath indexPathForItem:([self.itemsArray indexOfObject:item] * 2) inSection:0];
}
if (targetPath && currentPath) {
break;
}
}
if (targetPath && currentPath) {
[self.itemsCollectionView scrollToItemAtIndexPath:targetPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated];
}
}
Se non è sufficiente, chiedere il codice aggiuntivo nei commenti.
Penso che il problema sia legato all'uso di una sottoclasse UICollectionViewFlowLayout rispetto al proprio UICollectionViewLayout. Stai regolando il centro degli attributi di layout ma non anche layoutAttributesForItemAtIndexPath. E scommetto la dimensione del contenuto che i rendimenti del layout del flusso non sanno del tuo cambiamento o se lo fa è troppo tardi. Prova a stampare il tuo layout. –
@HenryTKirk, sono abbastanza sicuro che il tuo approccio sia molto più corretto del mio. Ma quando ho provato a implementare il mio layout ho affrontato gli stessi problemi e anche un paio di altri. Dato che la risposta corretta è stata fornita di seguito, ho deciso di sospendere la mia ricerca a questo punto in quanto non ho più tempo per questo controllo. –
puoi inviarmi il tuo layout per lo stesso numero sul mio layout –