it-swarm.com.de

Swift: Aktualisieren des UICollectionView-Layouts nach einer Drehung des Geräts

Ich habe UICollectionView (flowlayout) verwendet, um ein einfaches Layout zu erstellen. Die Breite für jede Zelle wird mit self.view.frame.width auf die Breite des Bildschirms festgelegt.

wenn ich das Gerät jedoch drehe, werden die Zellen nicht aktualisiert.

 enter image description here

Ich habe eine Funktion gefunden, die beim Orientierungswechsel aufgerufen wird:

override func willRotateToInterfaceOrientation(toInterfaceOrientation: 
  UIInterfaceOrientation, duration: NSTimeInterval) {
    //code
}

ich kann jedoch keine Möglichkeit finden, das UICollectionView-Layout zu aktualisieren

Der Hauptcode ist hier:

class ViewController: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{

    @IBOutlet weak var myCollection: UICollectionView!

    var numOfItemsInSecOne: Int!
    override func viewDidLoad() {
        super.viewDidLoad()

        numOfItemsInSecOne = 8
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {

        //print("orientation Changed")
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return numOfItemsInSecOne
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellO", forIndexPath: indexPath)

        return cell
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{
    let itemSize = CGSize(width: self.view.frame.width, height: 100)
    return itemSize
    }}
37

Fügen Sie diese Funktion hinzu:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews() 
    myCollection.collectionViewLayout.invalidateLayout()
}

Wenn Sie die Ausrichtung ändern, wird diese Funktion aufgerufen.

44
Khuong

Die bessere Option ist, invalidateLayout() anstelle von reloadData() aufzurufen, da die Zellen nicht neu erstellt werden müssen. Die Leistung ist also etwas besser:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews() 
    myCollection.collectionViewLayout.invalidateLayout()
}
37
kelin

Sie können es auch auf diese Weise ungültig machen.

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

    [self.collectionView.collectionViewLayout invalidateLayout]; 
}
10
awuu

Die viewWillLayoutSubviews () hat bei mir nicht funktioniert. ViewDidLayoutSubviews () auch nicht. Beide ließen die App in eine Endlosschleife übergehen, die ich mit einem Druckbefehl überprüfte.

Eine der Möglichkeiten, die funktionieren, ist 

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Reload here
}
5
The Doctor

zum Aktualisieren der UICollectionViewLayout kann auch die traitCollectionDidChange-Methode verwendet werden:

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

    guard let previousTraitCollection = previousTraitCollections else {
        return
    }
    collectionView?.collectionViewLayout.invalidateLayout()
}
4
birdy

sie können Ihr UICollectionView-Layout mit aktualisieren

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    if isLandscape {
        return CGSizeMake(yourLandscapeWidth, yourLandscapeHeight)
    }
    else {
        return CGSizeMake(yourNonLandscapeWidth, yourNonLandscapeHeight)
    }
}
3

Ich hatte auch ein Problem, aber dann wurde es gelöst mit: 

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionViewFlowLayoutSetup(with: view.bounds.size.width)
        collectionView?.collectionViewLayout.invalidateLayout()
        collectionViewFlowLayoutSetup(with: size.width)
    }

    fileprivate func collectionViewFlowLayoutSetup(with Width: CGFloat){

        if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: Width, height: 300)
        }

    }
1
user2698544

Für mich geht das. Und das ist Code in Objective-C:

- (void)viewDidLayoutSubviews {
  [super viewDidLayoutSubviews];
  [collectionView.collectionViewLayout invalidateLayout];
}
1
congpc87

Wenn UICollectionLayout eine Begrenzungsänderung erkennt, werden Sie gefragt, ob das Layout ungültig umgeleitet werden muss. Sie können die Methode direkt umschreiben. UICollectionLayout kann die invalidateLayout-Methode zum richtigen Zeitpunkt aufrufen

class CollectionViewFlowLayout: UICollectionViewFlowLayout{

    /// The default implementation of this method returns false.
    /// Subclasses can override it and return an appropriate value
    /// based on whether changes in the bounds of the collection
    /// view require changes to the layout of cells and supplementary views.
    /// If the bounds of the collection view change and this method returns true,
    /// the collection view invalidates the layout by calling the invalidateLayout(with:) method.
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {

        return (self.collectionView?.bounds ?? CGRect.zero) == newBounds
    }
}
0
Z King

