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}…
… Sollte zum Inhaltsversatz im Querformat auch mit { bounds.size.width * 2, 0} führen (und umgekehrt).
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 .
quelle
Antworten:
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
quelle
floor
Funktion anstelle von verwendenround
?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]; }
quelle
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
quelle
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 } }
quelle
Ich denke, die richtige Lösung besteht darin, die Methode targetContentOffsetForProposedContentOffset: in einer Unterklasse zu überschreiben
UICollectionViewFlowLayout
Aus den Dokumenten:
quelle
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 } }
quelle
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.
prepareForAnimatedBoundsChange
sollte aufgerufen werden, wenn Sie das Gerät drehen,finalizeAnimatedBoundsChange
nachdem 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
quelle
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.
quelle
Ich benutze eine Variante von fz. Antwort (iOS 7 & 8):
Vor dem Drehen:
Nach der Rotation:
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]; }
quelle
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.
-(void)prepareLayout
.Legen Sie itemSize, scrollDirection und andere fest.
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
.Berechnen Sie die Seitenzahl oder den Offset des sichtbaren Inhalts.
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
.Versatz des visuellen Inhalts zurückgeben.
-(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
quelle
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.
quelle
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]; }]; }
quelle
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.
quelle
Was macht der Job für mich ist das:
Stellen Sie die Größe Ihrer my-Zellen anhand Ihrer my-
UICollectionViewDelegateFlowLayout
Methode einfunc collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize { return collectionView.bounds.size }
Danach implementiere ich
willRotateToInterfaceOrientationToInterfaceOrientation:duration:
sooverride 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"
quelle
Möglicherweise möchten Sie die collectionView während der (falschen) Animation ausblenden und stattdessen eine Platzhalteransicht der Zelle anzeigen, die sich korrekt dreht.
Für eine einfache Fotogalerie habe ich einen Weg gefunden, der ziemlich gut aussieht. Siehe meine Antwort hier: Wie kann ich eine UICollectionView ähnlich der Foto-App drehen und die aktuelle Ansicht zentriert halten?
quelle
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.
quelle
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.
quelle
-(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.
quelle
Verwenden Sie
<CollectionViewDelegateFlowLayout>
unddidRotateFromInterfaceOrientation:
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); } }
quelle
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.
quelle
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); }
quelle
Ich habe dieses Problem mit folgenden Schritten gelöst:
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.
quelle
Ich hatte einige Probleme mit dem
animateAlongsideTransition
BlockierenanimateAlongsideTransition
(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 michself.topVisibleRowIndexPath
vor 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
willRotateToInterfaceOrientation
undwillRotateToInterfaceOrientation
immer noch erfolgreich in iOS später 8.0 aufgerufen werden, wenn die MethodeviewWillTransitionToSize
nicht 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 :)
quelle
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]; }]; }
quelle