Die Größe der CALayers wurde bei der Änderung der UIView-Grenzen nicht geändert. Warum?


100

Ich habe eine, UIViewdie ungefähr 8 verschiedene CALayerUnterschichten zu ihrer Schicht hinzugefügt hat. Wenn ich die Grenzen der Ansicht ändere (animiert), wird die Ansicht selbst verkleinert (ich habe sie mit a markiertbackgroundColor ), aber die Größe der Unterschichten bleibt unverändert .

Wie kann man das lösen?

Antworten:


149

Ich habe den gleichen Ansatz wie Solin verwendet, aber dieser Code enthält einen Tippfehler. Die Methode sollte sein:

- (void)layoutSubviews {
  [super layoutSubviews];
  // resize your layers based on the view's new bounds
  mylayer.frame = self.bounds;
}

Für meine Zwecke wollte ich immer, dass die Unterebene die volle Größe der übergeordneten Ansicht hat. Fügen Sie diese Methode in Ihre Ansichtsklasse ein.


8
@fishinear bist du sicher? es klingt wie Sie Frame beschreiben. Grenzen sollten theoretisch immer 0,0 als Ursprung haben.
Griotspeak

3
@griotspeak - das ist eigentlich nicht der Fall. Die Beschreibung der Eigenschaft bounds finden Sie hier: developer.apple.com/library/ios/#documentation/uikit/reference/… - "Standardmäßig ist der Ursprung des Begrenzungsrechtecks ​​auf (0, 0) festgelegt, dies ist jedoch möglich Ändern Sie diesen Wert, um verschiedene Teile der Ansicht anzuzeigen. "
JDC

Vielen Dank, ich habe seitdem von einigen Fällen erfahren (zum Beispiel in der Bildlaufansicht), in denen dies nicht der Fall ist, aber ich habe diesen Kommentar vergessen.
Griotspeak

31
Vergessen Sie nicht, [super layoutSubviews] aufzurufen, da dies in verschiedenen UIKit-Klassen zu unerwartetem Verhalten führen kann.
Kpmurphy91

2
Dies funktionierte bei mir mit einer UITextViewSelbstgröße (scrollEnabled = NO) und einem automatischen Layout nicht sehr gut . Ich hatte eine Textansicht an die Übersicht gebunden, die wiederum einen CALayeram unteren Rand (einen Rand) hatte, und ich wollte , dass die Randposition aktualisiert wird, wenn sich die Höhe der Textansicht ändert. Aus irgendeinem Grund layoutSubiewswurde viel zu spät aufgerufen (und die Ebenenposition wurde animiert).
Iosdude

26

Da CALayer auf dem iPhone keine Layout-Manager unterstützt, müssen Sie die Hauptebene Ihrer Ansicht zu einer benutzerdefinierten CALayer-Unterklasse machen, in der Sie überschreiben layoutSublayers, um die Frames aller Unterschichten festzulegen. Sie müssen auch die +layerClassMethode Ihrer Ansicht überschreiben , um die Klasse Ihrer neuen CALayer-Unterklasse zurückzugeben.


Override + LayerClass wie bei OpenGL Layer Override? Denke schon. Rahmen der Unterschichten einstellen? Auf was einstellen? Muss ich alle Frames / Positionen der Sublayer einzeln berechnen, abhängig von der Frame-Größe der Superlayer? Gibt es keine "Constraint-ähnliche" oder "Link-to-Superlayer" -ähnliche Lösung? Oh Gott, ein weiterer Tag außerhalb des Zeitrahmens. CGLayers wurde in der Größe geändert, war aber zu verzögert. CALayers sind schnell genug, wurden aber nicht in der Größe geändert. So viele überraschen. Trotzdem danke für die Antwort.
Geri Borbás

3
CALayer auf dem Mac verfügt über Layout-Manager, die jedoch auf dem iPhone nicht verfügbar sind. Also ja, Sie müssen die Rahmengrößen selbst berechnen. Eine andere Möglichkeit könnte darin bestehen, Unteransichten anstelle von Unterschichten zu verwenden. Dann können Sie die Autoresizing-Masken der Unteransichten entsprechend einstellen.
Ole Begemann

2
Ja, UIViews sind zu leistungsintensive Klassen, weshalb ich CALayers verwende.
Geri Borbás

Ich habe versucht, wie Sie vorgeschlagen haben. Es funktioniert und ist es nicht. Ich ändere die Größe der animierten Ansicht, aber die Größe der Unterebenen wird in einer anderen Animation geändert. Verdammt, was soll ich jetzt tun? Wie kann in der CALayer-Unterklasse überschrieben werden, was in JEDEM (!) Frame der Ansichtsanimation aufgerufen wird? Gibt es irgendwelche?
Geri Borbás

