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

<p>я работаю над некоторыми пользовательскими элементами управления вводом на основе uiview и пытаюсь определить правильную практику настройки представления. при работе с uiviewcontroller довольно просто использовать <code>loadview</code> и связанные с ним методы <code>viewwill</code>, <code>viewdid</code>, но при создании подкласса uiview самыми близкими мне методами являются <code>`awakefromnib</code>, <code>drawrect</code> и <code>layoutsubviews</code>. (я думаю о терминах обратного вызова настройки и разрыва.) в моем случае я настраиваю свой фрейм и внутренние представления в <code>layoutsubviews</code>, но на экране ничего не вижу.</p><p>каков наилучший способ обеспечить правильную высоту и ширину моего вида? (мой вопрос применим независимо от того, использую ли я autolayout, хотя может быть два ответа.) какова надлежащая "лучшая практика"?</p>

<p>apple довольно четко определила, как создавать подкласс <code>uiview</code> в документе.</p><p>проверьте список ниже, особенно взгляните на <code>initwithframe:</code> и <code>layoutsubviews</code>. первый предназначен для настройки фрейма вашего <code>uiview</code>, а второй предназначен для настройки фрейма и макета его подпредставлений.</p><p>также помните, что <code>initwithframe:</code> вызывается, только если вы создаете экземпляр своего <code>uiview</code> программно. если вы загружаете его из файла пера (или раскадровки), будет использоваться <code>initwithcoder:</code>. а в <code>initwithcoder:</code> кадр еще не рассчитан, поэтому вы не можете изменить кадр, настроенный в интерфейсном разработчике. как и предлагалось <a href="https://stackoverflow.com/questions/3352042/loading-a-uiview-subclass-from-nib-size-issues">в этом ответе</a> вы можете подумать о вызове <code>initwithframe:</code> из <code>initwithcoder:</code> для настройки фрейма.</p><p>наконец, если вы загружаете свое <code>uiview</code> из пера (или раскадровки), у вас также есть возможность <code>awakefromnib</code> для выполнения пользовательских инициализаций фрейма и макета, поскольку при вызове <code>awakefromnib</code> гарантируется, что каждое представление в иерархии было разархивировано и инициализировано.</p><p>из документа <a href="https://web.archive.org/web/20140812095209/https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/protocols/nsnibawaking\_protocol/reference/reference.html"><code>nsnibawaking</code></a> (теперь заменено документом <a href="https://developer.apple.com/documentation/objectivec/nsobject/1402907-awakefromnib?language=objc"><code>awakefromnib</code></a> ):</p><blockquote> <p>сообщения другим объектам можно безопасно отправлять из awakefromnib - к этому времени гарантируется, что все объекты разархивированы и инициализированы (хотя, конечно, не обязательно пробуждены)</p> </blockquote><p>стоит также отметить, что с autolayout вы не должны явно устанавливать рамку своего представления. вместо этого вы должны указать набор достаточных ограничений, чтобы фрейм автоматически вычислял кадр.</p><p>прямо из <a href="https://developer.apple.com/documentation/uikit/uiview?language=objc">документация</a> :</p><blockquote> <h2>методы для переопределения</h2> <h3>инициализация</h3> <ul> <li> <p><strong><code>initwithframe:</code></strong> рекомендуется реализовать этот метод. вы также можете реализовать собственные методы инициализации в дополнение или вместо этого метода.</p> </li> <li> <p><strong><code>initwithcoder:</code></strong> реализуйте этот метод, если вы загружаете представление из файла пера interface builder и ваше представление требует пользовательской инициализации.</p> </li> <li> <p><strong><code>layerclass</code></strong> реализуйте этот метод, только если вы хотите, чтобы ваше представление использовало другой слой core animation для своего резервного хранилища. например, если вы используете opengl es для рисования, вам нужно переопределить этот метод и вернуть класс caeagllayer.</p> </li> </ul> <h3>рисование и печать</h3> <ul> <li> <p><strong><code>drawrect:</code></strong> реализуйте этот метод, если ваше представление рисует пользовательский контент. если ваш вид не выполняет никаких пользовательских рисунков, избегайте переопределения этого метода.</p> </li> <li> <p><strong><code>drawrect:forviewprintformatter:</code></strong> реализуйте этот метод, только если вы хотите по-разному отображать содержимое вашего представления во время печати.</p> </li> </ul> <h3>ограничения</h3> <ul> <li> <p><strong><code>requiresconstraintbasedlayout</code></strong> реализуйте этот метод класса, если вашему классу представления требуются ограничения для правильной работы.</p> </li> <li> <p><strong><code>updateconstraints</code></strong> реализуйте этот метод, если вашему представлению необходимо создать пользовательские ограничения между вашими подпредставлениями.</p> </li> <li> <p><strong><code>alignmentrectforframe:</code></strong>, <strong><code>frameforalignmentrect:</code></strong> реализуйте эти методы, чтобы переопределить соответствие ваших представлений другие взгляды.</p> </li> </ul> <h3>раскладка</h3> <ul> <li> <p><strong><code>sizethatfits:</code></strong> реализуйте этот метод, если вы хотите, чтобы у вашего представления был размер по умолчанию, отличный от того, который обычно был бы при операциях изменения размера. например, вы можете использовать этот метод, чтобы предотвратить сужение вашего вида до точки, где подпредставления не могут отображаться правильно.</p> </li> <li> <p><strong><code>layoutsubviews</code></strong> реализуйте этот метод, если вам требуется более точный контроль над компоновкой ваших подпредставлений, чем обеспечивают ограничения или режимы автоматического изменения размера.</p> </li> <li> <p><strong><code>didaddsubview:</code></strong>, <strong><code>willremovesubview:</code></strong> реализуйте эти методы по мере необходимости, чтобы отслеживать добавления и удаления подпредставлений.</p> </li> <li> <p><strong><code>willmovetosuperview:</code></strong>, <strong><code>didmovetosuperview</code></strong> реализуйте эти методы по мере необходимости, чтобы отслеживать движение текущий вид в вашей иерархии представлений.</p> </li> <li> <p><strong><code>willmovetowindow:</code></strong>, <strong><code>didmovetowindow</code></strong> реализуйте эти методы по мере необходимости, чтобы отслеживать движение вашего посмотреть в другое окно.</p> </li> </ul> <h3>обработка событий:</h3> <ul> <li> <p><strong><code>touchesbegan:withevent:</code></strong>, <strong><code>touchesmoved:withevent:</code></strong>, <strong><code>touchesended:withevent:</code></strong>, <code>touchescancelled:withevent:</code> реализуйте эти методы, если вам нужно напрямую обрабатывать события касания. (для ввода на основе жестов используйте распознаватели жестов.)</p> </li> <li> <p><strong><code>gesturerecognizershouldbegin:</code></strong> реализуйте этот метод, если ваше представление напрямую обрабатывает события касания и может потребовать, чтобы подключенные распознаватели жестов не запускали дополнительные действия.</p> </li> </ul> </blockquote>

<p>это все еще высоко в google. ниже приведен обновленный пример для swift.</p><p>функция <code>didload</code> позволяет вам поместить весь ваш пользовательский код инициализации. как уже упоминали другие, <code>didload</code> будет вызываться, когда представление создается программно с помощью <code>init(frame:)</code> или когда десериализатор <em>xib</em> объединяет шаблон <em>xib</em> с вашим представлением через <code>init(coder:)</code></p><blockquote> <p><em>в сторону</em>: <code>layoutsubviews</code> и <code>updateconstraints</code> вызываются несколько раз для большинства представлений. это предназначено для расширенных многопроходных макетов и корректировок при изменении границ вида. лично я избегаю многопроходных схем, когда это возможно, потому что они сжигают циклы процессора и делают все головной болью. кроме того, я помещаю код ограничения в сами инициализаторы, так как редко делаю их недействительными.</p> </blockquote><pre><code>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 } } </code></pre>

<p>в apple <a href="http://developer.apple.com/library/ios/#documentation/windowsviews/conceptual/viewpg\_iphoneos/creatingviews/creatingviews.html#//apple\_ref/doc/uid/tp40009503-ch5-sw23">документация</a> есть приличное резюме, и это хорошо описано в бесплатном <a href="https://itunes.apple.com/us/course/coding-together-developing/id593208016">стэнфордский курс</a> , доступном в itunes. я представляю свою версию tl; dr здесь:</p><p>если ваш класс в основном состоит из подпредставлений, правильное место для их размещения - методы <code>init</code>. для представлений есть два разных метода <code>init</code>, которые могут вызываться в зависимости от того, создается ли ваше представление из кода или из nib/storyboard. я пишу свой собственный метод <code>setup</code>, а затем вызываю его из методов <code>initwithframe:</code> и <code>initwithcoder:</code>.</p><p>если вы делаете пользовательский рисунок, вы действительно хотите переопределить <code>drawrect:</code> в своем представлении. если ваш пользовательский вид в основном является контейнером для подпредставлений, вам, вероятно, не понадобится это делать.</p><p>переопределять <code>layoutsubviews</code> можно только в том случае, если вы хотите сделать что-то вроде добавления или удаления подпредставления в зависимости от того, находитесь ли вы в книжной или альбомной ориентации. в противном случае вы сможете оставить это в покое.</p>

<p><code>layoutsubviews</code> предназначен для установки фрейма в дочерних представлениях, а не в самом представлении.</p><p>для <code>uiview</code> назначенным конструктором обычно является <code>initwithframe:(cgrect)frame</code>, и вы должны установить фрейм там (или в <code>initwithcoder:</code>), возможно, игнорируя переданное значение фрейма. вы также можете предоставить другой конструктор и установить фрейм там.</p>