it-swarm.com.ru

Надлежащая практика для создания подклассов UIView?

Я работаю над некоторыми пользовательскими элементами управления вводом на основе UIView и пытаюсь определить правильную практику настройки представления. При работе с UIViewController довольно просто использовать loadView и связанные с ним методы viewWill, viewDid, но при создании подкласса UIView самыми близкими мне методами являются `awakeFromNib, drawRect и layoutSubviews. (Я думаю о терминах обратного вызова настройки и разрыва.) В моем случае я настраиваю свой фрейм и внутренние представления в layoutSubviews, но на экране ничего не вижу.

Каков наилучший способ обеспечить правильную высоту и ширину моего вида? (Мой вопрос применим независимо от того, использую ли я autolayout, хотя может быть два ответа.) Какова надлежащая "лучшая практика"?

151
Moshe

Apple довольно четко определила, как создавать подкласс UIView в документе.

Проверьте список ниже, особенно взгляните на initWithFrame: и layoutSubviews. Первый предназначен для настройки фрейма вашего UIView, а второй предназначен для настройки фрейма и макета его подпредставлений.

Также помните, что initWithFrame: вызывается, только если вы создаете экземпляр своего UIView программно. Если вы загружаете его из файла пера (или раскадровки), будет использоваться initWithCoder:. А в initWithCoder: кадр еще не рассчитан, поэтому вы не можете изменить кадр, настроенный в Интерфейсном Разработчике. Как и предлагалось в этом ответе вы можете подумать о вызове initWithFrame: из initWithCoder: для настройки фрейма.

Наконец, если вы загружаете свое UIView из пера (или раскадровки), у вас также есть возможность awakeFromNib для выполнения пользовательских инициализаций фрейма и макета, поскольку при вызове awakeFromNib гарантируется, что каждое представление в иерархии было разархивировано и инициализировано.

Из документа NSNibAwaking (теперь заменено документом awakeFromNib ):

Сообщения другим объектам можно безопасно отправлять из awakeFromNib - к этому времени гарантируется, что все объекты разархивированы и инициализированы (хотя, конечно, не обязательно пробуждены)

Стоит также отметить, что с autolayout вы не должны явно устанавливать рамку своего представления. Вместо этого вы должны указать набор достаточных ограничений, чтобы фрейм автоматически вычислял кадр.

Прямо из документация :

Методы для переопределения

Инициализация

  • initWithFrame: Рекомендуется реализовать этот метод. Вы также можете реализовать собственные методы инициализации в дополнение или вместо этого метода.

  • initWithCoder: Реализуйте этот метод, если вы загружаете представление из файла пера Interface Builder и ваше представление требует пользовательской инициализации.

  • layerClass Реализуйте этот метод, только если вы хотите, чтобы ваше представление использовало другой слой Core Animation для своего резервного хранилища. Например, если вы используете OpenGL ES для рисования, вам нужно переопределить этот метод и вернуть класс CAEAGLLayer.

Рисование и печать

  • drawRect: Реализуйте этот метод, если ваше представление рисует пользовательский контент. Если ваш вид не выполняет никаких пользовательских рисунков, избегайте переопределения этого метода.

  • drawRect:forViewPrintFormatter: Реализуйте этот метод, только если вы хотите по-разному отображать содержимое вашего представления во время печати.

Ограничения

  • requiresConstraintBasedLayout Реализуйте этот метод класса, если вашему классу представления требуются ограничения для правильной работы.

  • updateConstraints Реализуйте этот метод, если вашему представлению необходимо создать пользовательские ограничения между вашими подпредставлениями.

  • alignmentRectForFrame:, frameForAlignmentRect: Реализуйте эти методы, чтобы переопределить соответствие ваших представлений другие взгляды.

Раскладка

  • sizeThatFits: Реализуйте этот метод, если вы хотите, чтобы у вашего представления был размер по умолчанию, отличный от того, который обычно был бы при операциях изменения размера. Например, вы можете использовать этот метод, чтобы предотвратить сужение вашего вида до точки, где подпредставления не могут отображаться правильно.

  • layoutSubviews Реализуйте этот метод, если вам требуется более точный контроль над компоновкой ваших подпредставлений, чем обеспечивают ограничения или режимы автоматического изменения размера.

  • didAddSubview:, willRemoveSubview: Реализуйте эти методы по мере необходимости, чтобы отслеживать добавления и удаления подпредставлений.

  • willMoveToSuperview:, didMoveToSuperview Реализуйте эти методы по мере необходимости, чтобы отслеживать движение текущий вид в вашей иерархии представлений.

  • willMoveToWindow:, didMoveToWindow Реализуйте эти методы по мере необходимости, чтобы отслеживать движение вашего посмотреть в другое окно.

Обработка событий:

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent: Реализуйте эти методы, если вам нужно напрямую обрабатывать события касания. (Для ввода на основе жестов используйте распознаватели жестов.)

  • gestureRecognizerShouldBegin: Реализуйте этот метод, если ваше представление напрямую обрабатывает события касания и может потребовать, чтобы подключенные распознаватели жестов не запускали дополнительные действия.

286
Gabriele Petronella

Это все еще высоко в Google. Ниже приведен обновленный пример для Swift.

Функция didLoad позволяет вам поместить весь ваш пользовательский код инициализации. Как уже упоминали другие, didLoad будет вызываться, когда представление создается программно с помощью init(frame:) или когда десериализатор XIB объединяет шаблон XIB с вашим представлением через init(coder:)

в сторону: layoutSubviews и updateConstraints вызываются несколько раз для большинства представлений. Это предназначено для расширенных многопроходных макетов и корректировок при изменении границ вида. Лично я избегаю многопроходных схем, когда это возможно, потому что они сжигают циклы процессора и делают все головной болью. Кроме того, я помещаю код ограничения в сами инициализаторы, так как редко делаю их недействительными.

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

В Apple документация есть приличное резюме, и это хорошо описано в бесплатном Стэнфордский курс , доступном в iTunes. Я представляю свою версию TL; DR здесь:

Если ваш класс в основном состоит из подпредставлений, правильное место для их размещения - методы init. Для представлений есть два разных метода init, которые могут вызываться в зависимости от того, создается ли ваше представление из кода или из nib/storyboard. Я пишу свой собственный метод setup, а затем вызываю его из методов initWithFrame: и initWithCoder:.

Если вы делаете пользовательский рисунок, вы действительно хотите переопределить drawRect: в своем представлении. Если ваш пользовательский вид в основном является контейнером для подпредставлений, вам, вероятно, не понадобится это делать.

Переопределять layoutSubViews можно только в том случае, если вы хотите сделать что-то вроде добавления или удаления подпредставления в зависимости от того, находитесь ли вы в книжной или альбомной ориентации. В противном случае вы сможете оставить это в покое.

14
dpassage

layoutSubviews предназначен для установки фрейма в дочерних представлениях, а не в самом представлении.

Для UIView назначенным конструктором обычно является initWithFrame:(CGRect)frame, и вы должны установить фрейм там (или в initWithCoder:), возможно, игнорируя переданное значение фрейма. Вы также можете предоставить другой конструктор и установить фрейм там.

1
proxi