Ich bin auch nur darauf gestoßen. Scheint die beste Option zu sein, CALayer zu unterordnen, wie Ole sagte, aber es scheint unnötig umständlich.
Dimitri

14

Ich habe dies in der UIView verwendet.

-(void)layoutSublayersOfLayer:(CALayer *)layer
{
    if (layer == self.layer)
    {
        _anySubLayer.frame = layer.bounds;
    }

super.layoutSublayersOfLayer(layer)
}

Funktioniert bei mir.


Ja, das hat bei mir in iOS 8 funktioniert. Vermutlich würde es in früheren Versionen funktionieren. Ich hatte eine Verlaufshintergrund-Ebene und musste die Größe der Ebene ändern, wenn das Gerät gedreht wird.
EPage_Ed

Arbeitete für mich, aber es hat einen Anruf über super benötigen
Entropie

Das ist die beste Antwort! Ja, ich habe layoutSubviews ausprobiert, aber es bricht nur das Layout meines UITextField, wie das Verschwinden von leftView und rightView und das Verschwinden von mehr Text in UITextField. Vielleicht habe ich die Erweiterung anstelle der Vererbung von UIView verwendet, aber es war sehr fehlerhaft. DAS FUNKTIONIERT GROSSARTIG! THX
Michał Ziobro

Für eine Schicht mit benutzerdefinierter Zeichnung ( CALayerDelegates draw(_:in:)), musste ich anrufen auch _anySubLayer.setNeedsDisplay(). Dann hat das bei mir funktioniert.
jedwidz

9

Ich hatte das gleiche Problem. In der Ebene einer benutzerdefinierten Ansicht habe ich zwei weitere Unterebenen hinzugefügt. Um die Größe der Unterebenen zu ändern (jedes Mal, wenn sich die Grenzen der benutzerdefinierten Ansicht ändern), habe ich die Methode layoutSubviewsmeiner benutzerdefinierten Ansicht implementiert . Bei dieser Methode aktualisiere ich einfach den Frame jeder Unterschicht, um ihn an die aktuellen Grenzen der Ebene meiner Unteransicht anzupassen.

Etwas wie das:

-(void)layoutSubviews{
   //keep the same origin, just update the width and height
   if(sublayer1!=nil){
      sublayer1.frame = self.layer.bounds;
   }
}

16
Zu Ihrer Information, Sie brauchen das if (sublayer1! = Nil) nicht, da ObjC Nachrichten zu nil definiert (in diesem Fall -setFrame :) als No-Op, das 0 zurückgibt.
Andrew Pouliot

7

2017

Die wörtliche Antwort auf diese Frage:

"Die Größe der CALayers wurde bei der Änderung der Grenzen von UIView nicht geändert. Warum?"

ist das zum Guten oder Schlechten

needsDisplayOnBoundsChange

Der Standardwert ist false in CALayer.

Lösung,

class CircularGradientViewLayer: CALayer {
    
    override init() {
        
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override open func draw(in ctx: CGContext) {
        go crazy drawing in .bounds
    }
}

In der Tat leite ich Sie zu dieser Qualitätssicherung

https://stackoverflow.com/a/47760444/294884

was erklärt, was zum Teufel die kritische contentsScaleEinstellung tut; Normalerweise müssen Sie dies gleichermaßen festlegen, wenn Sie requireDisplayOnBoundsChange festlegen.


5

Swift 3 Version

Fügen Sie in der benutzerdefinierten Zelle die folgenden Zeilen hinzu

Zuerst deklarieren

let gradientLayer: CAGradientLayer = CAGradientLayer()

Fügen Sie dann die folgenden Zeilen hinzu

override func layoutSubviews() {
    gradientLayer.frame = self.YourCustomView.bounds
}

0

Wie [Ole] schrieb, unterstützt CALayer die automatische Größenänderung unter iOS nicht. Sie sollten das Layout also manuell anpassen. Meine Option war, den Rahmen der Ebene innerhalb von (iOS 7 und früher) anzupassen.

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 

oder (ab iOS 8)

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

0

In Ihrer benutzerdefinierten Ansicht müssen Sie eine Variable für Ihre benutzerdefinierte Ebene deklarieren. Deklarieren Sie keine Variable in scope init. Und starten Sie es einfach einmal, versuchen Sie nicht, den Nullwert festzulegen und neu zu starten

    Klasse CustomView: UIView {
        var customLayer: CALayer = CALayer ()

        überschreiben func layoutSubviews () {
            super.layoutSubviews ()
    // guard let _fillColor = self._fillColor else {return}
            initializeLayout ()
        }}
        private func initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (at: 0)
        }}
    }}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.