Как изменить размер пользовательского вида с помощью окна с Cocoa Авто макета?

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

NSView *contentView = [self.window contentView];
CustomView *customView = [[CustomView alloc] initWithFrame:[contentView bounds]];
[contentView addSubview:customView];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeWidth
        multiplier:1
        constant:0]];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeHeight
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeHeight
        multiplier:1
        constant:0]];

Тогда окно не позволяет изменять его размер.
Если я добавлю:

[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

Тогда пользовательское представление не появляется (drawRect:, похоже, никогда не вызывается). Я пробовал разные способы (включая визуальный формат @"|[customview]|"), но я всегда сталкиваюсь с теми же проблемами. Я знаю, что это можно сделать с помощью старой системы авторезистирования, с помощью:

[customView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];

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

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

Ответ 1

С помощью Auto Layout существует (по крайней мере) три возможных способа ограничить представление таким образом, чтобы он занимал весь вид содержимого Windows, при необходимости изменяя размер.

Ограничения визуального формата в отношении супервизора

NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

NSDictionary *views = NSDictionaryOfVariableBindings(customView);

[contentView addConstraints:
    [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|"
        options:0
        metrics:nil
        views:views]];

[contentView addConstraints:
    [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|"
    options:0
    metrics:nil
    views:views]];

Программные ограничения для ребер

(это должно быть эквивалентно визуальному формату выше)

+ (void)addEdgeConstraint:(NSLayoutAttribute)edge superview:(NSView *)superview subview:(NSView *)subview {
    [superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
        attribute:edge
        relatedBy:NSLayoutRelationEqual
        toItem:superview
        attribute:edge
        multiplier:1
        constant:0]];
}

и

NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

[[self class] addEdgeConstraint:NSLayoutAttributeLeft superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeRight superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeTop superview:contentView subview:customView];
[[self class] addEdgeConstraint:NSLayoutAttributeBottom superview:contentView subview:customView];

Ограничение программы для размера

NSView *contentView = [_window contentView];
MyView *customView = [[MyView alloc] initWithFrame:[contentView bounds]];
[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

[contentView addSubview:customView];

[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeWidth
        multiplier:1
        constant:0]];
[contentView addConstraint:
    [NSLayoutConstraint constraintWithItem:customView
        attribute:NSLayoutAttributeHeight
        relatedBy:NSLayoutRelationEqual
        toItem:contentView
        attribute:NSLayoutAttributeHeight
        multiplier:1
        constant:0]];

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

[customView setTranslatesAutoresizingMaskIntoConstraints:NO];

также применяется оригинальная маска авторазрезания, которая приводит к поведению, описанному в вопросе: окно не изменяется.

Как упоминалось Regexident, вы можете использовать:

[_window visualizeConstraints:[contentView constraints]];

для отладки Auto Layout. Также стоит проверить вывод консоли.

Ответ 2

@Bavarious ответ хороший, я просто добавлю еще несколько вещей.

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

  • Определите представление, которое находится не в том месте. Вызов метода - [NSView _subtreeDescription] из gdb и/или передача аргументов -NSShowAllViews YES может помочь определить, какое представление неверно.
  • Определите ограничения или ограничения, которые являются неправильными или отсутствуют. - [Ограничения NSLayoutConstraintAffectingLayoutForOrientation:] дает вам меньший набор ограничений для работы. - [NSWindow visualizeConstraints:] может помочь вам понять, каковы эти ограничения, и вы можете видеть, из какого из них вы не хотите быть там. Он также покажет вам, если ваш макет неоднозначен (недостаточно ограничений).
  • Определите, откуда произошло неправильное ограничение. Шаблон Cocoa Layout в Инструментах подобен инструменту Leaks - он покажет вам все события в жизненном цикле ограничения, например, когда он был создан, добавлен в окно, изменен и т.д. Итак, как только вы знаете, какое ограничение является проблемой, используйте поле поиска в Инструментах для фильтрации вниз, чтобы просто просмотреть это ограничение, и вы можете увидеть обратные трассировки для всех событий жизненного цикла и выяснить, где вы сделали то, что вам не нужно.

Обычно вопрос, который вы публикуете (мои вещи не работают!), будет недостаточным для того, чтобы люди могли сказать, что неправильно, что является одной из причин того, что важно использовать отладочную информацию. См. Видеозапись сессии WWDC 2011 (бесплатно для всех) и документы для получения дополнительной информации об этом.

Buuuuut Я могу сказать, что пошло не так на этот раз.:-) Перед тем, как вы выключили translatesAutoresizingMaskIntoConstraints, вы были более ограничены, чем хотели, - были также исправлены ширина и высота вашего представления, поэтому окно не могло изменять размер. ПОСЛЕ того, как вы его отключили, у вас была двусмысленная компоновка, потому что вы нигде не закрепили свой взгляд! Вы сказали, насколько велика она должна быть (такая же, как и надзор), но не там, где это должно было быть.

Кен

Cocoa Рамки, основной автомат макета

Ответ 3

Вы можете создавать ограничения с помощью макетов компоновки с очень легко читаемым форматом:

Код Swift2

func fit(childView: UIView, parentView: UIView) {
    childView.translatesAutoresizingMaskIntoConstraints = false
    childView.topAnchor.constraintEqualToAnchor(parentView.topAnchor).active = true
    childView.leadingAnchor.constraintEqualToAnchor(parentView.leadingAnchor).active = true
    childView.trailingAnchor.constraintEqualToAnchor(parentView.trailingAnchor).active = true
    childView.bottomAnchor.constraintEqualToAnchor(parentView.bottomAnchor).active = true
}

Использование:

parrentView.addSubview(childView)
fit(childView, parentView: parrentView)

Ответ 4

Я обнаружил, что -drawRect: не будет вызываться в случае, если прямоугольник кадра равен 0,0,0,0. Нежелательные ограничения, по-видимому, приводят к тому, что кадр становится 0,0,0,0.