OS X Cocoa Auto Layout скрытые элементы - PullRequest
29 голосов
/ 28 октября 2011

Я пытаюсь использовать новую Автоматическую компоновку в Lion, потому что она кажется довольно приятной. Но я не могу найти хорошую информацию о том, как делать вещи. Например:

У меня есть две метки:

+----------------+
| +------------+ |
| + label 1    | |
| +------------+ |
|                |
| +------------+ |
| | label 2    | |
| +------------+ |
+----------------+

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

+----------------+
| +------------+ |
| + label 2    | |
| +------------+ |
|                |
|                |
|                |
|                |
+----------------+

Какие ограничения мне нужно добавить, чтобы он работал автоматически с autolayout? Я понимаю, что могу просто написать все, но у меня есть около 30 таких надписей, изображений и кнопок разных стилей и форм, которые являются необязательными, и я не хочу добавлять сотни строк кода, когда это может работать автоматически, довольно хорошо .

Если это не сработает, тогда я просто воспользуюсь WebView и сделаю это с HTML и CSS.

Ответы [ 8 ]

24 голосов
/ 17 августа 2012

Это возможно с автоматическим макетом, но не очень хорошо масштабируется.

Итак, на примере, скажем, у вас есть метка A и метка B (или кнопка или что-то еще на самом деле). Сначала начните с добавления верхнего ограничения к суперпредставлению для A. Затем ограничение по вертикали между A и B. Пока это все нормально. Если бы вы удалили A на этом этапе, B имел бы неоднозначную компоновку. Если бы вы скрывали это, оно все равно занимало бы его пространство, включая пространство между метками.

Затем вам нужно добавить еще одно ограничение из B в начало суперпредставления. Измените приоритет на этом, чтобы быть ниже чем другие (скажем, 900), а затем установите его константу как стандартную (или другое меньшее значение). Теперь, когда A удален из его суперпредставления, ограничение более низкого приоритета вступит в действие и потянет B к вершине. Ограничения выглядят примерно так:

Interface Builder screenshot

Проблема возникает, когда вы пытаетесь сделать это с длинным списком меток.

10 голосов
/ 21 марта 2014

Свертывающий подкласс UILabel

Одним из простых решений является создание подкласса UILabel и изменение размера внутреннего содержимого.

@implementation WBSCollapsingLabel

- (CGSize)intrinsicContentSize
{
    if (self.isHidden) {
        return CGSizeMake(UIViewNoIntrinsicMetric, 0.0f);
    } else {
        return [super intrinsicContentSize];
    }
}

- (void)setHidden:(BOOL)hidden
{
    [super setHidden:hidden];

    [self updateConstraintsIfNeeded];
    [self layoutIfNeeded];
}

@end
4 голосов
/ 06 ноября 2013

Эта категория делает сворачивание ограниченных видов Auto Layout действительно простым:

https://github.com/depth42/AutolayoutExtensions

Я только добавил его в проект, и он прекрасно работает.

2 голосов
/ 24 февраля 2012

Я не думаю, что вы могли бы сделать это таким образом. Если вы сделали макет для метки 2 основанным на ограничении расстояния от метки 1, даже если вы сделали авто-свертку метки 1 до нулевой высоты, когда у нее нет содержимого, метка 2 все равно будет на таком расстоянии вниз, то есть в:

+----------------+
| +------------+ |
| + label 1    | |
| +------------+ |
|        ^       |
|        ^       !
| +------------+ |
| | label 2    | |
| +------------+ |
+----------------+

Где ^ - ограничение расстояния для автоматического размещения - Если Метка 1 знает, как стать нулевой высотой, когда строка пуста, вы все равно получите:

+----------------+
| +------------+ |
|        ^       |
|        ^       !
| +------------+ |
| | label 2    | |
| +------------+ |
+----------------+

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

Но, сделав все это, вы теперь закодировали подкласс NSLabel, который автоматически изменяет размеры, создали объект ограничения вручную, а не через визуальный язык, и согнули NSLayoutConstraint сверх его воли.

Я думаю, вам лучше просто поменять рамку метки 2, если строка метки 1 пуста!

1 голос
/ 29 июня 2013

Вот пример того, как я справился с этим программно, а не с помощью Interface Builder. В итоге; Я только добавляю представление, если оно включено, и затем перебираю подпредставления, добавляя вертикальные ограничения по мере продвижения.

Обратите внимание, что рассматриваемые виды инициализируются до этого.

/*
  Begin Auto Layout
*/
NSMutableArray *constraints = [NSMutableArray array];
NSMutableDictionary *views = [[NSMutableDictionary alloc] init];


/*
  Label One
*/
if (enableLabelOne) {
    [contentView addSubview:self.labelOne];

    self.labelOne.translatesAutoresizingMaskIntoConstraints = NO;

    [views setObject:self.labelOne
              forKey:@"_labelOne"];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_labelOne(44)]"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_labelOne]-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];
}

/*
    Label Two
*/
if (enableLabelTwo) {
    [contentView addSubview:self.labelTwo];

    self.labelTwo.translatesAutoresizingMaskIntoConstraints = NO;

    [views setObject:self.labelTwo
              forKey:@"_labelTwo"];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_labelTwo(44)]"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];
}

