8

Sto lavorando al selettore di valori personalizzato ispirato a UIPickerView. Sembra che: Technical prototype screen 1Come 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: Technical prototype screen 2

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: Technical prototype screen 3

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.

+0

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. –

+0

@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. –

+0

puoi inviarmi il tuo layout per lo stesso numero sul mio layout –

risposta

4

In scrollToValue metodo cambiamento:

if (targetPath && currentPath) { 
    [self.itemsCollectionView scrollToItemAtIndexPath:targetPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; 
} 

in:

if (targetPath && currentPath) { 
    if (targetPath.row < currentPath.row) { 
     [self.itemsCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.itemsArray.count inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; 
     [self.itemsCollectionView scrollToItemAtIndexPath:targetPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; 
    } else { 
     [self.itemsCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; 
     [self.itemsCollectionView scrollToItemAtIndexPath:targetPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; 

    } 
} 

e che funzionerà.

+0

È una soluzione incredibilmente semplice e funziona come un fascino! Molte grazie a te! –

+2

Puoi spiegare perché è una correzione, per favore? –

2

penso che è necessario impostare manualmente il contenuto di offset nel metodo scrollToValue:

[self.itemsCollectionView setContentOffset:offset animated:YES]; 

Per trovare questo offset è necessario catturare l'indice cella corrente (currentCell) e di trovare la larghezza della cella prima. Per esempio:

CGFloat xOffset = 0; 
for (int i = 0 ; i < currentCell ; i++) 
{ 
    if(i % 2 == 1){ 
     xOffset += dotCellWidth; // 20 in your case 
    } 
    else{ 
     // here you need to find the width of the cell at index i (25 or 34 in your case) 
     xOffset += numberCellWidth 
    } 
} 
// finish by adjusting the offset to the center of the current cell 
xOffset += currentCellWidth/2; // 12.5f or 17.f 

CGPoint offset = CGPointMake(xOffset, 0.0); 
[self.itemsCollectionView setContentOffset:offset animated:YES]; 
+0

Non funziona, questo offset non è corretto. Il problema è molto semplice: il layout personalizzato cambia le coordinate del centro dell'articolo, non la larghezza. E immagino che questo cambiamento influenzi il contentSize in qualche modo, ma non so come gestire queste modifiche. –