CALayers, UIView sınırları değişikliğinde yeniden boyutlandırılmadı. Neden?


101

Katmanına eklenen UIViewyaklaşık 8 farklı CALayeralt katmana sahibim . Eğer ben görünümün sınırları değiştirmek (animasyonlu), ardından görünüm kendisi (Ben bunu kontrol küçülür backgroundColor), ancak alt katmanlar boyutu değişmeden kalır .

Bu nasıl çözülür?

Yanıtlar:


149

Solin'in kullandığı yaklaşımın aynısını kullandım ama bu kodda bir yazım hatası var. Yöntem şöyle olmalıdır:

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

Amaçlarım için, alt katmanın her zaman üst görünümün tam boyutu olmasını istedim. Bu yöntemi kendi görüş sınıfınıza koyun.


8
@fishinear emin misin? çerçeve tanımlıyormuşsunuz gibi geliyor. teorik olarak sınırların orijini her zaman 0,0 olmalıdır.
griotspeak

3
@griotspeak - aslında durum böyle değil. Bounds özelliğinin açıklamasına buradan bakın: developer.apple.com/library/ios/#documentation/uikit/reference/… - "Varsayılan olarak, sınırlar dikdörtgeninin başlangıç ​​noktası (0, 0) olarak ayarlanmıştır ancak şunları yapabilirsiniz: görünümün farklı bölümlerini görüntülemek için bu değeri değiştirin. "
jdc

Çok teşekkürler, o zamandan beri bunun doğru olmadığı birkaç vakayı (biri için kaydırma görünümü) öğrendim ama bu yorumu unuttum.
griotspeak

31
Çeşitli UIKit sınıflarında beklenmedik davranışlara neden olabileceğinden [super layoutSubviews] 'i çağırmayı unutmayın.
Kpmurphy91

2
Bu, kendi kendine boyutlandırma UITextView(scrollEnabled = NO) ve otomatik düzen ile benim için pek işe yaramadı . Gözetleme görünümüne sabitlenmiş bir metin görünümü vardı, bu da CALayerkendi alt kısmında (bir kenarlık) vardı ve metin görünümünün yüksekliği değiştiğinde kenarlık konumunu güncellemesini istedim. Nedense layoutSubiewsçok geç çağrıldı (ve katman konumu canlandırıldı).
iosdude

26

İPhone'daki CALayer, yerleşim yöneticilerini desteklemediğinden, görünümünüzün ana katmanını layoutSublayers, tüm alt katmanların çerçevelerini ayarlamak için geçersiz kıldığınız özel bir CALayer alt sınıfı yapmanız gerektiğini düşünüyorum . +layerClassYeni CALayer alt sınıfınızın sınıfını döndürmek için görünümünüzün yöntemini de geçersiz kılmalısınız.


+ LayerClass'ı, OpenGL katman geçersiz kılma durumunda olduğu gibi geçersiz kıl? Öyle düşünüyorum. Alt katmanların çerçeveleri belirlensin mi? Neye ayarla? Üst katman çerçeve boyutuna bağlı olarak tüm alt katmanların çerçevelerini / konumlarını ayrı ayrı hesaplamalı mıyım? "Kısıtlama benzeri" veya "süper katmana bağlantı" benzeri bir çözüm yok mu? Tanrım, zaman çerçevesinin dışında başka bir gün. CGLayers yeniden boyutlandırıldı, ancak çok gecikti, CALayers yeterince hızlı, ancak yeniden boyutlandırılmadı. Çok fazla sürpriz. Cevabınız için yine de teşekkürler.
Geri Borbás

3
Mac'teki CALayer, bunun için düzen yöneticilerine sahiptir ancak bunlar iPhone'da mevcut değildir. Yani evet, çerçeve boyutlarını kendiniz hesaplamalısınız. Diğer bir seçenek, alt katmanlar yerine alt görünümleri kullanmak olabilir. Ardından, alt görünümlerin otomatik yeniden boyutlandırma maskelerini buna göre ayarlayabilirsiniz.
Ole Begemann

2
Ya, UIViews çok pahalı performans sınıfları, bu yüzden CALayer kullanıyorum.
Geri Borbás

