Подтвердить что ты не робот

OS X Cocoa Скрытые элементы Auto Layout

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

Скриншот интерфейса Builder http://f.cl.ly/items/2J120b3Q3o2u1H1z3b2T/Screen%20Shot%202012-08-16%20at%2010.40.28%20PM.png

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

Ответ 3

Скрывающий подкласс 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

Эта категория упрощает свертывание автоматических макетов с ограниченным представлением:

https://github.com/depth42/AutolayoutExtensions

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

Ответ 5

Вот пример того, как я обрабатывал это программно, а не используя 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.

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

Ответ 6

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

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

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

Ответ 7

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

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

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); потому что у него есть subview (s); но нет никаких (активных) ограничений компоновки, которые привязывают к ним эти подпрограммы.

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

Вот код С# Xamarin, как искать те ограничения, которые привязывают subview (s) к внутренним ребрам представления 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();
    }
}

Ответ 8

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

введите описание изображения здесь

Используется Cartography, чтобы заменять их каждый раз, когда 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
}