LayoutSubviews ne zaman çağrılır?


264

layoutSubviewAnimasyon sırasında mesaj almayan özel bir görünüm var .

Ekranı dolduran bir görüşüm var. Gezinme çubuğunun yüksekliğini değiştirirsem, ekranın altında Arayüz Oluşturucu'da doğru şekilde yeniden boyutlandırılan özel bir alt görünümü vardır. layoutSubviewsgörünüm oluşturulduğunda çağrılır, ancak bir daha asla. Alt görünümlerim doğru bir şekilde düzenlenmiş. Arama sırasında durum çubuğunu kapatırsam, layoutSubviewsana görünüm yeniden boyutlandırılmasını canlandırsa bile alt görünümün hiç çağrılmaz.

layoutSubviewsGerçekten hangi şartlar altında adlandırılır?

Ben var autoresizesSubviewsayarlı NObenim özel bir görünüm için. Ve Interface Builder'da üst ve alt payandalara ve dikey ok setine sahibim.


Bulmacanın bir başka kısmı da pencerenin anahtar yapılması gerektiğidir:

[window makeKeyAndVisible];

aksi takdirde alt görünümler otomatik olarak yeniden boyutlandırılmaz.

Yanıtlar:


492

Benzer bir sorum vardı, ama cevaptan (ya da internette bulabildiğim herhangi birinden) memnun kalmadım, bu yüzden pratikte denedim ve işte ne var:

  • initlayoutSubviewsçağrılmasına neden olmaz (duh)
  • addSubview:neden olur layoutSubviewseklenmektedir görünümde çağrılacak, görünümü (hedef görünümüne) eklenmiştir ve hedefin tüm subviews ediliyor
  • view , çerçevesinin yalnızca çerçevenin boyut parametresi farklıysa ayarlanan görünümü setFrame akıllıca çağırırlayoutSubviews
  • bir UIScrollView öğesinin kaydırılması layoutSubviewsscrollView öğesinde çağrılmasına ve denetimin
  • bir cihazı döndürmek yalnızca layoutSubviewüst görünümden çağırır (yanıt veren görünüm Kontrolörler birincil görünümü)
  • Bir görünümü yeniden boyutlandırmak layoutSubviewsonun denetimini çağırır

Sonuçlarım - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/


1
Mükemmel cevap. Hep merak ettim layoutSubviews. Does initWithFrame:nedeni layoutSubviewsçağrılacak?
Robert

2
@Robert - initWithFrame kullanıyordum ... yani hayır.
BadPirate

8
@BadPirate: evet . Yeniden boyutlandırmak eğer benim deneylerine göre, view1.1bu aramaları layoutSubviewsarasında view1ve daha sonra layoutSubviewsbir view1.1. Bu çağrı üzerine çağırarak, superviews için süresiz yaymak değil view1.1.1sadece aramaları layoutSubviewsüzerine view1.1ve view1.1.1. Sadece boyutunu değiştirmeden hareket etmek hiçbirini çağırmaz layoutSubviews.
João Portela

1
viewDidLoad UIView'de (ancak UIViewController'da) çağrılmaz. Görüntüleme UIView başlatıldıktan sonra yük çağrıldı.
BadPirate

2
Benim deney dayanarak, ikinci kural doğru olmayabilir: Ben eklediğinizde view1.2içine view1, layoutSubviewsait view1.2ve view1denir, ama layoutSubviewsbir view1.1çağrılmaz. ( view1.1ve view1.2alt görünümleridir view1). Yani, hedef görünümün tüm alt görünümlerine layoutSubviewsyöntem adı verilmez .
HongchaoZhang

96

@BadPirate tarafından verilen önceki cevaba dayanarak, biraz daha denedim ve bazı açıklamalar / düzeltmeler yaptım. Ben layoutSubviews:bir görünümde sadece ve eğer eğer çağrılacak bulundu :

  • Kendi sınırları (çerçeve değil) değişti.
  • Doğrudan alt görüşmelerinden birinin sınırları değişti.
  • Görünüme bir alt görünüm eklenir veya görünümden kaldırılır.

