it-swarm.com.de

UICollectionView - Größenänderung der Zellen auf dem Gerät - Swift

Ich habe eine UICollectionView erstellt, damit ich Ansichten in ordentlichen Spalten anordnen kann. Ich möchte, dass es eine einzige Spalte für Geräte mit einer Breite von 500 Pixel gibt.

Um dies zu erreichen, habe ich diese Funktion erstellt:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    let size = collectionView.frame.width
    if (size > 500) {
        return CGSize(width: (size/2) - 8, height: (size/2) - 8)
    }
    return CGSize(width: size, height: size)
}

Dies funktioniert beim ersten Laden wie erwartet. Wenn ich das Gerät drehe, wird die Berechnung jedoch nicht immer wiederholt, und die Ansichten werden nicht immer wie erwartet neu gezeichnet. Hier ist mein Code für das Drehen des Geräts:

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    collectionView.collectionViewLayout.invalidateLayout()
    self.view.setNeedsDisplay()
}

Ich gehe davon aus, dass ich vergessen habe, etwas neu zu zeichnen, aber ich bin mir nicht sicher, was. Alle Ideen werden sehr dankbar aufgenommen!

41
Ben

Sie könnten viewWillLayoutSubviews verwenden. Diese Frage sollte hilfreich sein, wird aber bassisch aufgerufen, wenn die View-Controller-Ansichten ihre Unteransichten anordnen. 

Ihr Code wird also so aussehen:

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()

  guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
    return
  }

  if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
    //here you can do the logic for the cell size if phone is in landscape
  } else {
    //logic if not landscape 
  }

  flowLayout.invalidateLayout()
}
19
adolfosrs

Der einfachste Weg, dies zu erreichen, ist vielleicht, dasLayout während der viewWillTransitionToSize-Instanz zu ungültigen:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}
57
valvoline

Ich habe eher viewWillTransitionToSize mit der gleichen Sache benutzt und habe einfach invalidateLayout() dort angerufen. 

13

Ich habe folgenden Ansatz gewählt, der bei mir funktioniert hat. Das Problem bei mir war, dass ich das Layout in ungültig gemacht habe

viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

Aber zu diesem Zeitpunkt (wie dieser Methodenname andeutet) wurde das Gerät noch nicht gedreht. Diese Methode wird also vor viewWillLayoutSubviews aufgerufen und daher haben wir in dieser Methode keine rechten Grenzen und Frames (sicherer Bereich), da das Gerät danach rotiert.

Also habe ich Benachrichtigungen verwendet

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}

@objc func rotated(){
    guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}

und dann funktioniert in der Flow-Delegate-Methode der Sammlungsansicht alles wie erwartet.

extension ViewController: UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
        if #available(iOS 11.0, *) {
            return CGSize(width: view.safeAreaLayoutGuide.layoutFrame.width, height: 70)
        } else {
            return CGSize(width: view.frame.width, height: 70)
        }
    }
}
3
Nikunj Acharya

Die traitCollectionDidChange-Methode kann auch anstelle von viewWillLayoutSubviews verwendet werden:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    guard let previousTraitCollection = previousTraitCollection, traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
        traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass else {
            return
    }

    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular {
        // iPad portrait and landscape
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        // iPhone portrait
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass = .compact {
        // iPhone landscape
        // do something here...
    }
    collectionView?.collectionViewLayout.invalidateLayout()
    collectionView?.reloadData()
}
2
birdy

Verwenden Sie den Übergangskoordinator, um die Zellengröße der Sammlungsansicht zu ändern und die Änderung während der Drehung zu animieren.

(Swift 4+)

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

    // Have the collection view re-layout its cells.
    coordinator.animate(
        alongsideTransition: { _ in self.collectionView.collectionViewLayout.invalidateLayout() },
        completion: { _ in }
    )
}
1
Graham Perks

Dies ist für andere mit vielleicht gleichem Sonderfall. Situation: Ich habe ein UICollectionView als UITableViewCell in einen Table View Controller eingefügt. Die Zeilenhöhe wurde auf UITableViewAutomaticDimension gesetzt, um die Anzahl der Zellen in der Sammlungsansicht anzupassen. Bei der Gerätedrehung wurde das Layout nicht korrekt angezeigt, obwohl sich andere Zellen mit dynamischem Inhalt in derselben Tabellenansicht korrekt verhielten. Nach langer Recherche habe ich eine Lösung gefunden, die funktioniert:

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

- (void) reloadTableViewSilently {
    dispatch_async(dispatch_get_main_queue(), ^{
// optional: [UIView setAnimationsEnabled:false];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
// optional: [UIView setAnimationsEnabled:true];
    });
}
0
Herbert Bay

Ich hatte die Aufgabe, die Elementgröße in der UICollectionView-Unterklasse anzupassen. Die beste Methode dafür ist setFrame. Property collectionViewFlowLayout Ich habe von ViewController übergeben (in meinem Fall war es Outlet vom Standardflusslayout).

// .h
@property (nonatomic, weak) UICollectionViewFlowLayout *collectionViewFlowLayout;

// .m
- (void)setFrame:(CGRect)frame {
    if (!CGSizeEqualToSize(frame.size, self.frame.size)) {
        collectionViewFlowLayout.itemSize =
        CGSizeMake((UIDeviceOrientationIsLandscape(UIDevice.currentDevice.orientation) ? (frame.size.width - 10) / 2 : frame.size.width), collectionViewFlowLayout.itemSize.height);
    }
    [super setFrame:frame];
}
0
Igor

Sie können einen Maskeninhalt erstellen und in die collectionView verschieben. Nachdem die Quer-/Hochformatanimation abgeschlossen ist, müssen Sie sie so schnell wie möglich entfernen.

Hier ist ein Beispiel:

@property (strong, nonatomic) UIImageView *maskImage;

.........

- (UIImageView *) imageForCellAtIndex: (NSInteger) index {
    UICollectionView *collectionView = self.pagerView.test;
    FSPagerViewCell *cell = nil;
    NSArray *indexPaths = [collectionView indexPathsForVisibleItems];
    for (NSIndexPath *indexPath in indexPaths) {
        if (indexPath.item == index) {
            cell = (FSPagerViewCell *)[collectionView cellForItemAtIndexPath: indexPath];
            break;
        }
    }
    if (cell) {
        return cell.imageView;
    }
    return nil;
}


- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
         [self test_didRotateFromInterfaceOrientation: orientation];

         UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
         if (imageView) {
                 UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
                 CGSize itemSize = self.pagerView.itemSize;
                 UIImageView *newImage = [[UIImageView alloc] initWithImage: imageView.image];
                 [newImage setFrame: CGRectMake((_contentView.bounds.size.width - itemSize.width)/2.0f, 0, itemSize.width, itemSize.height)];
                 newImage.contentMode = imageView.contentMode;
                 newImage.clipsToBounds = imageView.clipsToBounds;
                 newImage.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
                 [self.pagerView addSubview: newImage];

                 self.maskImage = newImage;
         }

         [self.pagerView.test performBatchUpdates:^{
             [self.pagerView.test setCollectionViewLayout:self.pagerView.test.collectionViewLayout animated:YES];
         } completion:nil];
         // do whatever
     } completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         [self.maskImage removeFromSuperview];
     }];
}
0
KuTeo