Prawidłowa praktyka podklasowania UIView?

Pracuję nad niestandardowymi kontrolkami wejściowymi opartymi na UIView i próbuję ustalić właściwą praktykę konfigurowania widoku. Podczas pracy z UIViewController, jest to dość proste w użyciu loadView i pokrewne viewWill, viewDid metody, ale przy podklasowaniu UIView, najbliższe methosdy jakie mam to `awakeFromNib, drawRect, i layoutSubviews. (Myślę o ustawieniach i rozmowach zwrotnych.) W moim przypadku konfiguruję ramkę i wewnętrzne widoki w layoutSubviews, ale nic nie widzę na ekranie.

Jaki jest najlepszy sposób, aby upewnić się, że mój widok ma odpowiednią wysokość i szerokość, jaką chcę, aby miał? (Moje pytanie dotyczy niezależnie od tego, czy używam autolayout, chociaż mogą być dwie odpowiedzi. Jaka jest właściwa "najlepsza praktyka"?

Author: Andrew Barber, 2013-04-12

4 answers

Apple jasno zdefiniowało, jak podklasować UIView w dokumencie.

Zapoznaj się z poniższą listą, szczególnie przyjrzyj się initWithFrame: i layoutSubviews. Pierwszy z nich jest przeznaczony do Ustawienia ramki UIView, podczas gdy drugi jest przeznaczony do Ustawienia ramki i układu jej podwidów.

Pamiętaj również, że initWithFrame: jest wywoływana tylko wtedy, gdy tworzysz swoją UIView programowo. Jeśli ładujesz go z pliku nib (lub storyboardu), zostanie użyty initWithCoder:. I w initWithCoder: ramce nie został jeszcze obliczony, więc nie można modyfikować ramki skonfigurowanej w Kreatorze interfejsów. Zgodnie z sugestią w tej odpowiedzi {[42] } możesz pomyśleć o wywołaniu initWithFrame: z initWithCoder: w celu skonfigurowania ramki.

Wreszcie, jeśli załadujesz swój UIView z stalówki( lub storyboardu), masz również awakeFromNib możliwość wykonywania niestandardowych inicjalizacji ramek i układów, od kiedy wywoływane jest awakeFromNib, masz gwarancję, że każdy widok w hierarchii został niezarejestrowany i zainicjowany.

Od doc z NSNibAwaking

Wiadomości do innych obiektów mogą być wysyłane bezpiecznie z wewnątrz przebudzonego znib-w tym czasie jest pewne, że wszystkie obiekty są niezarchiwizowane i zainicjalizowane (choć niekoniecznie przebudzone, oczywiście) [37]}

Warto również zauważyć, że przy autolayout nie należy wyraźnie ustawiać ramki widoku. Zamiast tego należy określić zestaw wystarczających ograniczeń, tak aby ramka była automatycznie obliczana przez układ silnik.

Prosto z dokumentacji :

Metody nadpisywania

Inicjalizacja

  • initWithFrame: zaleca się wdrożenie tej metody. Możesz także zaimplementować niestandardowe metody inicjalizacji oprócz, lub zamiast tej metody.

  • initWithCoder: zaimplementuj tę metodę, jeśli wczytasz widok z pliku NIB Buildera interfejsu i twój widok wymaga niestandardowego inicjalizacja.

  • layerClass zaimplementuj tę metodę tylko wtedy, gdy chcesz, aby Widok używał innej warstwy animacji rdzenia w magazynie kopii zapasowej. Na przykład, jeśli używasz OpenGL ES do rysowania, chciałbyś nadpisuje tę metodę i zwraca klasę CAEAGLLayer.

Rysunek i druk

  • drawRect: zaimplementuj tę metodę, jeśli Widok rysuje zawartość niestandardową. Jeśli twój widok nie robi żadnych Niestandardowy rysunek, unikaj nadpisywania tego metoda.

  • drawRect:forViewPrintFormatter: zaimplementuj tę metodę tylko wtedy, gdy chcesz inaczej narysować zawartość widoku podczas drukowania.

  • requiresConstraintBasedLayout zaimplementuj tę metodę klasy, jeśli twoja klasa view wymaga ograniczeń do poprawnego działania.

  • updateConstraints zaimplementuj tę metodę, jeśli twój widok musi tworzyć własne ograniczenia między podglądy.

  • alignmentRectForFrame:, frameForAlignmentRect: zaimplementuj te metody, aby nadpisać sposób wyrównywania widoków do innych widoków.

Układ

  • sizeThatFits: zaimplementuj tę metodę, jeśli chcesz, aby Widok miał inny domyślny rozmiar niż zwykle podczas zmiany rozmiaru szef. Możesz na przykład użyć tej metody, aby zapobiec widok od kurczenia się do punktu, w którym nie można wyświetlić podglądu prawidłowo.

  • layoutSubviews zaimplementuj tę metodę, jeśli potrzebujesz bardziej precyzyjnej kontroli nad układem podwidywań niż ograniczenie lub zachowania autoresizujące zapewniają.

  • didAddSubview:, willRemoveSubview: zaimplementuj te metody w razie potrzeby, aby śledzić dodawanie i usuwanie podwidywań.

  • willMoveToSuperview:, didMoveToSuperview wdrożyć te metody w razie potrzeby, aby śledzić ruch bieżącego widoku w twój widok hierarchia.

  • willMoveToWindow:, didMoveToWindow zaimplementuj te metody w razie potrzeby, aby śledzić ruch widoku do innego okna.

Obsługa Zdarzeń:

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent: wdrożenie te metody, jeśli chcesz obsługiwać zdarzenia dotykowe bezpośrednio. (Dla wprowadzanie oparte na gestach, użyj rozpoznawania gestów.)

  • gestureRecognizerShouldBegin: Zaimplementuj tę metodę, jeśli Widok obsługuje zdarzenia dotykowe bezpośrednio i może chcieć zapobiec dołączaniu rozpoznawanie gestów z wyzwalania dodatkowych działań.

 271
Author: Gabriele Petronella,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:09

To wciąż jest wysoko w Google. Poniżej znajduje się zaktualizowany przykład swift.

Funkcja didLoad pozwala umieścić cały własny kod inicjalizacji. Jak wspomnieli inni, didLoad zostanie wywołany, gdy Widok zostanie utworzony programowo przez init(frame:) lub gdy XIB deserializer połączy szablon XIB z Twoim widokiem przez init(coder:)

na bok: layoutSubviews i updateConstraints są wywoływane wielokrotnie dla większości poglądów. Przeznaczony jest do zaawansowane układy wieloprzebiegowe i korekty, gdy zmieniają się granice widoku. Osobiście unikam układów wieloprzebiegowych, gdy jest to możliwe, ponieważ spalają cykle procesora i przyprawiają o ból głowy. Dodatkowo umieszczam Kod ograniczenia w samych inicjalizatorach, ponieważ rzadko je unieważniam.

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
  }
}
 33
Author: seo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-07 19:04:24

Jest przyzwoite podsumowanie w dokumentacji Apple , a to jest dobrze omówione w bezpłatnym kursie Stanforda dostępnym na iTunes. Prezentuję tu moją wersję TL; DR:

Jeśli twoja klasa składa się głównie z podwidywań, właściwym miejscem do ich przydzielenia są metody init. W przypadku widoków istnieją dwie różne metody init, które mogą zostać wywołane, w zależności od tego, czy widok jest inicjowany z kodu lub z nib / storyboard. Co robię, to piszę własną metodę setup, a następnie wywołaj go z metod initWithFrame: i initWithCoder:.

Jeśli robisz niestandardowe rysowanie, rzeczywiście chcesz nadpisać drawRect: w Twoim widoku. Jeśli jednak twój widok niestandardowy jest głównie kontenerem dla podwidywań, prawdopodobnie nie będziesz musiał tego robić.

Zastąp tylko layoutSubViews, Jeśli chcesz dodać lub usunąć podgląd podrzędny w zależności od orientacji pionowej lub poziomej. W przeciwnym razie, powinieneś być w stanie zostawić to w spokoju.

 14
Author: dpassage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-12 18:59:50

layoutSubviews służy do ustawiania ramek na widokach potomnych, a nie na samym widoku.

Dla UIView, oznaczonym konstruktorem jest typowo initWithFrame:(CGRect)frame i należy ustawić tam ramkę (lub w initWithCoder:), ewentualnie ignorując przekazaną w ramce wartość. Możesz również podać inny konstruktor i ustawić tam ramkę.

 1
Author: proxi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-12 18:58:12