2013-04-10 2 views
13

Ich möchte "Karten" in meiner App wie Safari Tabs oder App Store Suche implementieren.UICollectionView: Paging wie Safari Tabs oder App Store Suche

Ich werde den Benutzer eine Karte in einer Mitte des Bildschirms und einen Teil der vorherigen und nächsten Karten auf der linken und rechten Seite zeigen. (Siehe App-App-Suche oder Safari-Registerkarten zum Beispiel)

Ich entschied mich für UICollectionView, und ich muss die Seitengröße ändern (nicht finden, wie) oder implementieren eigene Layout-Unterklasse (weiß nicht wie)?

Irgendwelche Hilfe, bitte?

Antwort

26

Unten ist der einfachste Weg, den ich gefunden habe, um diesen Effekt zu bekommen. Es beinhaltet Ihre Sammlung und eine zusätzliche geheime Ansicht.

Ihre Sammlung Ansichten

  • Ihre Sammlung Ansicht einrichten einrichten und alle Methoden die Datenquelle.
  • Rahmen Sie die Sammlungsansicht ein; Es sollte die volle Breite überspannen, die Sie sehen möchten.
  • Stellen Sie die contentInset Sammlung Ansicht:

    _collectionView.contentInset = UIEdgeInsetsMake(0, (self.view.frame.size.width-pageSize)/2, 0, (self.view.frame.size.width-pageSize)/2); 
    

Diese richtig die Zellen Offset hilft.

Ihr Geheimnis Scroll

  • eine Scrollview erstellen einrichten, kann überall Sie mögen. Sie können es auf hidden setzen, wenn Sie möchten.
  • Legen Sie die Größe der Scrollview-Grenzen auf die gewünschte Größe Ihrer Seite fest.
  • Legen Sie sich als Delegierter der Scrollview fest.
  • Legen Sie contentSize auf die erwartete Inhaltsgröße Ihrer Sammlungsansicht fest.

Bewegen Sie Gestenerkenner

  • das Geheimnis Scrollgeste Erkenner der Sammlung Ansicht hinzufügen, und deaktivieren Sie die Gestenerkenner Sammlung Ansicht:

    [_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer]; 
    _collectionView.panGestureRecognizer.enabled = NO; 
    

Delegierter

Wenn sich die Bildlaufleiste bewegt, ermitteln Sie ihren Offset und setzen Sie ihn auf den Offset der Sammlungsansicht.

ich gebloggt das hier, so überprüfen Sie diesen Link für Updates: http://khanlou.com/2013/04/paging-a-overflowing-collection-view/

+0

Dies ist jedoch nicht passieren wählen und tippen Ereignisse korrekt bis zur darunter liegenden Sammlung Ansicht ab iOS7. Irgendwelche Ideen? – eanticev

+1

Auf iOS7 (zumindest war das vorher wahrscheinlich) benötigen Sie den Scrollview nicht. Ermitteln Sie einfach die linke und rechte Gestenbewegung und verwenden Sie diese, um den Versatz Ihrer Sammlungsansicht 1 Seite vorwärts/rückwärts einzustellen. – buildsucceeded

+3

Ich kann nicht für das Leben von mir es auf iOS7 arbeiten. 'scrollViewDidScroll' einer" geheimen "Scroll-Ansicht wird niemals zugestellt. –

-6

Die bisherige ist ziemlich kompliziert, UICollectionView eine Unterklasse von UIScrollView ist, so dass nur dies tun:

[self.collectionView setPagingEnabled:YES]; 

Sie sind alle bereit zu gehen.

Siehe hierzu detailed tutorial.

+7

Dies betrifft nicht das Problem, dass während des Seitenwechsels Inhalte auf der linken und rechten Seite sichtbar sind, wie die Safari-Tabs oder die App-Store-Suche. –

1

Ein wenig Bearbeiten auf Soroush Antwort, die den Trick für mich getan hat. Der einzige bearbeiten Ich habe stattdessen die Geste der Deaktivierung:

[_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer]; 
_collectionView.panGestureRecognizer.enabled = NO; 

ich deaktiviert Scrollen auf der Kollektion:

_collectionView.scrollEnabled = NO; 

Als die Geste Deaktivieren der geheimen Scrollgeste als auch deaktiviert.

6

Sie können UICollectionViewFlowLayout Unterklasse und überschreiben wie so:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)contentOffset 
           withScrollingVelocity:(CGPoint)velocity 
{ 
    NSArray* layoutAttributesArray = 
    [self layoutAttributesForElementsInRect:self.collectionView.bounds]; 

    if(layoutAttributesArray.count == 0) 
     return contentOffset; 


    UICollectionViewLayoutAttributes* candidate = 
    layoutAttributesArray.firstObject; 

    for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray) 
    { 
     if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) 
      continue; 


     if((velocity.x > 0.0 && layoutAttributes.center.x > candidate.center.x) || 
      (velocity.x <= 0.0 && layoutAttributes.center.x < candidate.center.x)) 
      candidate = layoutAttributes; 


    } 

    return CGPointMake(candidate.center.x - self.collectionView.bounds.size.width * 0.5f, contentOffset.y); 
} 

