Центрирование представления с визуальным форматом NSLayoutConstraints

Я пытаюсь центрировать представление, используя язык визуального формата.

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_progressView(300)]-|" options:NSLayoutFormatAlignAllCenterY metrics:0 views:views]];

(views — это NSDictionaryOfVariableBindings, содержащий _progressView)

Он не центрирует мой вид (ширина: 300) в пределах self.view (ширина: 768). Он выравнивает его по левому краю с отступом в 8 пикселей, как если бы я написал только @"H:|-[_progressView(300)".

Единственный способ центрировать представление, используя что-то вроде:?

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_progressView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];

Спасибо


person Joseph    schedule 16.11.2013    source источник


Ответы (3)


Эта строка формата

@"H:|-[_progressView(300)]-|"

не указывает AutoLayout центрировать progressView. Вместо этого он говорит, что progressView должен иметь ширину 300 и иметь стандартное поле, определенное системой, с обеих сторон от краев супервизора. Очевидно, что это не может быть удовлетворено, поэтому Auto Layout снимает некоторые ограничения (вы, вероятно, получаете некоторые журналы на консоли). Ограничение, которое отбрасывается в вашем случае, вероятно, является полем справа.

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

@interface UIView (MyLayout)
- (void)centerHorizontallyInSuperview;
@end

@implementation UIView (MyLayout)
- (void)centerHorizontallyInSuperview {
    NSLayoutConstraint *c;
    c = [NSLayoutConstraint constraintWithItem:self
                                     attribute:NSLayoutAttributeCenterX
                                     relatedBy:NSLayoutRelationEqual                                                             
                                        toItem:self.superview
                                     attribute:NSLayoutAttributeCenterX 
                                    multiplier:1 
                                      constant:0];
    [self.superview addConstraint:c];
}
@end
person DrummerB    schedule 16.11.2013

Три подхода:

  1. Вы можете поместить свой код добавления ограничения в метод или категорию, как это было предложено DrummerB.

    Вы также, начиная с iOS 9, можете использовать более новый, более краткий синтаксис, используя якоря и используя activateConstraints:

    [NSLayoutConstraint activateConstraints:@[
        [centerView.centerXAnchor constraintEqualToAnchor:view.centerXAnchor],
        ...
    ]];
    

    Или в Свифт 3:

    NSLayoutConstraint.activate([
        centerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ...
    ])
    
  2. Вы можете использовать UILayoutGuide объекты:

    UILayoutGuide *leftGuide = [[UILayoutGuide alloc] init];
    [view addLayoutGuide:leftGuide];
    UILayoutGuide *rightGuide = [[UILayoutGuide alloc] init];
    [view addLayoutGuide:rightGuide];
    

    и определите их равными друг другу с помощью VFL следующим образом:

    H:|[leftGuide][_progressView(==300)][rightGuide(==leftGuide)]|
    

    Если вы не можете использовать направляющие макета (например, вам нужна поддержка версий iOS до 9.0, вы хотите создать это в IB и т. д.), вы можете использовать невидимые объекты UIView (нулевой альфа-канал или прозрачный цвет фона) в очень похожая манера. Просто добавьте эти «распорные» представления в свою иерархию представлений, используйте горизонтальные ограничения, как указано выше, но просто настройте представления, чтобы вы их не видели.

    Этот метод был бы громоздким способом решения проблемы «как центрировать вид», но он чрезвычайно полезен, если вы, например, хотите равномерно распределить дюжину видов и по какой-то причине не можете использовать UIStackView. Вы можете использовать сколько угодно UILayoutGuide (или невидимых UIView) объектов для достижения желаемого эффекта.

  3. Для полноты картины я должен указать, что можно добиться эффекта, используя options из NSLayoutFormatAlignAllCenterX/Y, как указано в https://stackoverflow.com/a/14917695/1271826, но, признаюсь, я нахожу это излишне запутанным.

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

person Rob    schedule 16.11.2013
comment
Несмотря на то, что мне больше всего нравится решение по категориям для моей текущей проблемы, решение с разделителями может быть очень полезным, когда дело доходит до разделения нескольких элементов. Я проголосовал за ваш ответ. - person Joseph; 16.11.2013
comment
отличная сумма вариантов. 1. ваш 2-й. решение хорошо, только если у вас нет ничего рядом с centeredView. Правильно? и для создания clearView я должен установить для них альфа-канал 0? 2. Можете ли вы объяснить, что делает (<=0)? - person Honey; 11.01.2017
comment
@Honey - Нет, этот второй метод отлично работает, если у вас есть что-то рядом с центрированным видом, просто у вас, вероятно, будет отдельный VFL для центрирования вида, а другой - для размещения чего-либо рядом с ним. И да, чтобы изображение было четким, просто установите его фон на clearColor или уменьшите его альфа-канал. Или в настоящее время вы обычно используете вместо этого NSLayoutGuide, что намного лучше. Что касается синтаксиса >= в третьем подходе, хотя я изначально нашел этот третий подход интеллектуально интересным, оглядываясь назад, я бы не советовал тратить на него время. Это ужасный узор. - person Rob; 13.01.2017

Для тех, кому это нужно, категория @DrummerB ObjC как расширение Swift:

extension UIView {

    func centerInSuperview() {
        self.centerHorizontallyInSuperview()
        self.centerVerticallyInSuperview()
    }

    func centerHorizontallyInSuperview(){
        let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .CenterX, relatedBy: .Equal, toItem: self.superview, attribute: .CenterX, multiplier: 1, constant: 0)
        self.superview?.addConstraint(c)
    }

    func centerVerticallyInSuperview(){
        let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .CenterY, relatedBy: .Equal, toItem: self.superview, attribute: .CenterY, multiplier: 1, constant: 0)
        self.superview?.addConstraint(c)
    }

}
person Koen.    schedule 26.08.2015
comment
Я бы проголосовал, но постараюсь зарезервировать это для кода, который я пробовал и знаю, что он работает. Поэтому вместо этого я просто скажу спасибо за публикацию этого. - person funroll; 18.09.2015
comment
Безумно красиво и удобно! - person Rafael Nobre; 03.06.2016