it-swarm.com.ru

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

Я разработал свою собственную ячейку в IB, разделил ее на подклассы и соединил свои розетки с моим собственным классом. У меня есть три подпредставления в содержимом ячейки: UIView (cdView) и две метки (titleLabel и emailLabel). В зависимости от данных, доступных для каждой строки, иногда я хочу, чтобы UIView и две метки отображались в моей ячейке, а иногда только две метки. То, что я пытаюсь сделать, это установить ограничения таким образом, если я установлю свойство UIView скрытым или я удалю его из суперпредставления, две метки переместятся влево. Я пытался установить ведущее ограничение UIView на Superview (содержимое ячейки) для 10 пикселей и UILabels, приводя Ограничения на 10 пикселей к следующему представлению (UIView). Позже в моем коде 

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}

Я скрываю свой cell.cdView и хотел бы, чтобы метки двигались влево, но они остаются в той же позиции в Cell. Я пытался удалить cell.cdView из superview, но он тоже не работал. Я приложил изображение, чтобы уточнить, о чем я.

cell

Я знаю, как сделать это программно, и я не ищу это решение. Я хочу установить ограничения в IB, и я ожидаю, что мои подпредставления будут перемещаться динамически, если другие виды будут удалены или скрыты. Можно ли это сделать в IB с помощью авто-макета?

.....
289
Guferos

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

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

В вашем случае это скорее всего означает:

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

Что вам нужно сделать, так это разумно чрезмерно ограничить ваши ярлыки. Оставьте ваши существующие ограничения (10-тпс пространства для другого вида) в покое, но добавьте еще одно ограничение: отодвиньте левые края ваших меток на 10 пунктов от левого края их суперпредставления с необязательным приоритетом (высокий приоритет по умолчанию, вероятно, будет работать хорошо).

Затем, когда вы хотите, чтобы они переместились влево, полностью удалите левое представление. Обязательное ограничение 10pt для левого представления исчезнет вместе с представлением, к которому оно относится, и у вас останется только высокоприоритетное ограничение, чтобы метки находились на расстоянии 10 пунктов от их суперпредставления. На следующем проходе макета это должно заставить их расширяться влево до тех пор, пока они не заполнят ширину суперпредставления, но для вашего промежутка вокруг краев.

Одно важное предостережение: если вы хотите, чтобы ваш левый вид снова отображался на картинке, вы не только должны добавить его обратно в иерархию видов, но также должны восстановить все его ограничения одновременно. Это означает, что вам нужен способ поместить ограничение 10pt между представлением и его метками обратно всякий раз, когда это представление отображается снова.

339
Tim

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

Для вида, который вы хотите скрыть, установите ограничение ширины. Ограничьте другие взгляды с ведущим горизонтальным разрывом к этому представлению.

Чтобы скрыть, обновите .constant ограничения ширины до 0.f. Другие виды автоматически переместятся влево, чтобы занять позицию.

Смотрите мой другой ответ здесь для более подробной информации:

Как изменить ограничения метки во время выполнения?

194
Max MacLeod

Для тех, кто поддерживает только iOS 8+, есть новое логическое свойство active . Это поможет включить только необходимые ограничения динамически

Постскриптум Выход ограничения должен быть сильным, а не слабым

Пример:

@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
}

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

UIStackView (iOS 9+)
Еще один вариант - обернуть ваши взгляды в UIStackView. Когда вид скрыт, UIStackView автоматически обновит макет

74
Silmaril

UIStackView автоматически перемещает свои представления, когда свойство hidden изменяется в любом из его подпредставлений (iOS 9+). 

UIView.animateWithDuration(1.0) { () -> Void in
   self.mySubview.hidden = !self.mySubview.hidden
}

Перейти к 11:48 в этом видео WWDC для демонстрации:

Тайны Авто Макета, Часть 1

56
bonus

В моем проекте используется собственный подкласс @IBDesignableUILabel (для обеспечения согласованности цвета, шрифта, вставок и т.д.), И я реализовал что-то вроде следующего:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}

Это позволяет подклассу меток принимать участие в автоматической компоновке, но при скрытии не занимать места.

17
Robert Atkins

Для гуглеров: основываясь на ответе Макса, чтобы решить проблему заполнения, которую многие заметили, я просто увеличил высоту метки и использовал эту высоту в качестве разделителя вместо фактического заполнения. Эта идея может быть расширена для любого сценария с содержащими представлениями.

Вот простой пример:

IB Screenshot

В этом случае я отображаю высоту метки Author на соответствующую IBOutlet:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

и когда я устанавливаю высоту ограничения на 0.0f, мы сохраняем «отступ», потому что высота кнопки Play позволяет это сделать.

13
jterry

Я закончил тем, что создал 2 xibs. Один с левым обзором и один без него. Я зарегистрировал оба в контроллере и затем решил, что использовать во время cellForRowAtIndexPath.