İlgili bazı ayrıntılar:

  • Sınırlar, ancak farklı bir başlangıç ​​noktası dahil olmak üzere yeni değer farklıysa değiştirilmiş sayılır . Özellikle layoutSubviews:UIScrollView her kaydırıldığında çağrıldığından, sınırın kökenini değiştirerek kaydırma işlemini gerçekleştirdiğini unutmayın.
  • Çerçeveyi değiştirmek yalnızca boyut değiştiğinde sınırları değiştirir, çünkü bounds özelliğine yayılan tek şey budur.
  • Henüz görünüm hiyerarşisinde olmayan bir görünümün sınırlarında yapılan bir değişiklik layoutSubviews: , görünümün sonunda görünüm hiyerarşisine eklendiğinde çağrılmasına neden olur .
  • Ve sadece bütünlük için: bu tetikleyiciler doğrudan layoutSubviews'ı çağırmaz, daha çok setNeedsLayoutbir bayrağı ayarlayan / yükselten çağrıdır . Çalışma döngüsünün her yinelemesi , görünüm hiyerarşisindeki tüm görünümler için bu bayrak işaretlenir. Bayrağın bulunduğu her görünüm için layoutSubviews:üzerine çağrılır ve bayrak sıfırlanır. Hiyerarşinin yukarısındaki görünümler önce kontrol edilecek / çağrılacaktır.

5
bu cevabı yeterince değerlendiremiyorum. en iyi cevap olmalı. verilen üç kural yeterlidir. ben asla bu kuralların mükemmel tarif etmedi herhangi bir layoutSubview davranış rastlamak değil.
Pärserk

1
Doğrudan bir alt görünümün sınırları değiştiğinde layoutSubviews'in çağrıldığını düşünmüyorum. Ben layoutSubviews çağrısı test sadece bir yan etkisi olduğunu düşünüyorum. Bazı durumlarda, bir alt görünüm sınırları değiştiğinde layoutSubviews çağrılamaz. Lütfen frogcjn'ın cevabını kontrol edin, çünkü anwser sadece deneyler yerine Apple'ın belgelerine dayanmaktadır.
Simon Backx

19

https://developer.apple.com/library/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_ref/doc/uid/TP40009503-CH5-SW1

Görünümde aşağıdaki olaylardan herhangi biri gerçekleştiğinde düzen değişiklikleri oluşabilir:

a. Bir görünümün sınır dikdörtgeninin boyutu değişir.
b. Genellikle kök görünümünün sınır dikdörtgeninde bir değişikliği tetikleyen bir arabirim yönlendirme değişikliği oluşur.
c. Görünümün katmanıyla ilişkilendirilmiş Temel Animasyon alt katmanları kümesi değişir ve düzen gerektirir.
d. Uygulamanız  bir görünümün setNeedsLayout veya  layoutIfNeededyöntemini çağırarak mizanpajı oluşmaya zorlar  .
e. Uygulamanız setNeedsLayout , görünümün temel katman nesnesinin yöntemini çağırarak düzeni zorlar  .


Ayrıca, önemli olan, görünüm henüz görünüm yığınına eklenmediğinde bu olayların hiçbirinin çağrılmamasıdır. Bunu "can" kelimesi ile dahil edersiniz, ancak özellikle bu olaylardan biri tarafından düzen gerekli olarak işaretlendiğinde uygulamanın ana iş parçacığının bir sonraki kullanılabilir döngüsünde çağrılır.
user1122069

13

