Правильная практика для подкласса UIView?

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

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

4 ответов


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

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

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

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

из документа NSNibAwaking

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

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

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

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

инициализации

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

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

  • layerClass реализуйте этот метод, только если вы хотите, чтобы ваше представление использовало другой слой анимации ядра для своего резервного хранилища. Например, если вы используете 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: реализуйте этот метод, если ваш вид обрабатывает события касания напрямую и может потребоваться предотвратить прикрепление распознаватели жестов от запуска дополнительных действий.


это по-прежнему высоко в 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
  }
}

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

если ваш класс в основном состоит из подвидов, правильное место для их выделения находится в init методы. Для просмотров есть два разных init методы, которые могут быть вызваны, в зависимости от того, создается ли ваше представление из кода или из nib/раскадровки. Что я делаю напиши мою собственную!--2--> метод, а затем вызвать его из обоих initWithFrame: и initWithCoder: методы.

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

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


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

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