Önerdiğin yolu denedim. Çalışıyor ve değil. Görünümü canlandırılmış olarak yeniden boyutlandırıyorum, ancak alt katmanlar farklı bir animasyonda yeniden boyutlandırılıyor. Lanet olsun, şimdi ne yapmalı? CALayer alt sınıfında, görünümün animasyonunun HER (!) Karesinde neyi çağıran geçersiz kılma yöntemi nedir? Var mı?
Geri Borbás

Sadece buna da rastladım. Görünüşe göre en iyi seçenek Ole'nin dediği gibi CALayer'ı alt sınıflara ayırmaktır, ancak gereksiz yere hantal görünüyor.
Dimitri

15

Bunu UIView'de kullandım.

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

super.layoutSublayersOfLayer(layer)
}

Benim için çalışıyor.


Evet, bu benim için iOS 8'de çalıştı. Muhtemelen önceki sürümlerde çalışacaktı. Bir degrade arka plan katmanım vardı ve cihaz döndürüldüğünde katmanı yeniden boyutlandırmam gerekiyordu.
EPage_Ed

Benim için çalıştı, ancak süper üzerine bir çağrı gerek yoktu
entropi

Bu en iyi cevap! Evet, layoutSubviews'i denedim ama bu sadece UITextField'ımın düzenini bozuyor, örneğin leftView ve rightView kayboluyor ve UITextField'da daha fazla Metin kayboluyor. Belki UIView mirası yerine uzantı kullandım, ancak çok hatalıydı. BU BÜYÜK ÇALIŞIYOR! THX
Michał Ziobro

Özel çizim ( CALayerDelegateler draw(_:in:)) içeren bir katman için ayrıca aramak zorunda kaldım _anySubLayer.setNeedsDisplay(). Sonra bu benim için çalıştı.
jedwidz

9

Ben de aynı sorunu yaşadım. Özel bir görünümün katmanına iki alt katman daha ekledim. Alt katmanları yeniden boyutlandırmak için (özel görünümün sınırları her değiştiğinde), yöntemi uyguladımlayoutSubviews özel görünümümün ; bu yöntemin içinde, her bir alt katmanın çerçevesini, alt görünümümün katmanının mevcut sınırlarıyla eşleşecek şekilde güncelliyorum.

Bunun gibi bir şey:

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

16
Bilginize, ObjC iletileri sıfır olarak tanımladığından (alt katman1! = Nil) if (alt katman1! = Nil) 'e ihtiyacınız yok (bu durumda, -setFrame :), 0'ı döndüren bir işlem yok olarak tanımlıyor.
Andrew Pouliot

7

2017

Bu sorunun gerçek cevabı:

"CALayers, UIView sınırları değiştiğinde yeniden boyutlandırılmadı. Neden?"

bu daha iyi mi kötü mü

needsDisplayOnBoundsChange

varsayılan olarak yanlış girilir CALayer.

çözüm,

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
    }
}

Doğrusu, sizi bu QA'ya yönlendiriyorum

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

bu kritik contentsScaleortamın ne yaptığını açıklıyor ; needDisplayOnBoundsChange'i ayarladığınızda genellikle bunu eşit şekilde ayarlamanız gerekir.


5

Swift 3 Sürümü

Özel hücrede, Aşağıdaki satırları ekle

Önce beyan et

let gradientLayer: CAGradientLayer = CAGradientLayer()

Ardından aşağıdaki satırları ekleyin

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

0

[Ole] 'nin yazdığı gibi CALayer, iOS'ta otomatik yeniden boyutlandırmayı desteklemiyor. Bu yüzden düzeni manuel olarak ayarlamalısınız. Benim seçeneğim, içinde katman çerçevesini ayarlamaktı (iOS 7 ve öncesi)

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

veya (iOS 8 itibariyle)

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

0

Özel görünümünüzde, özel katmanınız için değişken bildirmeniz gerekir, init kapsamındaki değişken bildirmeyin. Ve sadece bir kez başlatın, sıfır değeri ayarlamayı ve yeniden başlatmayı denemeyin

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

        override func layoutSubviews () {
            super.layoutSubviews ()
    // koruma _fillColor = self._fillColor else {return} sağlar
            initializeLayout ()
        }
        özel func initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (at: 0)
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.