Dies wird die nächste oder vorherige Zelle von der Geschwindigkeit abhängig bekommen ... es wird jedoch nicht auf der aktuellen Zelle schnappen.

+0

Einen anderen 'CGPoint' für den Offset über diese Delegate-Methode zurückzugeben funktioniert ganz gut. Der Übergang zum Anhalten an der Zelle fühlt sich ziemlich glatt an. – Andrei

+0

Es scheint auch der Trick zu sein, die Bildlaufansicht beim Übergang * zu * fangen * zu bringen, um die 'Verzögerungsrate' der Sammlungsansicht auf 'UIScrollViewDecelerationRateFast' zu setzen. Für ein schnelleres Einrasten scheint es auch so zu sein, als ob es funktioniert, wenn Sie die 'decelerationRate' auf etwas kleiner als 0,990000 setzen (was das aktuelle 'UIScrollViewDecelerationRateFast' zu sein scheint). – Andrei

0

Ich werde eine andere Lösung hinzufügen. Das Einrasten ist nicht perfekt (nicht so gut wie wenn Paging aktiviert ist, funktioniert aber gut genug).

Ich habe versucht, Soroush-Lösung zu implementieren, aber es funktioniert nicht für mich.

Da die UICollectionView eine Unterklasse von UIScrollView ist es zu einem wichtigen UIScrollViewDelegate Verfahren reagiert das ist:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
        withVelocity:(CGPoint)velocity 
       targetContentOffset:(inout CGPoint *)targetContentOffset 

Die targetContentOffset (ein inout Zeiger) können Sie den Haltepunkt für eine Sammlung Ansicht neu definieren (die x in diesem Fall, wenn Sie horizontal wischen).

Eine kurze Notiz über ein paar der folgenden Variablen gefunden:

  • self.cellWidth - das ist Ihre Sammlung Breite Sichtzelle (man kann es auch dort hart codieren, wenn Sie wollen)

  • self.minimumLineSpacing - das ist der minimale Zeilenabstand, den Sie zwischen den Zellen setzen

  • self.scrollingObjects ist das Array von Objekten in der Sammlung enthalten (ich brauche dies vor allem für die zählen, zu wissen, wann Scrollen zu stoppen)

So ist die Idee, diese Methode in der Sammlung Ansicht der Delegierten zu implementieren, etwa so:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
        withVelocity:(CGPoint)velocity 
       targetContentOffset:(inout CGPoint *)targetContentOffset 
{  
    if (self.currentIndex == 0 && velocity.x < 0) { 
     // we have reached the first user and we're trying to go back 
     return; 
    } 

    if (self.currentIndex == (self.scrollingObjects.count - 1) && velocity.x > 0) { 
     // we have reached the last user and we're trying to go forward 
     return; 
    } 

    if (velocity.x < 0) { 
     // swiping from left to right (going left; going back) 
     self.currentIndex--; 
    } else { 
     // swiping from right to left (going right; going forward) 
     self.currentIndex++; 
    } 

    float xPositionToStop = 0; 

    if (self.currentIndex == 0) { 
     // first row 
     xPositionToStop = 0; 
    } else { 
     // one of the next ones 
     xPositionToStop = self.currentIndex * self.cellWidth + (self.currentIndex + 1) * self.minimumLineSpacing - ((scrollView.bounds.size.width - 2*self.minimumLineSpacing - self.cellWidth)/2); 
    } 

    targetContentOffset->x = xPositionToStop; 

    NSLog(@"Point of stopping: %@", NSStringFromCGPoint(*targetContentOffset)); 
} 

Wir freuen uns auf jedes Feedback Sie haben die macht das Einrasten besser.Ich halte auch für eine bessere Lösung auf der Suche ...

+0

Dies scheint zu funktionieren ... repliziert jedoch nicht "Paging aktiviert" präzise und fühlt sich ein bisschen peinlich. –

2

@Mike M's answer in Swift ...

class CenteringFlowLayout: UICollectionViewFlowLayout { 

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 

     guard let collectionView = collectionView, 
      let layoutAttributesArray = layoutAttributesForElementsInRect(collectionView.bounds), 
      var candidate = layoutAttributesArray.first else { return proposedContentOffset } 

     layoutAttributesArray.filter({$0.representedElementCategory == .Cell }).forEach { layoutAttributes in 

      if (velocity.x > 0 && layoutAttributes.center.x > candidate.center.x) || 
       (velocity.x <= 0 && layoutAttributes.center.x < candidate.center.x) { 
       candidate = layoutAttributes 
      } 
     } 

     return CGPoint(x: candidate.center.x - collectionView.bounds.width/2, y: proposedContentOffset.y) 
    } 

}