2013-06-25 2 views
5

Представьте себе следующий сценарий. У вас есть UIImageView и UIButton. Первый - 300x360, второй - 210x70. imageview содержит изображение в каталоге, button говорит «открытый каталог».Центр два вида по вертикали с использованием NSLayoutConstraint

Я хотел бы расположить элементы в главном окне в соответствии с этими требованиями:

  • два элемента должны быть центрированы по горизонтали, а именно координаты center.x должны быть все равны (вид, изображение и кнопка);

  • два элемента должны быть центрированы по вертикали следующим образом: разделитель (гибкий) - изображение - разделитель (фиксированный, допустим, 30 очков) - кнопка - разделитель (гибкий). Самый верхний и самый нижний разделитель должен иметь одинаковый размер (это значение центрировано).

Я не могу заставить это работать, используя NSLayoutConstraint.

До сих пор то, что я делал, центрировало координату X двух элементов, используя NSLayoutAttributeCenterX и NSLayoutRelationEqual к тому же атрибуту view.

Последняя часть, согласно моей идее, заключается в том, чтобы зафиксировать их вертикальное выравнивание. Я пробовал использовать @"V:|-[imageview(360)]-[button(70)]-|", но это не сработает (Unable to simultaneously satisfy constraints.).

Если я использую @"V:|-[imageview(360)]-[button]-|", я получаю все частично в порядке. А именно, верхняя часть идеальна, но кнопка растягивается так, чтобы заполнить промежуток между внутренним разделителем и нижней частью представления.

Как я могу сделать эти элементы фиксированными и позволить Auto-layout просто понять, как их разместить в представлении?

+0

В прошивке 9, посмотрите на UIStackView и UILayoutGuide для решения такого рода проблем. –

ответ

10

я был в состоянии сделать это, делая что-то вроде этого:

NSNumber *sepHeight = @60.0F; 

// Center the two views horizontally 
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView 
                 attribute:NSLayoutAttributeCenterX 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:self.view 
                 attribute:NSLayoutAttributeCenterX 
                multiplier:1 
                 constant:0]]; 

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

// Position the two views one below the other, using the separator height defined above 
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[imageview]-sepHeight-[button]" 
                    options:0 
                    metrics:NSDictionaryOfVariableBindings(sepHeight) 
                    views:views]]; 

// Force the button distance from the bottom to be the half of the size of the content 
CGFloat constant = (imageview.frame.size.height + button.frame.size.height + [sepHeight floatValue])/2.0; 
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                 attribute:NSLayoutAttributeBottom 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:self.view 
                 attribute:NSLayoutAttributeCenterY 
                multiplier:1 
                 constant:constant]]; 

Хитрая часть является постоянной величиной. Это значение составляет половину высоты всех просмотров, включая их разделители. Это означает, что если высота изображения равна 360, высота кнопки составляет 70, а разделитель равен 60, то эта константа будет (360 + 70 + 60)/2 = 245.

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

+0

Умный! Хороший пример использования косвенности мышления Auto Layout, а также коэффициента и константы. Итак, один из способов его описания - Коэффициент = 0,5 Константа = (изображениеHeightConstant + imageButtonVerticalSpaceConstant + buttonHeightConstant) Или я что-то пропустил ...? Константы могут быть сохранены как переменные или свойства CGFloat и скорректированы для соответствующих значений ... :) – uchuugaka

+0

Нет, множитель применяется к значению self.view.frame.origin.y, и константа добавляется к такому значению. Теперь я перепишу его с помощью переменных. –

+1

Это работает для меня тоже, спасибо! –

0

Вам необходимо сделать ваши вертикальные пространства равными или больше, чем в верхней части изображения и в нижней части кнопки. Но это также звучит так, что было бы проще просто иметь дополнительное представление, содержащее ваше изображение и кнопку. Затем центрируйте это представление V и H в супер-представлении.

Auto Layout по-прежнему означает просмотр контейнеров.

+0

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

+0

Совсем нет. Контейнер может быть ограничен вершиной одного суб-представления и нижней части другого. – uchuugaka

+0

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

5

Есть два способа приблизиться к этому.

  1. Как предлагает @uchuugaka, поместите свое изображение и кнопку в виде контейнера. У вас нет такой же проблемы с центрированием, если подъявки привязаны к краям контейнера (размер которого вы бы соответствовали соответственно - высота была бы равна 360 + 30 + 70 == 460). Затем вы можете центрировать этот контейнер в своем представлении с помощью простое выравнивание-центр-Y.

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


- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    UIView* imageview = [UIView new]; 
    imageview.backgroundColor = [UIColor blueColor]; 
    imageview.translatesAutoresizingMaskIntoConstraints = NO; 

    UIView* button = [UIView new]; 
    button.backgroundColor = [UIColor greenColor]; 
    button.translatesAutoresizingMaskIntoConstraints = NO; 


    UIView* spacer1 = [UIView new]; 
    spacer1.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5]; 
    spacer1.translatesAutoresizingMaskIntoConstraints = NO; 
    spacer1.hidden = YES; // comment out to show spacer! 

    UIView* spacer2 = [UIView new]; 
    spacer2.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5]; 
    spacer2.translatesAutoresizingMaskIntoConstraints = NO; 
    spacer2.hidden = YES; // comment out to show spacer! 

    [self.view addSubview: imageview]; 
    [self.view addSubview: button]; 
    [self.view addSubview: spacer1]; 
    [self.view addSubview: spacer2]; 

    NSDictionary* views = NSDictionaryOfVariableBindings(imageview, button, spacer1, spacer2); 

    NSArray* constraints; 
    constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"V:|[spacer1(==spacer2)][imageview(360)]-30-[button(70)][spacer2(==spacer1)]|" 
                  options: 0 
                  metrics: nil 
                  views: views]; 
    [self.view addConstraints: constraints]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageview 
                  attribute:NSLayoutAttributeWidth 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:nil 
                  attribute:NSLayoutAttributeNotAnAttribute 
                 multiplier:1 
                  constant:300]]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                  attribute:NSLayoutAttributeWidth 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:nil 
                  attribute:NSLayoutAttributeNotAnAttribute 
                 multiplier:1 
                  constant:210]]; 


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

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                  attribute:NSLayoutAttributeCenterX 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:self.view 
                  attribute:NSLayoutAttributeCenterX 
                 multiplier:1 
                  constant:0]]; 
} 
+2

Мне нравится идея проставки. За исключением того, что он напоминает мне 90-ех HTML-файлы gifs;) – uchuugaka

+0

Распорки или дополнительный вид контейнера, если вы заранее не знаете (фиксированную) высоту. Ну что ж. :) –