Behalten Sie das contentOffset in einer UICollectionView bei, während Sie die Schnittstellenausrichtung drehen

74

Ich versuche, Änderungen der Schnittstellenausrichtung in einem UICollectionViewController zu verarbeiten. Ich versuche zu erreichen, dass ich nach einer Schnittstellenrotation das gleiche contentOffset haben möchte . Dies bedeutet, dass es entsprechend dem Verhältnis der Grenzwertänderung geändert werden sollte.

Beginnend im Hochformat mit einem Inhaltsoffset von { bounds.size.width * 2, 0}…

UICollectionView in portait

Sollte zum Inhaltsversatz im Querformat auch mit { bounds.size.width * 2, 0} führen (und umgekehrt).

UICollectionView im Querformat

Das Berechnen des neuen Versatzes ist nicht das Problem, aber Sie wissen nicht, wo (oder wann) Sie ihn einstellen müssen, um eine reibungslose Animation zu erhalten. Was ich damit mache, ist das Ungültigmachen des Layouts in willRotateToInterfaceOrientation:duration:und das Zurücksetzen des Inhaltsversatzes in didRotateFromInterfaceOrientation::

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration;
{
    self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
                                                    self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
    [self.collectionView.collectionViewLayout invalidateLayout];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
    CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
                                           self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
    [self.collectionView newContentOffset animated:YES];
}

Dies ändert den Inhaltsversatz nach der Drehung.

Wie kann ich es während der Rotation einstellen? Ich habe versucht, den neuen Inhaltsoffset einzustellen, willAnimateRotationToInterfaceOrientation:duration:aber dies führt zu einem sehr seltsamen Verhalten.

Ein Beispiel finden Sie in meinem Projekt auf GitHub .

Tobias Kräntzer
quelle
7
Haben Sie eine Lösung dafür gefunden?
Ghar

Antworten:

33

Sie können dies entweder im View Controller tun:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    guard let collectionView = collectionView else { return }
    let offset = collectionView.contentOffset
    let width = collectionView.bounds.size.width

    let index = round(offset.x / width)
    let newOffset = CGPoint(x: index * size.width, y: offset.y)

    coordinator.animate(alongsideTransition: { (context) in
        collectionView.reloadData()
        collectionView.setContentOffset(newOffset, animated: false)
    }, completion: nil)
}

Oder im Layout selbst: https://stackoverflow.com/a/54868999/308315

Gurjinder Singh
quelle
Genaue und präzise Antwort. Danke vielmals!
Kenan Begić
Nach einigen Recherchen ist dies die eleganteste Lösung!
iDoc
Es klappt. Sie müssen nur sicherstellen, dass viewWillTransition aufgerufen wird. Ein anderer Punkt ist, wenn die nächste Seite keine "ganze" Seite ist, müssen Sie nur die Runde der Indexvariablen entfernen.
Nathan Barreto
Sollten Sie die floorFunktion anstelle von verwenden round?
Mario
@Mario Ich benutze nur Runde
Gurjinder Singh
18

Lösung 1, "einfach einrasten"

Wenn Sie nur sicherstellen möchten, dass das contentOffset an der richtigen Position endet, können Sie eine Unterklasse von UICollectionViewLayout erstellen und die Methode targetContentOffsetForProposedContentOffset: implementieren . Zum Beispiel könnten Sie so etwas tun, um die Seite zu berechnen:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
    return CGPointMake(page * [self.collectionView frame].size.width, 0);
}

Das Problem ist jedoch, dass die Animation für diesen Übergang äußerst seltsam ist. Was ich in meinem Fall mache (der fast der gleiche ist wie in Ihrem), ist:

Lösung 2, "reibungslose Animation"

1) Zuerst lege ich die Zellengröße fest, die von collectionView verwaltet werden kann : layout: sizeForItemAtIndexPath: delegate-Methode wie folgt:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout  *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.view bounds].size;
}

Beachten Sie, dass sich [self.view bounds] je nach Gerätedrehung ändert.