Они используют один и тот же класс UITableViewCell. Недостатком является то, что между XIB есть некоторое дублирование контента, но эти ячейки довольно просты. Плюс в том, что у меня нет кучки кода для ручного управления удалением вида, обновлением ограничений и т.д.

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

[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];
8
Michael Peterson

связать ограничение между пользовательским интерфейсом и метками как IBOutlet и установить для элемента приоритета меньшее значение, если установлено скрытое = ДА

8
n0c

В этом случае я сопоставляю высоту метки Author с соответствующим IBOutlet:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

и когда я устанавливаю высоту ограничения равной 0.0f, мы сохраняем «отступ», потому что высота кнопки воспроизведения позволяет это сделать.

cell.authorLabelHeight.constant = 0;

enter image description here enter image description here

6
Mitul Marsoniya

Используйте два UIStackView по горизонтали и вертикали, когда какое-то подпредставление в стеке скрыто, другие подпредставления стека будут перемещены, используйте Распределение -> Заполнить пропорционально для вертикального стека с двумя UILabel и нуждаются в заданных значениях ширины и высоты для первого UIView  enter image description here

5
Roman Solodyashkin

Попробуйте это, я реализовал ниже код,

У меня есть один вид на ViewController, в котором добавлены другие три вида, когда любой вид скрыт, другие два вида переместятся, выполните следующие шаги ., 

1.ViewController.h File 

#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

2.ViewController.m

 #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

 enter image description here  enter image description here  enter image description here

Надеюсь, что эта логика поможет кому-то.

2
Jaywant Khedkar

В моем случае я установил константу ограничения height на 0.0f, а также установил для свойства hidden значение YES.

Чтобы снова показать представление (с подпредставлениями), я сделал наоборот: я установил постоянную высоты в ненулевое значение и установил для свойства hidden значение NO.

1
testing

Это мое другое решение, использующее ограничение приоритета. Идея установить ширину 0.

  1. создать вид контейнера (оранжевый) и установить ширину .  enter image description here

  2. создайте представление контента (красный) и установите конечное пространство 10pt в superview (оранжевый). Обратите внимание на концевые пробелы, есть 2 концевых ограничения с разным приоритетом. Низкий (= 10) и Высокий (<= 10). Это важно, чтобы избежать двусмысленности .  enter image description here

  3. Установите оранжевую ширину вида на 0, чтобы скрыть вид .  enter image description here

1
mnemonic23

Как и предположил no_scene, вы можете сделать это, изменив приоритет ограничения во время выполнения. Это было намного проще для меня, потому что у меня было более одного вида блокировки, который нужно было удалить.

Вот фрагмент с использованием ReactiveCocoa:

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];
        }];
    }];
0
skensell

Мне кажется, это самый простой ответ, пожалуйста, проверьте его, так как он работает хорошо

        StackFullView.layer.isHidden = true
        Task_TopSpaceSections.constant = 0.   //your constrain of top view

проверьте здесь https://www.youtube.com/watch?v=EBulMWMoFuw

0
Gowthaman M

Вот как я бы выровнял свои uiviews, чтобы получить ваше решение:

  1. Перетащите один UIImageView и поместите его влево.
  2. Перетащите один UIView и поместите его справа от UIImageView. 
  3. Перетащите две метки UILabels внутри того UIView, у которого начальные и конечные ограничения равны нулю.
  4. Установите ведущее ограничение UIView, содержащее 2 метки, в superview вместо UIImagView.
  5. Если UIImageView скрыт, установите ведущую константу ограничения в 10 пикселей для суперпредставления. В противном случае установите ведущую постоянную ограничения равной 10 пикселей + UIImageView.width + 10 пикселей.

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

0
Deepak Thakur

На случай, если это кому-то поможет, я создал вспомогательный класс для использования визуального формата ограничений. Я использую это в моем текущем приложении.

AutolayoutHelper

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

Я должен поблагодарить Тима за его ответ выше , этот ответ об UIScrollView , а также этот учебник .

0
Ferran Maylinch

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

0
Hogdotmac

Это старый вопрос, но все же я надеюсь, что это поможет. Исходя из Android, на этой платформе у вас есть удобный метод isVisible, чтобы скрыть его от вида, но также не учитывать кадр, когда автопутешествие рисует вид.

используя extension и "extension" uiview, вы можете сделать аналогичную функцию в ios (не уверен, почему ее нет в UIKit), здесь реализация в Swift 3:

    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()
    }
0
Vincent Choubard

Я буду использовать горизонтальное представление стека. Он может удалить кадр, когда подпредставление скрыто. 

На изображении ниже красный вид является фактическим контейнером для вашего контента и имеет конечный интервал 10pt к оранжевому суперпредставлению (ShowHideView), затем просто подключите ShowHideView к IBOutlet и покажите/скрыть/удалить его программно.

  1. Это когда вид виден/установлен.

 view is visible

  1. Это когда вид скрыт/не установлен.

 view is hidden/removed

0
mnemonic23