UIView alt sınıflaması için uygun uygulama?


158

Bazı özel UIView tabanlı giriş denetimleri üzerinde çalışıyorum ve görünümü ayarlamak için uygun uygulamayı bulmaya çalışıyorum. UIViewController ile çalışırken, kullanımı oldukça basit loadViewve ilgili viewWill, viewDidyöntemler, ancak bir UIView sınıflara, ben var en yakın methosds vardır `awakeFromNib, drawRectve layoutSubviews. (Kurulum ve sökme geri çağrıları açısından düşünüyorum.) Benim durumumda, çerçeve ve iç görünümlerimi ayarlıyorum layoutSubviews, ama ekranda hiçbir şey görmüyorum.

Görüşümün sahip olmasını istediğim doğru yükseklik ve genişliğe sahip olmasını sağlamanın en iyi yolu nedir? (İki cevap olsa da, sorum otomatik düzenlemeyi kullanıp kullanmamaya bakılmaksızın geçerlidir.) Uygun "en iyi uygulama" nedir?

Yanıtlar:


298

Apple, alt sınıfların nasıl oluşturulacağını oldukça açık bir şekilde tanımladı UIView , dokümanda .

Aşağıdaki listeye bir göz atın, özellikle initWithFrame:ve layoutSubviews. Birincisi,UIView ve ikincisinin çerçevesini ve çerçevesini ayarlamayı amaçlamaktadır.

Bunun initWithFrame:sadece UIViewprogramınızı başlatırsanız çağrıldığını da unutmayın . Eğer bir uç dosyasından (veya film şeridinden) yüklüyorsanız, initWithCoder:kullanılır. Ve initWithCoder:çerçevede henüz hesaplanmadı, bu yüzden Arayüz Oluşturucu'da ayarladığınız çerçeveyi değiştiremezsiniz. Önerildiği gibi bu cevap aradığınız düşünebilirler initWithFrame:gelen initWithCoder:kurulum için çerçeveyi.

Son olarak, UIViewbir uçtan (veya film şeridinden) yüklerseniz, awakeFromNibözel çerçeve ve düzen başlatma işlemleri gerçekleştirme fırsatınız da vardır , çünkü awakeFromNibçağrıldığında hiyerarşideki her görünümün arşivden çıkarıldığı ve başlatıldığı garanti edilir.

NSNibAwaking(Şimdi doktorun yerini almıştır awakeFromNib) belgesinden :

Diğer nesnelere mesajlar awakeFromNib içinden güvenli bir şekilde gönderilebilir; bu süre zarfında tüm nesnelerin arşivden çıkarıldığından ve başlatıldığından emin olunur (tabii ki mutlaka uyandırılmamasına rağmen)

Ayrıca, otomatik düzenleme ile görünümünüzün çerçevesini açıkça ayarlamamanız gerektiğini de belirtmek gerekir. Bunun yerine, çerçevenin düzen motoru tarafından otomatik olarak hesaplanması için bir dizi yeterli kısıtlama belirtmeniz gerekir.

Doğrudan dokümantasyondan :

Geçersiz Kılma Yöntemleri

Başlatma

  • initWithFrame:Bu yöntemi uygulamanız önerilir. Bu yönteme ek olarak veya bu yöntemin yerine özel başlatma yöntemlerini de uygulayabilirsiniz.

  • initWithCoder: Görünümünüzü bir Interface Builder uç dosyasından yüklerseniz ve görünümünüz özel başlatma gerektiriyorsa bu yöntemi uygulayın.

  • layerClassBu yöntemi yalnızca, görünümünüzün destek deposu için farklı bir Temel Animasyon katmanı kullanmasını istiyorsanız uygulayın. Örneğin, çiziminizi yapmak için OpenGL ES kullanıyorsanız, bu yöntemi geçersiz kılmak ve CAEAGLLayer sınıfını döndürmek istersiniz.

Çizim ve baskı

  • drawRect:Görünümünüz özel içerik çiziyorsa bu yöntemi uygulayın. Görünümünüz herhangi bir özel çizim yapmıyorsa, bu yöntemi geçersiz kılmaktan kaçının.

  • drawRect:forViewPrintFormatter: Bu yöntemi yalnızca yazdırma sırasında görünümünüzün içeriğini farklı şekilde çizmek istiyorsanız uygulayın.

Kısıtlamalar

  • requiresConstraintBasedLayout Görünüm sınıfınız düzgün çalışmak için kısıtlamalar gerektiriyorsa bu sınıf yöntemini uygulayın.

  • updateConstraints Görünümünüzün alt görünümleriniz arasında özel kısıtlamalar oluşturması gerekiyorsa bu yöntemi uygulayın.

  • alignmentRectForFrame:, frameForAlignmentRect:Görünümlerinizin diğer görünümlerle nasıl hizalandığını geçersiz kılmak için bu yöntemleri uygulayın.

Yerleşim

  • sizeThatFits:Görünümünüzün yeniden boyutlandırma işlemleri sırasında normalden farklı bir varsayılan boyuta sahip olmasını istiyorsanız bu yöntemi uygulayın. Örneğin, görünümünüzün alt görüntülerin düzgün görüntülenemediği noktaya çekilmesini önlemek için bu yöntemi kullanabilirsiniz.

  • layoutSubviews Alt görünümlerinizin düzeni üzerinde kısıtlama veya otomatikleştirme davranışlarının sağladığından daha kesin bir kontrole ihtiyacınız varsa bu yöntemi uygulayın.

  • didAddSubview:, willRemoveSubview:Alt görünümlerin eklenmesini ve kaldırılmasını izlemek için bu yöntemleri gerektiği gibi uygulayın.

  • willMoveToSuperview:, didMoveToSuperviewGeçerli görünümün görünüm hiyerarşinizde hareketini izlemek için bu yöntemleri gerektiği gibi uygulayın.

  • willMoveToWindow:, didMoveToWindowGörünümünüzün hareketini farklı bir pencereye izlemek için bu yöntemleri gerektiği gibi uygulayın.

Olay işleme:

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent:Doğrudan dokunmatik olayları işlemek gerekiyorsa bu yöntemleri uygulamak. (Harekete dayalı giriş için hareket tanımlayıcıları kullanın.)

  • gestureRecognizerShouldBegin: Görünümünüz dokunma olaylarını doğrudan işliyorsa ve iliştirilmiş hareket tanıma araçlarının ek eylemleri tetiklemesini önlemek isteyebilirse bu yöntemi uygulayın.


- (void) setFrame: (CGRect) çerçevesi ne olacak?
pfrank

peki kesinlikle geçersiz kılabilirsiniz, ama hangi amaçla?
Gabriele Petronella

boyutunu veya konum değiştiğinde düzeni / çizimi değiştirmek için
pfrank

1
Ne olmuş layoutSubviews?
Gabriele Petronella

Gönderen stackoverflow.com/questions/4000664/... , "Bu noktadaki sorunsa o subviews sadece kendi boyutunu değiştiremezsiniz, ancak o boyut değişikliği canlandırabilirsiniz olduğunu. UIView animasyon çalıştığında, her seferinde layoutSubviews çağırmaz." Kişisel olarak olsa test
etmedim

38

Bu Google'da hala yüksek. Aşağıda swift için güncellenmiş bir örnek bulunmaktadır.

Bu didLoadişlev, tüm özel başlatma kodunuzu girmenizi sağlar. Diğerlerinin de belirttiği gibi, didLoadbir görünüm programlı olarak program aracılığıyla oluşturulduğunda init(frame:)veya XIB serileştiricisi bir XIB şablonunu görünümünüzle birleştirdiğinde çağrılırinit(coder:)

Kenara : layoutSubviewsve updateConstraintsgörüşlerin çoğunluğu için birden çok kez çağrılır. Bu, bir görünümün sınırları değiştiğinde gelişmiş çok geçişli düzenler ve ayarlamalar için tasarlanmıştır. Şahsen, mümkün olduğunca çok geçişli düzenlerden kaçınırım çünkü CPU döngülerini yakarlar ve her şeyi baş ağrısı haline getirir. Ayrıca, nadiren geçersiz kıldığım için başlatıcılara kısıtlama kodu koydum.

import UIKit

class MyView: UIView {
  //-----------------------------------------------------------------------------------------------------
  //Constructors, Initializers, and UIView lifecycle
  //-----------------------------------------------------------------------------------------------------
  override init(frame: CGRect) {
      super.init(frame: frame)
      didLoad()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    didLoad()
  }

  convenience init() {
    self.init(frame: CGRectZero)
  }

  func didLoad() {
    //Place your initialization code here

    //I actually create & place constraints in here, instead of in
    //updateConstraints
  }

  override func layoutSubviews() {
     super.layoutSubviews()

     //Custom manually positioning layout goes here (auto-layout pass has already run first pass)
  }

  override func updateConstraints() {
    super.updateConstraints()

    //Disable this if you are adding constraints manually
    //or you're going to have a 'bad time'
    //self.translatesAutoresizingMaskIntoConstraints = false

    //Add custom constraint code here
  }
}

Düzen kısıtlama kodunu başlangıç ​​işleminizden çağrılan bir yöntem ile layoutSubviews ve updateConstraints arasında ne zaman / neden böleceğinizi açıklayabilir misiniz? Görünüşe göre, yerleşim kodunu yerleştirmek için üç olası aday konumu da var. Peki, düzen kodunu üçü arasında ne zaman / ne / neden böleceğinizi nereden biliyorsunuz?
Clay Ellis

3
Ben asla updateConstraints kullanmıyorum; updateConstraints güzel olabilir, çünkü görünüm hiyerarşinizin tamamen init olarak kurulduğunu biliyorsunuz, bu nedenle hiyerarşide olmayan iki görünüm arasına bir kısıtlama ekleyerek bir istisna oluşturamazsınız :) layoutSubviews hiçbir zaman kısıtlama değişikliği yapmamalıdır; mizanpaj geçişi sırasında kısıtlamalar 'geçersiz kılındıysa layoutSubviews çağrıldığında kolayca sonsuz özyinelemeye neden olabilir. Manuel düzen kurulumu (doğrudan çerçeve ayarlamada olduğu gibi, performans nedenlerinden ötürü nadiren yapmanız gereken) layoutSubviews'e gider. Şahsen, init
seo'da

Özel oluşturma kodu için drawyöntemi geçersiz kılmalıyız?
Petrus Theron

14

Apple belgelerinde iyi bir özet var ve bu, iTunes'daki ücretsiz Stanford kursunda iyi bir şekilde ele alındı. TL; DR versiyonumu burada sunuyorum:

Sınıfınız çoğunlukla alt görüşlerden oluşuyorsa, bunları ayırmak için doğru yer inityöntemlerdir. Görünümler için, initgörünümünüz koddan veya bir uç / film şeridinden örneklenip oluşturulmadığına bağlı olarak çağrılabilecek iki farklı yöntem vardır. Yaptığım şey kendi setupyöntemimi yazmak ve sonra hem initWithFrame:ve initWithCoder:yöntemlerinden çağırmak .

Özel çizim yapıyorsanız, gerçekten drawRect:görünümünüzde geçersiz kılmak istersiniz . Özel görünümünüz çoğunlukla alt görünümler için bir kapsayıcıysa, muhtemelen bunu yapmanız gerekmez.

Yalnızca layoutSubViewsdikey veya yatay yönde olup olmadığınıza bağlı olarak alt görünüm ekleme veya kaldırma gibi bir şey yapmak istiyorsanız geçersiz kılın. Aksi takdirde, yalnız bırakabilmelisiniz.


Cevabınızı görünümü (awakeFromNib olan) 's subView'ın çerçevesini değiştirmek için kullanın layoutSubViews, işe yaradı.
uçak

1

layoutSubviews , görünümün kendisine değil, çocuk görünümlerine çerçeve oluşturmak içindir.

Çünkü UIView, atanmış kurucu tipik olarak initWithFrame:(CGRect)frameve çerçeveyi orada (veya içinde initWithCoder:) ayarlamanız gerekir , muhtemelen çerçeve değerinde geçirilen yok sayılır. Ayrıca farklı bir kurucu sağlayabilir ve çerçeveyi orada ayarlayabilirsiniz.


daha fazla ayrıntı alabilir misin? Bir görünümün subView'in çerçevesini nasıl ayarlayacağınızı bilmiyordum. görünümüawakeFromNib
uçak

2016'ya hızlı bir şekilde ilerlediğinizde, muhtemelen hiç çerçeve ayarlamamalı ve otomatik yerleşim (kısıtlamalar) kullanmamalısınız. Görünüm XIB'den (veya film şeridinden) geliyorsa, alt görünüm zaten ayarlanmış olmalıdır.
proksi
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.