2) Wenn sich das Gerät drehen soll, füge ich über der Sammlungsansicht eine Bildansicht mit allen Größenänderungsmasken hinzu. Diese Ansicht verbirgt tatsächlich die Verrücktheit der collectionView (weil sie darüber liegt) und da die Methode willRotatoToInterfaceOrientation: innerhalb eines Animationsblocks aufgerufen wird, wird sie entsprechend gedreht. Ich behalte auch das nächste contentOffset gemäß dem angezeigten indexPath bei, damit ich das contentOffset reparieren kann, sobald die Drehung abgeschlossen ist:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration
{
    // Gets the first (and only) visible cell.
    NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
    KSPhotoViewCell *cell = (id)[self.collectionView cellForItemAtIndexPath:indexPath];

    // Creates a temporary imageView that will occupy the full screen and rotate.
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[[cell imageView] image]];
    [imageView setFrame:[self.view bounds]];
    [imageView setTag:kTemporaryImageTag];
    [imageView setBackgroundColor:[UIColor blackColor]];
    [imageView setContentMode:[[cell imageView] contentMode]];
    [imageView setAutoresizingMask:0xff];
    [self.view insertSubview:imageView aboveSubview:self.collectionView];

    // Invalidate layout and calculate (next) contentOffset.
    contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds].size.height, 0);
    [[self.collectionView collectionViewLayout] invalidateLayout];
}

Beachten Sie, dass meine Unterklasse von UICollectionViewCell eine öffentliche imageView-Eigenschaft hat.

3) Schließlich besteht der letzte Schritt darin, den Inhaltsversatz auf einer gültigen Seite zu "fangen" und die temporäre Bildansicht zu entfernen.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.collectionView setContentOffset:contentOffsetAfterRotation];
    [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}
fz.
quelle
1
Ihre Antwort ist brillant! Aber Sie haben Anrufe bei super in willRotateToInterfaceOrientation und didRotateFromInterfaceOrientation verpasst.
Valeriy Van
Dies hat bei mir nicht funktioniert, ich habe es stattdessen geändert, um nach der Drehung keinen Versatz zu berechnen. Stattdessen speichere ich den NSIndexPath, der beim Abrufen von Zelleninformationen abgerufen wurde, und anstatt den Versatz festzulegen, scrolle ich ohne Animation zu diesem Versatz. Wenn jemand Interesse hat, kann ich eine tatsächliche Antwort mit Code zusammenstellen (schnell).
Mwright
15

Die Antwort "Nur einrasten" oben hat bei mir nicht funktioniert, da sie häufig nicht bei dem Element endete, das vor dem Drehen angezeigt wurde. Daher habe ich ein Flusslayout abgeleitet, das ein Fokuselement (falls festgelegt) zur Berechnung des Inhaltsversatzes verwendet. Ich setze das Element in willAnimateRotationToInterfaceOrientation und lösche es in didRotateFromInterfaceOrientation. Die Inset-Anpassung scheint unter IOS7 erforderlich zu sein, da die Sammlungsansicht unter der oberen Leiste angeordnet werden kann.

@interface HintedFlowLayout : UICollectionViewFlowLayout
@property (strong)NSIndexPath* pathForFocusItem;
@end

@implementation HintedFlowLayout

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    if (self.pathForFocusItem) {
        UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:self.pathForFocusItem];
        return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left, layoutAttrs.frame.origin.y-self.collectionView.contentInset.top);
    }else{
        return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
    }
}
@end
Troppoli
quelle
bei weitem die einfachste Lösung
FlowUI. SimpleUITesting.com
10

Für Benutzer von iOS 8+ sind willRotateToInterfaceOrientation und didRotateFromInterfaceOrientation veraltet.

Sie sollten jetzt Folgendes verwenden:

