как использовать автоматический макет для перемещения других видов, когда вид скрыт?

<p>я разработал свою собственную ячейку в ib, разделил ее на подклассы и соединил свои розетки с моим собственным классом. у меня есть три подпредставления в содержимом ячейки: uiview (cdview) и две метки (titlelabel и emaillabel). в зависимости от данных, доступных для каждой строки, иногда я хочу, чтобы uiview и две метки отображались в моей ячейке, а иногда только две метки. то, что я пытаюсь сделать, это установить ограничения таким образом, если я установлю свойство uiview скрытым или я удалю его из суперпредставления, две метки переместятся влево. я пытался установить ведущее ограничение uiview на superview (содержимое ячейки) для 10 пикселей и uilabels, приводя ограничения на 10 пикселей к следующему представлению (uiview). позже в моем коде </p><pre><code>-(uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(indexpath *)indexpath { ... record *record = [self.records objectatindex:indexpath.row]; if ([record.imageurl is equaltostring:@""]) { cell.cdview.hidden = yes; } </code></pre><p>я скрываю свой cell.cdview и хотел бы, чтобы метки двигались влево, но они остаются в той же позиции в cell. я пытался удалить cell.cdview из superview, но он тоже не работал. я приложил изображение, чтобы уточнить, о чем я.</p><p><img src="https://s21.postimg.cc/m7c5bss4n/cells.png" alt="cell"></p><p>я знаю, как сделать это программно, и я не ищу это решение. я хочу установить ограничения в ib, и я ожидаю, что мои подпредставления будут перемещаться динамически, если другие виды будут удалены или скрыты. можно ли это сделать в ib с помощью авто-макета?</p><pre><code>..... </code></pre>

<p>это возможно, но вам придется проделать небольшую дополнительную работу. есть пара концептуальных вещей, которые нужно сначала убрать с пути:</p><ul> <li>скрытые виды, даже если они не рисуют,<strong> по-прежнему участвуют </strong>в автоматической компоновке и обычно<strong> сохраняют свои рамки </strong>, оставляя другие связанные виды на своих местах.</li> <li>при удалении представления из его суперпредставления<strong> все связанные ограничения </strong>также удаляются из этой иерархии представлений.</li> </ul><p>в вашем случае это скорее всего означает:</p><ul> <li>если вы установите скрытый вид слева, надписи останутся на месте, поскольку этот вид слева все еще занимает место (даже если он не виден).</li> <li>если вы удалите свое левое представление, ваши метки, вероятно, останутся неоднозначно ограниченными, поскольку у вас больше не будет ограничений для левых краев ваших меток.</li> </ul><p>что вам нужно сделать, так это разумно<strong> чрезмерно ограничить </strong>ваши ярлыки. оставьте ваши существующие ограничения (10-тпс пространства для другого вида) в покое, но добавьте еще одно ограничение: отодвиньте левые края ваших меток на 10 пунктов от левого края их суперпредставления с необязательным приоритетом (высокий приоритет по умолчанию, вероятно, будет работать хорошо).</p><p>затем, когда вы хотите, чтобы они переместились влево, полностью удалите левое представление. обязательное ограничение 10pt для левого представления исчезнет вместе с представлением, к которому оно относится, и у вас останется только высокоприоритетное ограничение, чтобы метки находились на расстоянии 10 пунктов от их суперпредставления. на следующем проходе макета это должно заставить их расширяться влево до тех пор, пока они не заполнят ширину суперпредставления, но для вашего промежутка вокруг краев.</p><p>одно важное предостережение: если вы хотите, чтобы ваш левый вид снова отображался на картинке, вы не только должны добавить его обратно в иерархию видов, но также должны<strong> восстановить все его ограничения </strong>одновременно. это означает, что вам нужен способ поместить ограничение 10pt между представлением и его метками обратно всякий раз, когда это представление отображается снова.</p>

<p>добавление или удаление ограничений во время выполнения - тяжелая операция, которая может повлиять на производительность. однако есть более простая альтернатива.</p><p>для вида, который вы хотите скрыть, установите ограничение ширины. ограничьте другие взгляды с ведущим горизонтальным разрывом к этому представлению.</p><p>чтобы скрыть, обновите <code>.constant</code> ограничения ширины до 0.f. другие виды автоматически переместятся влево, чтобы занять позицию.</p><p>смотрите мой другой ответ здесь для более подробной информации:</p><p> <a href="https://stackoverflow.com/questions/19707229/how-to-change-label-constraints-during-runtime/19707579#19707579">как изменить ограничения метки во время выполнения?</a> </p>

<p>для тех, кто поддерживает только <strong>ios 8+</strong>, есть новое логическое свойство <a href="https://developer.apple.com/library/ios/documentation/appkit/reference/nslayoutconstraint\_class/#//apple\_ref/occ/instp/nslayoutconstraint/active">active</a> . это поможет включить только необходимые ограничения динамически</p><p>постскриптум выход ограничения должен быть <strong>сильным</strong>, а не слабым <br><br> <strong>пример:</strong></p><pre><code>@iboutlet weak var optionalview: uiview! @iboutlet var viewisvisibleconstraint: nslayoutconstraint! @iboutlet var viewishiddenconstraint: nslayoutconstraint! func showview() { optionalview.ishidden = false viewisvisibleconstraint.isactive = true viewishiddenconstraint.isactive = false } func hideview() { optionalview.ishidden = true viewisvisibleconstraint.isactive = false viewishiddenconstraint.isactive = true } </code></pre><p>также, чтобы исправить ошибку в раскадровке, вам нужно снять флажок <code>installed</code> для одного из этих ограничений . <br><br> <strong>uistackview <em>(ios 9+)</em></strong> <br> еще один вариант - обернуть ваши взгляды в <code>uistackview</code>. когда вид скрыт, <code>uistackview</code> автоматически обновит макет</p>

<p><code>uistackview</code> автоматически перемещает свои представления, когда свойство <code>hidden</code> изменяется в любом из его подпредставлений (ios 9+). </p><pre><code>uiview.animatewithduration(1.0) { () -> void in self.mysubview.hidden = !self.mysubview.hidden } </code></pre><p>перейти к <a href="https://developer.apple.com/videos/play/wwdc2015/218/?time=708">11:48</a> в этом видео wwdc для демонстрации:</p><p> <a href="https://developer.apple.com/videos/play/wwdc2015/218/?time=708">тайны авто макета, часть 1</a> </p>

<p>в моем проекте используется собственный подкласс <code>@ibdesignable</code> <code>uilabel</code> (для обеспечения согласованности цвета, шрифта, вставок и т.д.), и я реализовал что-то вроде следующего:</p><pre><code>override func intrinsiccontentsize() -> cgsize { if hidden { return cgsizezero } else { return super.intrinsiccontentsize() } } </code></pre><p>это позволяет подклассу меток принимать участие в автоматической компоновке, но при скрытии не занимать места.</p>

<p><em> для гуглеров: </em>основываясь на ответе макса, чтобы решить проблему заполнения, которую многие заметили, я просто увеличил высоту метки и использовал эту высоту в качестве разделителя вместо фактического заполнения. эта идея может быть расширена для любого сценария с содержащими представлениями.</p><p>вот простой пример:</p><p><img src="https://i.stack.imgur.com/rge8v.png" alt="ib screenshot"></p><p>в этом случае я отображаю высоту метки<em> author </em>на соответствующую <code>iboutlet</code>:</p><pre><code>@property (retain, nonatomic) iboutlet nslayoutconstraint* authorlabelheight; </code></pre><p>и когда я устанавливаю высоту ограничения на <code>0.0f</code>, мы сохраняем «отступ», потому что высота кнопки<em> play </em>позволяет это сделать.</p>

<p>я закончил тем, что создал 2 xibs. один с левым обзором и один без него. я зарегистрировал оба в контроллере и затем решил, что использовать во время cellforrowatindexpath.</p><p>они используют один и тот же класс uitableviewcell. недостатком является то, что между xib есть некоторое дублирование контента, но эти ячейки довольно просты. плюс в том, что у меня нет кучки кода для ручного управления удалением вида, обновлением ограничений и т.д.</p><p>в общем, это, вероятно, лучшее решение, поскольку они технически отличаются друг от друга и поэтому должны иметь разные xib.</p><pre><code>[self.table registernib:[uinib nibwithnibname:@"trackcell" bundle:nil] forcellreuseidentifier:@"trackcell"]; [self.table registernib:[uinib nibwithnibname:@"trackcellnoimage" bundle:nil] forcellreuseidentifier:@"trackcellnoimage"]; trackcell *cell = [tableview dequeuereusablecellwithidentifier:(appdelegate.showimages ? @"trackcell" : @"trackcellnoimage") forindexpath:indexpath]; </code></pre>

<p>связать ограничение между пользовательским интерфейсом и метками как iboutlet и установить для элемента приоритета меньшее значение, если установлено скрытое = да</p>

<p>в этом случае я сопоставляю высоту метки author с соответствующим iboutlet:</p><pre><code>@property (retain, nonatomic) iboutlet nslayoutconstraint* authorlabelheight; </code></pre><p>и когда я устанавливаю высоту ограничения равной 0.0f, мы сохраняем «отступ», потому что высота кнопки воспроизведения позволяет это сделать.</p><pre><code>cell.authorlabelheight.constant = 0; </code></pre><p> <a href="https://i.stack.imgur.com/reudc.png"><img src="https://i.stack.imgur.com/reudc.png" alt="enter image description here"> </a> <a href="https://i.stack.imgur.com/xsuqc.png"><img src="https://i.stack.imgur.com/xsuqc.png" alt="enter image description here"> </a> </p>

<p>используйте два uistackview по горизонтали и вертикали, когда какое-то подпредставление в стеке скрыто, другие подпредставления стека будут перемещены, используйте распределение -> заполнить пропорционально для вертикального стека с двумя uilabel и нуждаются в заданных значениях ширины и высоты для первого uiview <a href="https://i.stack.imgur.com/bbux9.png"> <img src="https://i.stack.imgur.com/bbux9.png" alt="enter image description here"> </a> </p>

<p>попробуйте это, я реализовал ниже код,</p><p>у меня есть один вид на <strong>viewcontroller</strong>, в котором добавлены другие три вида, когда любой вид скрыт, другие два вида переместятся, выполните следующие шаги . , </p><p><strong>1.viewcontroller.h file</strong> </p><pre><code>#import <uikit/uikit.h> @interface viewcontroller : uiviewcontroller @property (strong, nonatomic) iboutlet uiview *viewone; @property (strong, nonatomic) iboutlet uiview *viewtwo; @property (strong, nonatomic) iboutlet uiview *viewthree; @property (strong, nonatomic) iboutlet nslayoutconstraint *viewonewidth; @property (strong, nonatomic) iboutlet nslayoutconstraint *viewtwowidth; @property (strong, nonatomic) iboutlet nslayoutconstraint *viewthreewidth; @property (strong, nonatomic) iboutlet nslayoutconstraint *viewbottomwidth; @end </code></pre><p><strong>2.viewcontroller.m</strong></p><pre><code> #import "viewcontroller.h" @interface viewcontroller () { cgfloat viewonewidthconstant; cgfloat viewtwowidthconstant; cgfloat viewthreewidthconstant; cgfloat viewbottomwidthconstant; } @end @implementation viewcontroller @synthesize viewone, viewtwo, viewthree; - (void)viewdidload { [super viewdidload]; // do any additional setup after loading the view, typically from a nib. /* 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 */ // [viewone sethidden:no]; // [viewtwo sethidden:no]; // [viewthree sethidden:no]; // [viewone sethidden:no]; // [viewtwo sethidden:no]; // [viewthree sethidden:yes]; // [viewone sethidden:no]; // [viewtwo sethidden:yes]; // [viewthree sethidden:no]; // [viewone sethidden:no]; // [viewtwo sethidden:yes]; // [viewthree sethidden:yes]; // [viewone sethidden:yes]; // [viewtwo sethidden:no]; // [viewthree sethidden:no]; // [viewone sethidden:yes]; // [viewtwo sethidden:no]; // [viewthree sethidden:yes]; // [viewone sethidden:yes]; // [viewtwo sethidden:yes]; // [viewthree sethidden:no]; // [viewone sethidden:yes]; // [viewtwo sethidden:yes]; // [viewthree sethidden:yes]; [self hideshowbottombar]; } - (void)hideshowbottombar { bool isone = !viewone.ishidden; bool istwo = !viewtwo.ishidden; bool isthree = !viewthree.ishidden; viewonewidthconstant = \_viewonewidth.constant; viewtwowidthconstant = \_viewtwowidth.constant; viewthreewidthconstant = \_viewthreewidth.constant; viewbottomwidthconstant = \_viewbottomwidth.constant; if (isone && istwo && isthree) { // 0 0 0 \_viewonewidth.constant = viewbottomwidthconstant / 3; \_viewtwowidth.constant = viewbottomwidthconstant / 3; \_viewthreewidth.constant = viewbottomwidthconstant / 3; } else if (isone && istwo && !isthree) { // 0 0 1 \_viewonewidth.constant = viewbottomwidthconstant / 2; \_viewtwowidth.constant = viewbottomwidthconstant / 2; \_viewthreewidth.constant = 0; } else if (isone && !istwo && isthree) { // 0 1 0 \_viewonewidth.constant = viewbottomwidthconstant / 2; \_viewtwowidth.constant = 0; \_viewthreewidth.constant = viewbottomwidthconstant / 2; } else if (isone && !istwo && !isthree) { // 0 1 1 \_viewonewidth.constant = viewbottomwidthconstant; \_viewtwowidth.constant = 0; \_viewthreewidth.constant = 0; } else if (!isone && istwo && isthree) { // 1 0 0 \_viewonewidth.constant = 0; \_viewtwowidth.constant = viewbottomwidthconstant / 2; \_viewthreewidth.constant = viewbottomwidthconstant / 2; } else if (!isone && istwo && !isthree) { // 1 0 1 \_viewonewidth.constant = 0; \_viewtwowidth.constant = viewbottomwidthconstant; \_viewthreewidth.constant = 0; } else if (!isone && !istwo && isthree) { // 1 1 0 \_viewonewidth.constant = 0; \_viewtwowidth.constant = 0; \_viewthreewidth.constant = viewbottomwidthconstant; } else if (isone && istwo && isthree) { // 1 1 1 \_viewonewidth.constant = 0; \_viewtwowidth.constant = 0; \_viewthreewidth.constant = 0; } } - (void)didreceivememorywarning { [super didreceivememorywarning]; // dispose of any resources that can be recreated. } @end </code></pre><p> <a href="https://i.stack.imgur.com/s4tjv.png"> <img src="https://i.stack.imgur.com/s4tjv.png" alt="enter image description here"> </a> <a href="https://i.stack.imgur.com/w5rut.png"> <img src="https://i.stack.imgur.com/w5rut.png" alt="enter image description here"> </a> <a href="https://i.stack.imgur.com/uolln.png"> <img src="https://i.stack.imgur.com/uolln.png" alt="enter image description here"> </a> </p><p>надеюсь, что эта логика поможет кому-то.</p>

<p>в моем случае я установил константу ограничения<em> height </em>на <code>0.0f</code>, а также установил для свойства <code>hidden</code> значение <code>yes</code>.</p><p>чтобы снова показать представление (с подпредставлениями), я сделал наоборот: я установил постоянную высоты в ненулевое значение и установил для свойства <code>hidden</code> значение <code>no</code>.</p>

<p>это мое другое решение, использующее ограничение приоритета. идея установить ширину 0.</p><ol> <li><p>создать вид контейнера (оранжевый) и установить ширину . <a href="https://i.stack.imgur.com/yomdc.png"> <img src="https://i.stack.imgur.com/yomdc.png" alt="enter image description here"> </a> </p></li> <li><p>создайте представление контента (красный) и установите конечное пространство 10pt в superview (оранжевый). обратите внимание на концевые пробелы, есть 2 концевых ограничения с разным приоритетом. низкий (= 10) и высокий (<= 10). это важно, чтобы избежать двусмысленности . <a href="https://i.stack.imgur.com/7xrnc.png"> <img src="https://i.stack.imgur.com/7xrnc.png" alt="enter image description here"> </a> </p></li> <li><p>установите оранжевую ширину вида на 0, чтобы скрыть вид . <a href="https://i.stack.imgur.com/fffyi.png"> <img src="https://i.stack.imgur.com/fffyi.png" alt="enter image description here"> </a> </p></li> </ol>

<p>как и предположил no\_scene, вы можете сделать это, изменив приоритет ограничения во время выполнения. это было намного проще для меня, потому что у меня было более одного вида блокировки, который нужно было удалить.</p><p>вот фрагмент с использованием reactivecocoa:</p><pre><code>racsignal* isviewonehiddensignal = racobserve(self.viewmodel, isviewonehidden); racsignal* isviewtwohiddensignal = racobserve(self.viewmodel, isviewtwohidden); racsignal* isviewthreehiddensignal = racobserve(self.viewmodel, isviewthreehidden); rac(self.viewone, hidden) = isviewonehiddensignal; rac(self.viewtwo, hidden) = isviewtwohiddensignal; rac(self.viewthree, hidden) = isviewthreehiddensignal; rac(self.viewfourbottomconstraint, priority) = [[[[racsignal combinelatest:@[isviewonehiddensignal, isviewtwohiddensignal, isviewthreehiddensignal]] and] distinctuntilchanged] map:^id(nsnumber* allarehidden) { return [allarehidden boolvalue] ? @(780) : @(uilayoutprioritydefaulthigh); }]; racsignal* updateframessignal = [racobserve(self.viewfourbottomconstraint, priority) distinctuntilchanged]; [updateframessignal subscribenext:^(id x) { @strongify(self); [self.view setneedsupdateconstraints]; [uiview animatewithduration:0.3 animations:^{ [self.view layoutifneeded]; }]; }]; </code></pre>

<p>мне кажется, это самый простой ответ, пожалуйста, проверьте его, так как он работает хорошо</p><pre><code> stackfullview.layer.ishidden = true task\_topspacesections.constant = 0. //your constrain of top view </code></pre><p>проверьте здесь <a href="https://www.youtube.com/watch?v=ebulmwmofuw">https://www.youtube.com/watch?v=ebulmwmofuw</a> </p>

<p>вот как я бы выровнял свои uiviews, чтобы получить ваше решение:</p><ol> <li>перетащите один uiimageview и поместите его влево.</li> <li>перетащите один uiview и поместите его справа от uiimageview. </li> <li>перетащите две метки uilabels внутри того uiview, у которого начальные и конечные ограничения равны нулю.</li> <li>установите ведущее ограничение uiview, содержащее 2 метки, в superview вместо uiimagview.</li> <li>если uiimageview скрыт, установите ведущую константу ограничения в 10 пикселей для суперпредставления. в противном случае установите ведущую постоянную ограничения равной 10 пикселей + uiimageview.width + 10 пикселей.</li> </ol><p>я создал собственное правило большого пальца. всякий раз, когда вам нужно скрыть/показать какой-либо пользовательский интерфейс, на чьи ограничения могут повлиять, добавьте все затронутые/зависимые вложенные представления внутри пользовательского интерфейса и программно обновите его константу ограничения начала/конца/вершины/основания. </p>

<p>на случай, если это кому-то поможет, я создал вспомогательный класс для использования <a href="https://developer.apple.com/library/ios/documentation/userexperience/conceptual/autolayoutpg/visualformatlanguage/visualformatlanguage.html">визуального формата</a> ограничений. я использую это в моем текущем приложении.</p><p> <a href="https://github.com/fmaylinch/autolayout-helper">autolayouthelper</a> </p><p>это может быть немного приспособлено к моим потребностям, но вы можете найти это полезным или захотите изменить его и создать своего собственного помощника.</p><p>я должен поблагодарить тима за его <a href="https://stackoverflow.com/a/18066138/1121497">ответ выше</a> , этот <a href="https://stackoverflow.com/a/16843937/1121497">ответ об uiscrollview</a> , а также этот <a href="http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/">учебник</a> .</p>

<p>правильный способ сделать это - отключить ограничения с помощью isactive = false. заметьте, однако, что деактивация ограничения удаляет и освобождает его, поэтому у вас должны быть сильные выходы для них.</p>

<p>это старый вопрос, но все же я надеюсь, что это поможет. исходя из android, на этой платформе у вас есть удобный метод <code>isvisible</code>, чтобы скрыть его от вида, но также не учитывать кадр, когда автопутешествие рисует вид.</p><p>используя extension и "extension" uiview, вы можете сделать аналогичную функцию в ios (не уверен, почему ее нет в uikit), здесь реализация в swift 3:</p><pre><code> func isvisible(\_ isvisible: bool) { self.ishidden = !isvisible self.translatesautoresizingmaskintoconstraints = isvisible if isvisible { //if visible we remove the hight constraint if let constraint = (self.constraints.filter{$0.firstattribute == .height}.first){ self.removeconstraint(constraint) } } else { //if not visible we add a constraint to force the view to have a hight set to 0 let height = nslayoutconstraint(item: self, attribute: .height, relatedby: .equal , toitem: nil, attribute: .notanattribute, multiplier: 0, constant: 0) self.addconstraint(height) } self.layoutifneeded() } </code></pre>

<p>я буду использовать горизонтальное представление стека. он может удалить кадр, когда подпредставление скрыто. </p><p>на изображении ниже красный вид является фактическим контейнером для вашего контента и имеет конечный интервал 10pt к оранжевому суперпредставлению (showhideview), затем просто подключите showhideview к iboutlet и покажите/скрыть/удалить его программно.</p><ol> <li>это когда вид виден/установлен.</li> </ol><p> <a href="https://i.stack.imgur.com/keuzz.png"> <img src="https://i.stack.imgur.com/keuzz.png" alt="view is visible"> </a> </p><ol start="2"> <li>это когда вид скрыт/не установлен.</li> </ol><p> <a href="https://i.stack.imgur.com/gfg4z.png"> <img src="https://i.stack.imgur.com/gfg4z.png" alt="view is hidden/removed"> </a> </p>