it-swarm.com.de

Wann werden layoutSubviews aufgerufen?

Ich habe eine benutzerdefinierte Ansicht, die während der Animation keine layoutSubview-Meldungen erhält.

Ich habe eine Ansicht, die den Bildschirm ausfüllt. Am unteren Bildschirmrand befindet sich eine benutzerdefinierte Unteransicht, deren Größe im Interface Builder korrekt geändert wird, wenn ich die Höhe der Navigationsleiste ändere. layoutSubviews wird beim Erstellen der Ansicht aufgerufen, jedoch nie wieder. Meine Unteransichten sind korrekt angeordnet. Wenn ich die Statusleiste während eines Anrufs ausschalte, wird die layoutSubviews der Unteransicht überhaupt nicht aufgerufen, obwohl die Hauptansicht ihre Größenänderung animiert.

Unter welchen Umständen wird layoutSubviews tatsächlich aufgerufen?

Ich habe autoresizesSubviews für meine benutzerdefinierte Ansicht auf NO gesetzt. Und im Interface Builder habe ich die oberen und unteren Streben und den vertikalen Pfeil eingestellt.

242
Steve Weller

Ich habe die Lösung auf die Behauptung von Interface Builder zurückverfolgt, dass Federn nicht in einer Ansicht geändert werden können, in der die simulierten Bildschirmelemente aktiviert sind (Statusleiste usw.). Da die Quellen für die Hauptansicht deaktiviert waren, konnte diese Ansicht ihre Größe nicht ändern und wurde daher vollständig abgescrollt, als die In-Call-Leiste erschien.

Das Deaktivieren der simulierten Funktionen, das Ändern der Ansichtsgröße und das korrekte Einstellen der Federn führten dazu, dass die Animation auftrat und meine Methode aufgerufen wurde.

Ein zusätzliches Problem beim Debuggen besteht darin, dass der Simulator die App beendet, wenn der Status während eines Anrufs über das Menü umgeschaltet wird. App beenden = kein Debugger.

7
Steve Weller

Ich hatte eine ähnliche Frage, war aber mit der Antwort nicht zufrieden (oder was ich im Internet finden konnte), also habe ich es in der Praxis ausprobiert und hier ist was ich bekommen habe:

  • init bewirkt nicht, dass layoutSubviews aufgerufen wirdbe (duh) 
  • addSubview: bewirkt, dass layoutSubviews für die hinzugefügte -Ansicht aufgerufen wird. Die Ansicht wird hinzugefügt (Zielansicht) und alle Subviews des Ziels
  • view setFrame ruft intelligent layoutSubviews bei der Ansicht auf, für die nur der Frame festgelegt ist , wenn der Größenparameter des Frames unterschiedlich ist
  • durch das Scrollen einer UIScrollView werden layoutSubviews für die scrollView und deren Superview aufgerufen
  • beim Drehen eines Geräts werden nur layoutSubview in der übergeordneten Ansicht aufgerufen (die antwortende viewControllers-Primäransicht ).
  • Wenn Sie die Größe einer Ansicht ändern, wird layoutSubviews im Übersichtsfenster aufgerufen

Meine Ergebnisse - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/

461
BadPirate

Aufbauend auf der vorherigen Antwort von @BadPirate experimentierte ich ein wenig weiter und kam zu einigen Klarstellungen/Korrekturen. Ich habe festgestellt, dass layoutSubviews: nur dann aufgerufen wird, wenn:

  • Die eigenen Grenzen (kein Frame) wurden geändert.
  • Die Grenzen einer direkten Unteransicht haben sich geändert.
  • Eine Unteransicht wird der Ansicht hinzugefügt oder aus der Ansicht entfernt.

Einige relevante Details:

  • Die Grenzen werden nur dann als geändert betrachtet, wenn der neue Wert anders ist, einschließlich eines anderen Ursprungs. Beachten Sie, dass layoutSubviews: immer dann aufgerufen wird, wenn ein UIScrollView-Bildlauf ausgeführt wird, da der Bildlauf durch Ändern des Ursprungs der Begrenzungen durchgeführt wird.
  • Das Ändern des Frames ändert die Grenzen nur, wenn sich die Größe geändert hat, da dies die einzige Sache ist, die an die Eigenschaft "bounds" weitergegeben wird.
  • Eine Änderung der Grenzen einer Ansicht, die sich noch nicht in einer Ansichtshierarchie befindet, führt zu einem Aufruf von layoutSubviews:, wenn die Ansicht schließlich einer Ansichtshierarchie hinzugefügt wird.
  • Und nur der Vollständigkeit halber: Diese Trigger rufen nicht direkt layoutSubviews auf, sondern rufen setNeedsLayout auf, wodurch ein Flag gesetzt wird. Bei jeder Wiederholung der Laufschleife wird für alle Ansichten in der Ansichtshierarchie dieses Flag geprüft. Für jede Ansicht, in der das Flag als angehoben gefunden wird, wird layoutSubviews: für es aufgerufen und das Flag wird zurückgesetzt. Ansichten oberhalb der Hierarchie werden zuerst geprüft/aufgerufen.