[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_labelTwo]-|"
                                                                         options:0
                                                                         metrics:nil
                                                                           views:views]];

/*
  Dynamically add vertical spacing constraints to subviews
*/
NSArray *subviews = [contentView subviews];

if ([subviews count] > 0) {
    UIView *firstView = [subviews objectAtIndex:0];
    UIView *secondView = nil;
    UIView *lastView = [subviews lastObject];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[firstView]"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:NSDictionaryOfVariableBindings(firstView)]];

    for (int i = 1; i < [subviews count]; i++) {
        secondView = [subviews objectAtIndex:i];
        [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[firstView]-10-[secondView]"
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:NSDictionaryOfVariableBindings(firstView, secondView)]];
        firstView = secondView;
    }

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[lastView]-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:NSDictionaryOfVariableBindings(lastView)]];
}


[self addConstraints:constraints];

Я только устанавливаю ограничение lastView, потому что этот код был адаптирован из чего-то внутри UIScrollView.

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

0 голосов
/ 09 декабря 2015

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

enter image description here

Используется Картография для их замены каждый раз, когда hidden изменяется в любом из них.

let buttons = self.buttons!.filter { button in
    return !button.hidden
}

constrain(buttons, replace: self.constraintGroup) { buttons in
    let superview = buttons.first!.superview!

    buttons.first!.left == superview.left

    for var i = 1; i < buttons.count; i++ {
        buttons[i].left == buttons[i-1].right + 10
    }

    buttons.last!.right == superview.right
}
0 голосов
/ 02 октября 2015

Я нашел другой способ сделать это.Эта методология может применяться где угодно, не имеет проблем с масштабированием;и обрабатывает поля также.И вам не нужны сторонние вещи для этого.

Во-первых, не используйте этот макет:

V:|-?-[Label1]-10-[Label2]-10-|
H:|-?-[Label1]-?-|
H:|-20-[Label2]-20-|

Используйте вместо этого:

("|" is the real (outer) container)
V:|-?-[Label1]-0-[Label2HideableMarginContainer]-0-|
H:|-?-[Label1]-?-|
H:|-0-[Label2HideableMarginContainer]-0-|

("|" is Label2HideableMarginContainer)
V:|-10-[Label2]-10-|
H:|-20-[Label2]-20-|

Так что жемы сделали сейчас?Label2 не используется напрямую в макете;он помещен в Margin-Container.Этот контейнер используется как прокси из Label2, с 0 полями в макете.Реальные поля помещены в из Margin-Container.

Теперь мы можем скрыть Label2 с:

  • установкой Hidden в YES на нем

И

  • Отключение ограничений Top, Bottom, Leading и Trailing.Так что ищите их, а затем установите Active на NO.Это заставит Margin-Container иметь Frame Size из (0,0);потому что у него есть подпредставление (я);но нет никаких (активных) ограничений макета, которые привязывают эти подпредставления к нему.

Может быть, немного сложнее, но вы должны разработать его только один раз.Всю логику можно поместить в отдельное место и использовать каждый раз, когда вам нужно скрыть smg.

Вот код C # Xamarin, как искать те ограничения, которые привязывают подпредставление (я) к внутренним краямMargin-Container вид:

public List<NSLayoutConstraint> SubConstraints { get; private set; }

private void ReadSubContraints()
{
    var constraints = View.Constraints; // View: the Margin-Container NSView
    if(constraints?.Any() ?? false)
    {
        SubConstraints = constraints.Where((NSLayoutConstraint c) => {
            var predicate = 
                c.FirstAttribute == NSLayoutAttribute.Top ||
                c.FirstAttribute == NSLayoutAttribute.Bottom ||
                c.FirstAttribute == NSLayoutAttribute.Leading ||
                c.FirstAttribute == NSLayoutAttribute.Trailing;
            predicate &= ViewAndSubviews.Contains(c.FirstItem); // ViewAndSubviews: The View and View.Subviews
            predicate &= ViewAndSubviews.Contains(c.SecondItem);
            return predicate;
        }).ToList();
    }
}
0 голосов
/ 28 июня 2013

Я нашел довольно приличный способ сделать это.Это похоже на Дэвида .Вот как это работает в коде.Я создал суперпредставление и все его подпредставления, даже те, которые могут не всегда отображаться.Я добавил многие ограничения, такие как V:|-[_btn], в суперпредставление.Как вы можете видеть в конце этих ограничений, в суперпредставлении нет ссылки внизу.Затем я создал два массива ограничений для обоих состояний представления, для меня различие заключается в раскрытии треугольника «Дополнительные параметры».Затем, когда щелкают треугольник в зависимости от его состояния, я добавляю и удаляю ограничения и подпредставления соответственно.Например, чтобы добавить, я делаю:

[self.backgroundView removeConstraints:self.lessOptionsConstraints];
[self.backgroundView addSubview:self.nameField];
[self.backgroundView addConstraints:self.moreOptionsConstraints];

Ограничения, которые я удалил, привязали кнопку к нижней части суперпредставления, как V:[_btn]-|.Добавленные мною ограничения выглядят как V:[_btn]-[_nameField]-|, поскольку вы можете видеть, что это ограничение помещает новый вид между исходным видом над ним и нижней частью суперпредставления, который увеличивает высоту суперпредставления.

...