17

Sono hanno un sacco di difficoltà cercando di creare un UICollectionView come nel lettore di Spotify che si comporta in questo modo:Come creare un UICollectionView centrato come nel lettore di Spotify

a busy cat

Il problema per me è di due volte .

1) Come centrare le celle in modo da poter vedere la cella centrale e quella di sinistra e destra.

  • Se creo celle quadrate e aggiungo spaziature tra ogni cella, le celle vengono visualizzate correttamente ma non centrate.

2) Con pagingEnabled = YES, la collectionview scorre correttamente da una pagina all'altra. Tuttavia, senza che le celle siano centrate, sposta semplicemente la vista insieme su una pagina che è la larghezza dello schermo. Quindi la domanda è come far muovere le pagine in modo da ottenere l'effetto sopra.

3) come si fa a animare la dimensione delle cellule che si muovono

  • Non voglio preoccupare di questo troppo. Se riesco a convincere quello per funzionare sarebbe bello, ma i problemi più difficili sono 1 e 2.

Il codice che ho attualmente è un semplice UICollectionView con normali cellule di impostazione delegato e UICollectionview personalizzati che sono quadrati. Forse ho bisogno di sottoclasse UICollectionViewFlowLayout? O forse ho bisogno di attivare pagingEnabled su NO e quindi utilizzare eventi di scorrimento personalizzati? Mi piacerebbe qualsiasi aiuto!

+1

Ecco un esempio trovato su Github: https://github.com/ikemai/ScaledVisibleCellsCollectionView forse potrebbe aiutare :) – Lapinou

+0

@Lapinou Ho dei problemi con questo nel mio progetto di Objective-C. Non riesco a far funzionare il ponte per il lavoro – evenodd

+0

Hmmm ... Normalmente, è abbastanza facile. Guarda questo post: http://stackoverflow.com/questions/24206732/cant-use-swift-classes-inside-objective-c fammi sapere se funziona per te;) – Lapinou

risposta

5

Come hai detto nel commento che si desidera che il codice Objective-C, c'è una famosa libreria chiamata iCarousel che può essere utile per completare il vostro requirement.Link: https://github.com/nicklockwood/iCarousel

Si può usare 'Rotary 'o 'lineare' o qualche altro stile con poche o nessuna modifica per implementare la visualizzazione personalizzata

per la sua attuazione si dispone di implementare solo alcuni metodi delegato di esso e che sta funzionando per es:

//specify the type you want to use in viewDidLoad 
_carousel.type = iCarouselTypeRotary; 

//Set the following delegate methods 
- (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel 
{ 
    //return the total number of items in the carousel 
    return [_items count]; 
} 

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view 
{ 
    UILabel *label = nil; 

    //create new view if no view is available for recycling 
    if (view == nil) 
    { 
     //don't do anything specific to the index within 
     //this `if (view == nil) {...}` statement because the view will be 
     //recycled and used with other index values later 
     view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)]; 
     ((UIImageView *)view).image = [UIImage imageNamed:@"page.png"]; 
     view.contentMode = UIViewContentModeCenter; 

     label = [[UILabel alloc] initWithFrame:view.bounds]; 
     label.backgroundColor = [UIColor clearColor]; 
     label.textAlignment = NSTextAlignmentCenter; 
     label.font = [label.font fontWithSize:50]; 
     label.tag = 1; 
     [view addSubview:label]; 
    } 
    else 
    { 
     //get a reference to the label in the recycled view 
     label = (UILabel *)[view viewWithTag:1]; 
    } 

    //set item label 
    label.text = [_items[index] stringValue]; 

    return view; 
} 

- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value 
{ 
    if (option == iCarouselOptionSpacing) 
    { 
     return value * 1.1; 
    } 
    return value; 
} 

Puoi chec k la demo di lavoro completo da 'Examples/Basic iOS Example' che è incluso con il link repository GitHub

Come è vecchio e popolare è possibile trovare alcuni tutorial correlati per esso e sarà anche molto più stabile rispetto alla implementazione del codice personalizzato