Der Aufruf von viewWillLayoutSubviews ist nicht optimal. Rufen Sie zuerst die invalidateLayout()-Methode auf.

Wenn der Fehler The behaviour of the UICollectionViewFlowLayout is not defined auftritt, müssen Sie überprüfen, ob alle Elemente in Ihrer Ansicht ihre Größe entsprechend einem neuen Layout geändert haben. (siehe die optionalen Schritte im Beispielcode)

Hier ist der Code, um loszulegen. Abhängig von der Art und Weise, wie Ihre Benutzeroberfläche erstellt wird, müssen Sie möglicherweise die richtige Ansicht zum Aufrufen der recalculate-Methode finden, die Sie jedoch zu den ersten Schritten führen sollten.

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

    super.viewWillTransition(to: size, with: coordinator)

    /// (Optional) Additional step 1. Depending on your layout, you may have to manually indicate that the content size of a visible cells has changed
    /// Use that step if you experience the `the behavior of the UICollectionViewFlowLayout is not defined` errors.

    collectionView.visibleCells.forEach { cell in
        guard let cell = cell as? CustomCell else {
            print("`viewWillTransition` failed. Wrong cell type")
            return
        }

        cell.recalculateFrame(newSize: size)

    }

    /// (Optional) Additional step 2. Recalculate layout if you've explicitly set the estimatedCellSize and you'll notice that layout changes aren't automatically visible after the #3

    (collectionView.collectionViewLayout as? CustomLayout)?.recalculateLayout(size: size)


    /// Step 3 (or 1 if none of the above is applicable)

    coordinator.animate(alongsideTransition: { context in
        self.collectionView.collectionViewLayout.invalidateLayout()
    }) { _ in
        // code to execute when the transition's finished.
    }

}

/// Example implementations of the `recalculateFrame` and `recalculateLayout` methods:

    /// Within the `CustomCell` class:
    func recalculateFrame(newSize: CGSize) {
        self.frame = CGRect(x: self.bounds.Origin.x,
                            y: self.bounds.Origin.y,
                            width: newSize.width - 14.0,
                            height: self.frame.size.height)
    }

    /// Within the `CustomLayout` class:
    func recalculateLayout(size: CGSize? = nil) {
        estimatedItemSize = CGSize(width: size.width - 14.0, height: 100)
    }

    /// IMPORTANT: Within the `CustomLayout` class.
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {

        guard let collectionView = collectionView else {
            return super.shouldInvalidateLayout(forBoundsChange: newBounds)
        }

        if collectionView.bounds.width != newBounds.width || collectionView.bounds.height != newBounds.height {
            return true
        } else {
            return false
        }
    }
0
Michael

Ich habe das Problem mit der folgenden Methode gelöst

Überschreiben Sie func viewDidLayoutSubviews () { wenn flowLayout = collectionView.collectionViewLayout als? UICollectionViewFlowLayout { collectionView.collectionViewLayout.invalidateLayout () collectionView.collectionViewLayout = flowLayout } }

0
CrazyPro007

Ich löse dies, indem ich eine Benachrichtigung einstelle, wenn sich die Bildschirmausrichtung ändert, und eine Zelle neu lädt, die die Elementgröße gemäß der Bildschirmausrichtung festlegt, und den Indexpfad zur vorherigen Zelle setzt. Dies funktioniert auch mit Flowlayout. Hier ist der Code, den ich geschrieben habe:

var cellWidthInLandscape: CGFloat = 0 {
    didSet {
        self.collectionView.reloadData()
    }
}

var lastIndex: Int = 0

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.dataSource = self
    collectionView.delegate = self
    NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
    cellWidthInLandscape = UIScreen.main.bounds.size.width

}
deinit {
    NotificationCenter.default.removeObserver(self)
}
@objc func rotated() {

        // Setting new width on screen orientation change
        cellWidthInLandscape = UIScreen.main.bounds.size.width

       // Setting collectionView to previous indexpath
        collectionView.scrollToItem(at: IndexPath(item: lastIndex, section: 0), at: .right, animated: false)
}
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

   // Getting last contentOffset to calculate last index of collectionViewCell
    lastIndex = Int(scrollView.contentOffset.x / collectionView.bounds.width)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // Setting new width of collectionView Cell
        return CGSize(width: cellWidthInLandscape, height: collectionView.bounds.size.height)

}
0
Yubraj Basyal