67

https://developer.Apple.com/library/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//Apple_ref/doc/uid/TP40009503-CH5ingSW1

Layoutänderungen können auftreten, wenn eines der folgenden Ereignisse auftritt in einer Ansicht:

ein. Die Größe des Begrenzungsrechtecks ​​einer Ansicht ändert sich.
b. Es kommt zu einer Änderung der Schnittstellenorientierung, die normalerweise eine Änderung des Begrenzungsrechtecks ​​der Stammansicht auslöst.
c. Die mit der Ebenenebene der Ansicht verknüpften Satz von Kernanimations-Sublayern ändert sich und erfordert ein Layout.
d. Ihre Anwendung erzwingt das Auftreten des Layouts durch Aufrufen der Methode setNeedsLayout oder layoutIfNeeded einer Ansicht.
e. Ihre Anwendung erzwingt ein Layout, indem Sie die setNeedsLayout-Methode des darunter liegenden Layer-Objekts der Ansicht aufrufen.

14
frogcjn

Einige der Punkte in BadPirates Antwort sind nur teilweise zutreffend:

  1. Für addSubView Punkt 

    addSubview bewirkt, dass layoutSubviews für die hinzugefügte Ansicht, die hinzugefügte Ansicht (Zielansicht) und alle Unteransichten des Ziels aufgerufen werden.

    Dies hängt von der Autoresize-Maske der Ansicht (Zielansicht) ab. Wenn die Maske für die automatische Größenänderung aktiviert ist, wird layoutSubview für jedes addSubview-Objekt aufgerufen. Wenn es keine Maske zum automatischen Ändern der Größe hat, wird layoutSubview nur aufgerufen, wenn sich die Bildgröße der Ansicht (Zielansicht) ändert.

    Beispiel: Wenn Sie UIView programmgesteuert erstellt haben (es hat standardmäßig keine Maske zur automatischen Größenänderung), wird LayoutSubview nur aufgerufen, wenn sich der UIView-Frame nicht in jedem addSubview ändert.

    Durch diese Technik erhöht sich auch die Leistung der Anwendung.

  2. Für den Gerätedrehpunkt

    Beim Drehen eines Geräts wird nur layoutSubview in der übergeordneten Ansicht (der primären Ansicht des antwortenden viewControllers) aufgerufen.

    Dies kann nur dann zutreffen, wenn sich Ihr VC in der Hierarchie VC befindet (root bei window.rootViewController). Dies ist jedoch der häufigste Fall. Wenn Sie unter iOS 5 einen VC erstellen, aber nicht zu einem anderen VC hinzugefügt werden, wird dieser VC beim Drehen des Geräts nicht bemerkt. Daher wird seine Ansicht durch Aufrufen von layoutSubviews nicht bemerkt.

9
Mohit Nigam

durch den Aufruf von [self.view setNeedsLayout]; in viewController wird viewDidLayoutSubviews aufgerufen 

7
bademi

hast du dir LayoutIfNeeded angesehen?

Das Dokumentations-Snippet befindet sich unten. Funktioniert die Animation, wenn Sie diese Methode während der Animation explizit aufrufen?

layoutIfNeeded Legt die Unteransichten bei Bedarf aus.

- (void)layoutIfNeeded

Diskussion Verwenden Sie diese Methode, um das Layout von Unteransichten vor dem Zeichnen zu erzwingen.

Verfügbarkeit Verfügbar in iPhone OS 2.0 und höher.

4

Bei der Migration einer OpenGL-App von SDK 3 auf 4 wurde layoutSubviews nicht mehr aufgerufen. Nach langem Ausprobieren habe ich schließlich MainWindow.xib geöffnet, das Window-Objekt ausgewählt, im Inspektor die Registerkarte "Fensterattribute" (ganz links) ausgewählt und "Sichtbar beim Start" aktiviert. Es scheint, dass in SDK 3 noch ein Aufruf von layoutSubViews ausgelöst wurde, nicht aber in 4.

6 Stunden Frustration sind vorbei.

2
Tal Yaniv

Ein weiterer Teil des Puzzles ist, dass das Fenster zum Schlüssel gemacht werden muss:

[window makeKeyAndVisible];

andernfalls wird die Größe der Unteransichten nicht automatisch geändert.

0
Steve Weller

Ein ziemlich dunkler, aber möglicherweise wichtiger Fall, wenn layoutSubviews niemals aufgerufen wird, lautet:

import UIKit

class View: UIView {

    override class var layerClass: AnyClass { return Layer.self }

    class Layer: CALayer {
        override func layoutSublayers() {
            // if we don't call super.layoutSublayers()...
            print(type(of: self), #function)
        }
    }

    override func layoutSubviews() {
        // ... this method never gets called by the OS!
        print(type(of: self), #function)
    }
}

let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
0
milos