14

Bene, ho reso UICollectionview in movimento proprio così, ieri.

posso condividere il mio codice con voi :)

Ecco il mio storyboard

make sicuro deselezionare 'Paging Abilitato'

Ecco il mio codice.

@interface FavoriteViewController() <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> 
{ 
    NSMutableArray * mList; 

    CGSize cellSize; 
} 

@property (weak, nonatomic) IBOutlet UICollectionView *cv; 
@end 

@implementation FavoriteViewController 

- (void) viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 

    // to get a size. 
    [self.view setNeedsLayout]; 
    [self.view layoutIfNeeded]; 

    CGRect screenFrame = [[UIScreen mainScreen] bounds]; 
    CGFloat width = screenFrame.size.width*self.cv.frame.size.height/screenFrame.size.height; 
    cellSize = CGSizeMake(width, self.cv.frame.size.height); 
    // if cell's height is exactly same with collection view's height, you get an warning message. 
    cellSize.height -= 1; 

    [self.cv reloadData]; 

    // setAlpha is for hiding looking-weird at first load 
    [self.cv setAlpha:0]; 
} 

- (void) viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear:animated]; 

    [self scrollViewDidScroll:self.cv]; 
    [self.cv setAlpha:1]; 
} 

#pragma mark - scrollview delegate 
- (void) scrollViewDidScroll:(UIScrollView *)scrollView 
{ 
    if(mList.count > 0) 
    { 
     const CGFloat centerX = self.cv.center.x; 
     for(UICollectionViewCell * cell in [self.cv visibleCells]) 
     { 
      CGPoint pos = [cell convertPoint:CGPointZero toView:self.view]; 
      pos.x += cellSize.width/2.0f; 
      CGFloat distance = fabs(centerX - pos.x); 

// If you want to make side-cell's scale bigger or smaller, 
// change the value of '0.1f' 
      CGFloat scale = 1.0f - (distance/centerX)*0.1f; 
      [cell setTransform:CGAffineTransformMakeScale(scale, scale)]; 
     } 
    } 
} 

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset 
{ // for custom paging 
    CGFloat movingX = velocity.x * scrollView.frame.size.width; 
    CGFloat newOffsetX = scrollView.contentOffset.x + movingX; 

    if(newOffsetX < 0) 
    { 
     newOffsetX = 0; 
    } 
    else if(newOffsetX > cellSize.width * (mList.count-1)) 
    { 
     newOffsetX = cellSize.width * (mList.count-1); 
    } 
    else 
    { 
     NSUInteger newPage = newOffsetX/cellSize.width + ((int)newOffsetX%(int)cellSize.width > cellSize.width/2.0f ? 1 : 0); 
     newOffsetX = newPage*cellSize.width; 
    } 

    targetContentOffset->x = newOffsetX; 
} 

#pragma mark - collectionview delegate 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 
{ 
    return mList.count; 
} 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"list" forIndexPath:indexPath]; 

    NSDictionary * dic = mList[indexPath.row]; 

    UIImageView * iv = (UIImageView *)[cell.contentView viewWithTag:1]; 
    UIImage * img = [UIImage imageWithData:[dic objectForKey:kKeyImg]]; 
    [iv setImage:img]; 

    return cell; 
} 

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return cellSize; 
} 
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section 
{ 
    CGFloat gap = (self.cv.frame.size.width - cellSize.width)/2.0f; 
    return UIEdgeInsetsMake(0, gap, 0, gap); 
} 
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section 
{ 
    return 0; 
} 
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section 
{ 
    return 0; 
} 

Codice chiave di cellula make centrato è

  1. scrollViewWillEndDragging

  2. insetForSectionAtIndex

Codice chiave di animare la dimensione è

  1. scrollviewDidScroll

auguro che questo ti aiuta

P.S. Se si desidera cambiare alfa, proprio come l'immagine che hai caricato, aggiungere [setalpha cella] in scrollViewDidScroll

+0

grazie per il codice utile –

1