BadPirate'nin cevabındaki bazı noktalar sadece kısmen doğrudur:

  1. için addSubViewnoktası

    addSubview Eklenen görünüme, eklendiği görünüme (hedef görünüm) ve hedefin tüm alt görünümlerine layoutSubviews çağrılmasına neden olur.

    Görünümün (hedef görünüm) otomatik boyutlandırma maskesine bağlıdır. Otomatik boyutlandırma maskesi AÇIK ise, her birinde layoutSubview çağrılıraddSubview . Otomatik boyutlandırma maskesi yoksa, layoutSubview yalnızca görünümün (hedef Görünüm) çerçeve boyutu değiştiğinde çağrılır.

    Örnek: UIView programını programlı olarak oluşturduysanız (varsayılan olarak otomatik boyutlandırma maskesi yoktur), LayoutSubview yalnızca her UIView çerçevesi değişmediğinde çağrılır addSubview .

    Bu teknik sayesinde uygulamanın performansı da artmaktadır.

  2. Cihazın dönme noktası için

    Bir cihazı döndürmek yalnızca üst görünümde layoutSubview öğesini çağırır (yanıt veren görünüm Kontrolörün birincil görünümü)

    Bu sadece VC'niz VC hiyerarşisinde (root at window.rootViewController) olduğunda geçerli olabilir, bu en yaygın durumdur. İOS 5'te, bir VC oluşturursanız, ancak başka bir VC'ye eklenmezse, cihaz döndüğünde bu VC fark edilmez. Bu nedenle, layoutSubviews çağrılarak görünümü fark edilmez.


9

Çözümü, Interface Builder'ın, simüle edilmiş ekran öğelerinin açık olduğu bir görünümde (durum çubuğu vb.) Yayların değiştirilemeyeceği konusunda ısrar ettim. Yaylar ana görünüm için kapalı olduğundan, bu görünüm boyutu değiştirilemedi ve bu nedenle çağrı çubuğu görüntülendiğinde bütünüyle aşağı kaydırıldı.

Simüle edilen özelliklerin kapatılması, ardından görünümün yeniden boyutlandırılması ve yayların doğru şekilde ayarlanması animasyonun oluşmasına ve yöntemimin çağrılmasına neden oldu.

Bu hata ayıklamada ekstra bir sorun, arama sırasında durum menü aracılığıyla değiştirildiğinde simülatörün uygulamadan çıkmasıdır. Uygulamadan çık = hata ayıklayıcı yok.


Görünüm yeniden boyutlandırıldığında layoutSubviews adının çağrıldığını mı söylüyorsunuz? Her zaman böyle olmadığını varsaydım ...
Andrey Tarantsov

Bu. Bir görünüm yeniden boyutlandırıldığında, alt görünümleriyle bir şeyler yapması gerekir. Bunu sağlamazsanız, yaylar, payandalar vb. Kullanarak otomatik olarak hareket ettirir.
Steve Weller

8

çağıran [self.view setNeedsLayout]; viewController çağrı viewDidLayoutSubviews bunu yapar


5

layoutIfNeeded'e baktın mı?

Doküman snippet'i aşağıdadır. Animasyon sırasında bu yöntemi açıkça çağırırsanız animasyon çalışır mı?

layoutIfNeeded Gerekirse alt görünümleri düzenler.

- (void)layoutIfNeeded

Tartışma Çizimden önce alt görünümlerin yerleşimini zorlamak için bu yöntemi kullanın.

Kullanılabilirlik iPhone OS 2.0 ve sonraki sürümlerinde kullanılabilir.


2

Bir OpenGL uygulamasını SDK 3'ten 4'e taşırken, layoutSubviews artık çağrılmadı. Birçok deneme yanılma işleminden sonra nihayet MainWindow.xib'i açtım, pencere nesnesini seçtim. SDK 3'te hala bir layoutSubViews çağrısına neden olduğu, ancak 4'te kullanılmadığı anlaşılıyor.

6 saatlik hayal kırıklığı sona erdi.


Pencere anahtarını yaptın mı? Değilse, bu her türlü ilginç şeyin olmamasına neden olabilir.
Steve Weller

-2

layoutSubviewsAsla çağrılmadığında oldukça belirsiz, ancak potansiyel olarak önemli bir durum :

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

1
Bu cevap neden burada?
Dominik Bucher
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.