/* 
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized). 
If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator 
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Update scroll position during rotation animation
        self.collectionView.contentOffset = (CGPoint){contentOffsetX, contentOffsetY};
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Whatever you want to do when the rotation animation is done
    }];
}

Swift 3:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { (context:UIViewControllerTransitionCoordinatorContext) in
        // Update scroll position during rotation animation
    }) { (context:UIViewControllerTransitionCoordinatorContext) in
        // Whatever you want to do when the rotation animation is done
    }
}
Neilc
quelle
3
Wie wäre es mit Vollbildzellen? Wenn ich zur zweiten oder dritten Zelle
schiebe
8

Ich denke, die richtige Lösung besteht darin, die Methode targetContentOffsetForProposedContentOffset: in einer Unterklasse zu überschreibenUICollectionViewFlowLayout

Aus den Dokumenten:

Während Layoutaktualisierungen oder beim Übergang zwischen Layouts ruft die Sammlungsansicht diese Methode auf, um Ihnen die Möglichkeit zu geben, den vorgeschlagenen Inhaltsversatz zu ändern, der am Ende der Animation verwendet werden soll. Sie können diese Methode überschreiben, wenn die Animationen oder der Übergang dazu führen können, dass Elemente auf eine Weise positioniert werden, die für Ihr Design nicht optimal ist.

2cupsOfTech
quelle
7

Swift 4.2-Unterklasse:

class RotatableCollectionViewFlowLayout: UICollectionViewFlowLayout {

    private var focusedIndexPath: IndexPath?

    override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
        super.prepare(forAnimatedBoundsChange: oldBounds)
        focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
    }

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
        guard let indexPath = focusedIndexPath
            , let attributes = layoutAttributesForItem(at: indexPath)
            , let collectionView = collectionView else {
                return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
        }
        return CGPoint(x: attributes.frame.origin.x - collectionView.contentInset.left,
                       y: attributes.frame.origin.y - collectionView.contentInset.top)
    }

    override func finalizeAnimatedBoundsChange() {
        super.finalizeAnimatedBoundsChange()
        focusedIndexPath = nil
    }
}
Bogdan Novikov
quelle
1
Das ist mehr, wonach ich gesucht habe. Ich wollte, dass Rotationscode in meiner benutzerdefinierten UICollectionView-Unterklasse enthalten ist, damit der Rotationscode nicht in der gesamten App dupliziert werden muss. Dies führt jedoch zu einem Bildlauf, was bedeutet, dass die Wiederverwendung von Zellen einsetzt. Dies kann unbeabsichtigte Nebenwirkungen haben oder auch nicht.
Daniel Williams
5

Um die Troppoli- Lösung zu optimieren, können Sie den Offset in Ihrer benutzerdefinierten Klasse festlegen, ohne sich Gedanken darüber machen zu müssen, den Code in Ihrem View Controller zu implementieren. prepareForAnimatedBoundsChangesollte aufgerufen werden, wenn Sie das Gerät drehen, finalizeAnimatedBoundsChangenachdem es fertig gedreht wurde.

@interface OrientationFlowLayout ()

@property (strong)NSIndexPath* pathForFocusItem;

@end

@implementation OrientationFlowLayout

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset {
    if (self.pathForFocusItem) {
        UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:
                                                         self.pathForFocusItem];
        return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left,
                           layoutAttrs.frame.origin.y - self.collectionView.contentInset.top);
    }
    else {
        return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
    }
}

- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds {
    [super prepareForAnimatedBoundsChange:oldBounds];
    self.pathForFocusItem = [[self.collectionView indexPathsForVisibleItems] firstObject];
}

- (void)finalizeAnimatedBoundsChange {
    [super finalizeAnimatedBoundsChange];
    self.pathForFocusItem = nil;
}

@end
Joe
quelle
ist der Aufruf zu super aber nötig?
FlowUI. SimpleUITesting.com
Ich habe dies in Xamarin implementiert, funktioniert hervorragend in iOS 11
Patrick Roberts
3

Dieses Problem hat mich auch ein bisschen gestört. Die Antwort mit der höchsten Bewertung schien mir ein bisschen zu hackig zu sein, also habe ich sie nur ein wenig heruntergefahren und das Alpha der Sammlungsansicht vor bzw. nach der Rotation geändert. Ich animiere auch nicht das Update des Inhaltsversatzes.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.collectionView.alpha = 0;
[self.collectionView.collectionViewLayout invalidateLayout];

self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
                                                self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
                                       self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);

[self.collectionView setContentOffset:newContentOffset animated:NO];
self.collectionView.alpha = 1;
}

Ziemlich glatt und weniger hackig.

Mafioso
quelle
3

Ich benutze eine Variante von fz. Antwort (iOS 7 & 8):

Vor dem Drehen:

  1. Speichern Sie den aktuell sichtbaren Indexpfad
  2. Erstellen Sie einen Snapshot der collectionView
  3. Fügen Sie eine UIImageView damit über die Sammlungsansicht

Nach der Rotation:

  1. Scrollen Sie zum gespeicherten Index
  2. Entfernen Sie die Bildansicht.

    @property (nonatomic) NSIndexPath *indexPath;
    
    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                    duration:(NSTimeInterval)duration {
        self.indexPathAfterRotation = [[self.collectionView indexPathsForVisibleItems] firstObject];
    
        // Creates a temporary imageView that will occupy the full screen and rotate.
        UIGraphicsBeginImageContextWithOptions(self.collectionView.bounds.size, YES, 0);
        [self.collectionView drawViewHierarchyInRect:self.collectionView.bounds afterScreenUpdates:YES];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        [imageView setFrame:[self.collectionView bounds]];
        [imageView setTag:kTemporaryImageTag];
        [imageView setBackgroundColor:[UIColor blackColor]];
        [imageView setContentMode:UIViewContentModeCenter];
        [imageView setAutoresizingMask:0xff];
        [self.view insertSubview:imageView aboveSubview:self.collectionView];
    
        [[self.collectionView collectionViewLayout] invalidateLayout];
    }
    
    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
        [self.collectionView scrollToItemAtIndexPath:self.indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    
        [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
    }
    
Dulgan
quelle
1
Am Ende
2

Nach dem Drehen der Schnittstellenausrichtung wird die UICollectionViewCell normalerweise an eine andere Position verschoben, da contentSize und contentOffset nicht aktualisiert werden.

Die sichtbare UICollectionViewCell befindet sich also immer nicht an der erwarteten Position.

Die sichtbare UICollectionView, die wir erwartet haben, folgt wie folgt

Orientierung, die wir erwartet hatten

UICollectionView muss die Funktion [collectionView sizeForItemAtIndexPath] von 『UICollectionViewDelegateFlowLayout deleg delegieren.

Und Sie sollten die Artikelgröße in dieser Funktion berechnen.

Das benutzerdefinierte UICollectionViewFlowLayout muss die Funktionen wie folgt überschreiben.

  1. -(void)prepareLayout

    .Legen Sie itemSize, scrollDirection und andere fest.

  2. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

    .Berechnen Sie die Seitenzahl oder den Offset des sichtbaren Inhalts.

  3. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset

    .Versatz des visuellen Inhalts zurückgeben.

  4. -(CGSize)collectionViewContentSize

    .Gibt die Gesamtinhaltsgröße von collectionView zurück.

Ihr viewController muss 『willRotateToInterfaceOrientation』 überschreiben. In dieser Funktion sollten Sie die Funktion [XXXCollectionVew.collectionViewLayout invalidateLayout] aufrufen.

Aber "willRotateToInterfaceOrientation" ist in iOS 9 veraltet, oder Sie können die Funktion [XXXCollectionVew.collectionViewLayout invalidateLayout] auf andere Weise aufrufen.

Es gibt ein Beispiel wie folgt: https://github.com/bcbod2002/CollectionViewRotationTest

Goston Wu
quelle
1
Bitte beschreiben Sie, was Sie getan haben oder erzählen Sie etwas über Ihr Projekt!
Alex Cio
2

in Swift 3.

Sie sollten verfolgen, welches Zellenelement (Seite) angezeigt wird, bevor Sie es mit indexPath.item, der x-Koordinate oder etwas anderem drehen. Dann in Ihrer UICollectionView:

override func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {

    let page:CGFloat = pageNumber // your tracked page number eg. 1.0
    return CGPoint(x: page * collectionView.frame.size.width, y: -(topInset))
    //the 'y' value would be '0' if you don't have any top EdgeInset
}

In meinem Fall mache ich das Layout in viewDidLayoutSubviews () ungültig, sodass die collectionView.frame.size.width die Breite der Ansicht der collectionVC ist, die gedreht wurde.

Kyle KIM
quelle
1

Diese Arbeit wie ein Zauber:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return self.view.bounds.size;
}

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    int currentPage = collectionMedia.contentOffset.x / collectionMedia.bounds.size.width;
    float width = collectionMedia.bounds.size.height;

    [UIView animateWithDuration:duration animations:^{
        [self.collectionMedia setContentOffset:CGPointMake(width * currentPage, 0.0) animated:NO];
        [[self.collectionMedia collectionViewLayout] invalidateLayout];
}];
}
user3676554
quelle
1

Wenn festgestellt wird, dass die Verwendung von targetContentOffsetForProposedContentOffset nicht in allen Szenarien funktioniert und das Problem bei der Verwendung von didRotateFromInterfaceOrientation darin besteht, dass visuelle Artefakte angezeigt werden. Mein perfekt funktionierender Code lautet wie folgt:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    _indexPathOfFirstCell = [self indexPathsForVisibleItems].firstObject;
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    if (_indexPathOfFirstCell) {
        [UIView performWithoutAnimation:^{
            [self scrollToItemAtIndexPath:self->_indexPathOfFirstCell atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
        }];
        _indexPathOfFirstCell = nil;
    }
}

Der Schlüssel besteht darin, die Methode willRotateToInterfaceOrientation zu verwenden, um den Teil in der Ansicht zu bestimmen, zu dem Sie einen Bildlauf durchführen möchten, und willAnimationRotationToInterfaceOrientation, um ihn neu zu berechnen, wenn die Größe der Ansicht geändert wurde (die Grenzen haben sich bereits geändert, als diese Methode vom Framework aufgerufen wird) und zu Scrollen Sie tatsächlich ohne Animation zur neuen Position. In meinem Code habe ich dazu den Indexpfad für die erste visuelle Zelle verwendet, aber ein Prozentsatz von contentOffset.y / contentSize.height würde die Aufgabe auch auf etwas andere Weise erledigen.

Werner Altewischer
quelle
Vergessen Sie nicht, dass die Rotations-API ab iOS 8 veraltet war]
Daniel Galasko
1

Was macht der Job für mich ist das:

  1. Stellen Sie die Größe Ihrer my-Zellen anhand Ihrer my- UICollectionViewDelegateFlowLayoutMethode ein

    func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
    {
        return collectionView.bounds.size
    }
    
  2. Danach implementiere ich willRotateToInterfaceOrientationToInterfaceOrientation:duration:so

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 
    {
        let currentPage = Int(collectionView.contentOffset.x / collectionView.bounds.size.width)
    
        var width = collectionView.bounds.size.height
        UIView.animateWithDuration(duration) {
            self.collectionView.setContentOffset(CGPointMake(width * CGFloat(currentPage), 0.0), animated: false)
            self.collectionView.collectionViewLayout.invalidateLayout()
        }
    }
    

Der obige Code ist in Swift, aber Sie verstehen es und es ist einfach zu "übersetzen"

Mihai Fratu
quelle
0

Ich verwende ein UICollectionViewFlowlayout-Objekt.

Stellen Sie den Ojbect-Zeilenabstand ein, wenn der Bildlauf horizontal erfolgt.

[flowLayout setMinimumLineSpacing:26.0f];

Stellen Sie den Zwischenabstand ein, wenn der Bildlauf vertikal erfolgt.

[flowLayout setMinimumInteritemSpacing:0.0f];

Beachten Sie, dass sich das Verhalten beim Drehen des Bildschirms anders verhält. In meinem Fall habe ich es horizontal scrollen, so dass der minimale Zeilenabstand 26.0f beträgt. Dann scheint es schrecklich, wenn es sich in Landschaftsrichtung dreht. Ich muss die Drehung überprüfen und den Mindestlinienabstand für diese Richtung 0.0f einstellen, um es richtig zu machen.

Das ist es! Einfach.

Itsraininggold
quelle
0

Ich hatte das Problem mit meinem Projekt, ich habe zwei verschiedene Layouts für die UICollectionView verwendet.

mCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"LandScapeCell" forIndexPath:indexPath];

theCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"PortraitCell" forIndexPath:indexPath];

Überprüfen Sie es dann für jede Ausrichtung und verwenden Sie Ihre Konfiguration für jede Ausrichtung.

Arun_
quelle
0
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize pnt = CGSizeMake(70, 70);
    return pnt; }

-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {

//    UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
    return UIEdgeInsetsMake(3, 0, 3, 0); }

Auf diese Weise können Sie den Inhaltsversatz und die Größe Ihrer Zelle anpassen.

Nikhil Lihla
quelle
0

Verwenden Sie <CollectionViewDelegateFlowLayout>und didRotateFromInterfaceOrientation:laden Sie in der Methode Daten der CollectionView neu.

Implementieren Sie die collectionView:layout:sizeForItemAtIndexPath:Methode von <CollectionViewDelegateFlowLayout>und überprüfen Sie in der Methode die Ausrichtung der Benutzeroberfläche und wenden Sie die benutzerdefinierte Größe jeder Zelle an.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

    if (UIInterfaceOrientationIsPortrait(orientation)) {

        return CGSizeMake(CGFloat width, CGFloat height);

    } else {

        return CGSizeMake(CGFloat width, CGFloat height);

    }

}
Soto_iGhost
quelle
0

Ich habe einen ähnlichen Fall, in dem ich dies benutze

- (void)setFrame:(CGRect)frame
{
    CGFloat currentWidth = [self frame].size.width;
    CGFloat offsetModifier = [[self collectionView] contentOffset].x / currentWidth;

    [super setFrame:frame];

    CGFloat newWidth = [self frame].size.width;

    [[self collectionView] setContentOffset:CGPointMake(offsetModifier * newWidth, 0.0f) animated:NO];
}

Dies ist eine Ansicht, die eine collectionView enthält. In der Übersicht mache ich das auch

- (void)setFrame:(CGRect)frame
{    
    UICollectionViewFlowLayout *collectionViewFlowLayout = (UICollectionViewFlowLayout *)[_collectionView collectionViewLayout];

    [collectionViewFlowLayout setItemSize:frame.size];

    [super setFrame:frame];
}

Hiermit stellen Sie die Zellengröße so ein, dass sie im Vollbildmodus angezeigt wird (um genau zu sein die Vollansicht;)). Wenn Sie dies hier nicht tun, werden möglicherweise viele Fehlermeldungen angezeigt, dass die Zellengröße größer als die Sammlungsansicht ist und das Verhalten hierfür nicht definiert ist und bla bla bla .....

Diese to-Methoden können natürlich in einer Unterklasse der Sammlungsansicht oder in der Ansicht mit der Sammlungsansicht zusammengeführt werden, aber für mein aktuelles Projekt war dies der logische Weg.

Saren Inden
quelle
0

Die "Just Snap" -Antwort ist der richtige Ansatz und erfordert keine zusätzliche Glättung mit IMO-Snapshot-Overlays. Es gibt jedoch ein Problem, das erklärt, warum manche Leute sehen, dass in einigen Fällen nicht zur richtigen Seite gescrollt wird. Bei der Berechnung der Seite möchten Sie die Höhe und nicht die Breite verwenden. Warum? Da sich die Ansichtsgeometrie bereits zum Zeitpunkt des Aufrufs von targetContentOffsetForProposedContentOffset gedreht hat, ist die Breite jetzt die Höhe. Auch Rundungen sind sinnvoller als Decken. Damit:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = round(proposedContentOffset.x / self.collectionView.bounds.size.height);
    return CGPointMake(page * self.collectionView.bounds.size.width, 0);
}
John Scalo
quelle
0

Ich habe dieses Problem mit folgenden Schritten gelöst:

  1. Berechnen Sie den aktuell gescrollten NSIndexPath
  2. Deaktivieren Sie das Scrollen und die Paginierung in UICollectionView
  3. Wenden Sie ein neues Flow-Layout auf UICollectionView an
  4. Aktivieren Sie das Scrollen und die Paginierung in UICollectionView
  5. Scrollen Sie mit UICollectionView zum aktuellen NSIndexPath

Hier ist die Codevorlage, die die oben genannten Schritte demonstriert:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                            duration:(NSTimeInterval)duration;
{
     //Calculating Current IndexPath
     CGRect visibleRect = (CGRect){.origin = self.yourCollectionView.contentOffset, .size = self.yourCollectionView.bounds.size};
     CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
     self.currentIndexPath = [self.yourCollectionView indexPathForItemAtPoint:visiblePoint];

     //Disable Scrolling and Pagination
     [self disableScrolling];

     //Applying New Flow Layout
     [self setupNewFlowLayout];

     //Enable Scrolling and Pagination
     [self enableScrolling];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
     //You can also call this at the End of `willRotate..` method.
     //Scrolling UICollectionView to current Index Path
     [self.yourCollectionView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}

- (void) disableScrolling
{
    self.yourCollectionView.scrollEnabled   = false;
    self.yourCollectionView.pagingEnabled   = false;
}

- (void) enableScrolling
{
    self.yourCollectionView.scrollEnabled   = true;
    self.yourCollectionView.pagingEnabled   = true;
}

- (void) setupNewFlowLayout
{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    flowLayout.minimumInteritemSpacing = 0;
    flowLayout.minimumLineSpacing = 0;
    [flowLayout setItemSize:CGSizeMake(EXPECTED_WIDTH, EXPECTED_HEIGHT)];

    [self.yourCollectionView setCollectionViewLayout:flowLayout animated:YES];
    [self.yourCollectionView.collectionViewLayout invalidateLayout];
}

Ich hoffe das hilft.

Salman Khakwani
quelle
0

Ich hatte einige Probleme mit dem animateAlongsideTransitionBlockieren animateAlongsideTransition(siehe den Code unten).

Beachten Sie, dass es während (aber nicht vor) der Animation aufgerufen wird. Meine Aufgabe bestand darin, die Bildlaufposition der Tabellenansicht durch Scrollen in die oberste sichtbare Zeile zu aktualisieren (ich hatte das Problem auf dem iPad, als sich die Zellen der Tabellenansicht beim Gerät nach oben verschoben haben Rotation, deshalb habe ich die Lösung für dieses Problem gefunden). Aber vielleicht wäre es auch für contentOffset nützlich.

Ich habe versucht, das Problem folgendermaßen zu lösen:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    __weak TVChannelsListTableViewController *weakSelf = self;

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        weakSelf.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

Aber es hat nicht funktioniert. Zum Beispiel war der Indexpfad der obersten Zelle (0, 20). Wenn jedoch der Block animateAlongsideTransition für die Gerätedrehung aufgerufen wurde und [[schwachSelf.tableView indexPathsForVisibleRows] firstObject] den Indexpfad (0, 27) zurückgab.

Ich dachte, das Problem sei das Abrufen von Indexpfaden zu weakSelf. Um das Problem zu lösen, habe ich mich self.topVisibleRowIndexPathvor dem [coordinator animateAlongsideTransition: completion]Methodenaufruf bewegt :

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    __weak TVChannelsListTableViewController *weakSelf = self;
    self.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];

    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

Und die andere interessante Sache, die ich entdeckt habe, ist, dass die veralteten Methoden willRotateToInterfaceOrientationund willRotateToInterfaceOrientationimmer noch erfolgreich in iOS später 8.0 aufgerufen werden, wenn die Methode viewWillTransitionToSizenicht neu definiert wird.

Die andere Möglichkeit, das Problem in meinem Fall zu lösen, bestand darin, eine veraltete Methode anstelle einer neuen zu verwenden. Ich denke, es wäre keine richtige Lösung, aber es ist möglich zu versuchen, wenn andere Wege nicht funktionieren :)

Julia
quelle
-2

Vielleicht möchten Sie diesen ungetesteten Code ausprobieren:

- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation
                                 duration: (NSTimeInterval)         duration
{
    [UIView animateWithDuration: duration
                      animation: ^(void)
     {
       CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x *
                                              self.collectionView.contentSize.height,
                                              self.scrollPositionBeforeRotation.y *
                                              self.collectionView.contentSize.width);
       [self.collectionView setContentOffset: newContentOffset
                                    animated: YES];
     }];
}
Kenn Cal
quelle