pagingEnabled non dovrebbe essere attivata come ha bisogno di ogni cella per essere la larghezza di visualizzare che non funziona per voi dal momento hai bisogno di vedere i bordi delle altre celle. Per i tuoi punti 1 e 2. Penso che troverai quello che ti serve here da una delle mie ultime risposte ad un'altra domanda.

L'animazione delle dimensioni delle celle può essere ottenuto mediante sottoclassi UIcollectionviewFlowLayout e prevalente layoutAttributesForItemAtIndexPath: Da che modificano gli attributi di layout forniti dal primo chiamando super e quindi modificare il layout attributi dimensione in base alla posizione in cui si riferisce al centro della finestra.

Speriamo che questo aiuti.

5

Volevo un comportamento simile un po 'indietro, e con l'aiuto di @Mike_M sono riuscito a capirlo. Sebbene ci siano molti, molti modi per farlo, questa particolare implementazione è creare un UICollectionViewLayout personalizzato.

codice qui sotto (GIST può essere trovato qui: https://gist.github.com/mmick66/9812223)

Ora è importante impostare la seguente: *yourCollectionView*.decelerationRate = UIScrollViewDecelerationRateFast, questo impedisce alle cellule di essere saltato da un colpo rapido.

Che dovrebbe coprire parte 1 e 2.Ora, per la terza parte, è possibile incorporarla nella raccolta personalizzata View invalidando e aggiornando costantemente, ma è un po 'una seccatura se me lo chiedi. Quindi un altro approccio sarebbe quello di impostare CGAffineTransformMakeScale(,) nello UIScrollViewDidScroll in cui si aggiorna dinamicamente la dimensione della cella in base alla sua distanza dal centro dello schermo.

È possibile ottenere i IndexPath delle celle visibili della collectionView utilizzando [*youCollectionView indexPathsForVisibleItems] e quindi ottenere le celle per questi indexPath. Per ogni cellula, calcolare la distanza del suo centro al centro di yourCollectionView

Il centro della CollectionView si possono trovare con questo metodo ingegnoso: CGPoint point = [self.view convertPoint:*yourCollectionView*.center toView:*yourCollectionView];

Ora impostare una regola, che se il centro della cellula è più lontano di x, la dimensione della cella è ad esempio la 'dimensione normale', chiamala 1. e più si avvicina al centro, più si avvicina al doppio della dimensione normale 2.

quindi tu possibile utilizzare la seguente idea if/else:

if (distance > x) { 
     cell.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 
} else if (distance <= x) { 

     float scale = MIN(distance/x) * 2.0f; 
     cell.transform = CGAffineTransformMakeScale(scale, scale); 
} 

Quello che succede è che le dimensioni della cella seguiranno esattamente il tuo tocco. Fatemi sapere se avete altre domande mentre sto scrivendo la maggior parte di questo fuori di testa.

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)offset 
          withScrollingVelocity:(CGPoint)velocity { 

CGRect cvBounds = self.collectionView.bounds; 
CGFloat halfWidth = cvBounds.size.width * 0.5f; 
CGFloat proposedContentOffsetCenterX = offset.x + halfWidth; 

NSArray* attributesArray = [self layoutAttributesForElementsInRect:cvBounds]; 

UICollectionViewLayoutAttributes* candidateAttributes; 
for (UICollectionViewLayoutAttributes* attributes in attributesArray) { 

    // == Skip comparison with non-cell items (headers and footers) == // 
    if (attributes.representedElementCategory != 
     UICollectionElementCategoryCell) { 
     continue; 
    } 

    // == First time in the loop == // 
    if(!candidateAttributes) { 
     candidateAttributes = attributes; 
     continue; 
    } 

    if (fabsf(attributes.center.x - proposedContentOffsetCenterX) < 
     fabsf(candidateAttributes.center.x - proposedContentOffsetCenterX)) { 
     candidateAttributes = attributes; 
    } 
} 

return CGPointMake(candidateAttributes.center.x - halfWidth, offset.y); 

}