Ограничения макета элемента UIView

Я новичок в разработке iOS, но дошел до того, что хочу создать свой собственный составной UIView в качестве пользовательского UIButton. Я хотел бы разместить UILabel и 2x UIImageViews следующим образом:

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

Но я также хотел бы привязать элементы управления (подпредставления) таким образом, чтобы по мере расширения метки, скажем, из-за переводов, представление автоматически обрабатывало дополнительное пространство. Например;

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

Идеально -

  • правая сторона UIView привязана справа; и остается фиксированной шириной/высотой (выравнивается по правому краю).
  • the label and bottom image divide the remaining left hand space
    • the label is vertically centered in the top half of the upper remaining space
    • нижнее изображение остается центрированным (как по вертикали, так и по горизонтали) в нижнем оставшемся пространстве
  • если метка шире нижнего изображения, то представление должно расширяться

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

Я работаю на C#/XAML, поэтому я обычно использую макеты сетки с фиксированными/авто/* столбцами и строками, но я предполагаю, что мне нужно использовать что-то вроде NSLayoutConstraints здесь - к сожалению, я не знаю, с чего начать и как для поиска решения. Любая помощь приветствуется!


person SimonM    schedule 25.02.2013    source источник


Ответы (1)


в вашем UIButton

(код не тестировался)

//put the code below in your button's init method    

UILabel *label = [[UILabel alloc] init];
UIImageView *bottomImageView = [[UIImageView alloc] init];
UIImageView *rightImageView = [[UIImageView alloc] init];

// we define our own contraints, 
// we don't want the system to fall-back on UIView's autoresizingMask property (pre-iOS 6)
self.translatesAutoresizingMaskIntoConstraints = NO;
label.translatesAutoresizingMaskIntoConstraints = NO;
bottomImageView.translatesAutoresizingMaskIntoConstraints = NO;
rightImageView.translatesAutoresizingMaskIntoConstraints = NO;

//fixed sizes => SET THESE as you want
CGFloat labelHeight, bottomWidth, rightImageWidth;


// 1 - Label constraints :
// labelWidth = 1 *self.width - rightImageWidth
NSLayoutConstraint *labelWidth = [NSLayoutConstraint constraintWithItem:label
                                                               attribute:NSLayoutAttributeWidth
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeWidth
                                                              multiplier:1
                                                                constant:(- rightImageWidth)];

// label must be at the top :
NSLayoutConstraint *labelTop = [NSLayoutConstraint constraintWithItem:label
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1
                                                                constant:0];

//label must be on the left border
NSLayoutConstraint *labelLeft = [NSLayoutConstraint constraintWithItem:label
                                                               attribute:NSLayoutAttributeLeft
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeLeft
                                                              multiplier:1
                                                                constant:0];

//label must be of 1/2 height

NSLayoutConstraint *labelHeight = [NSLayoutConstraint constraintWithItem:label
                                                               attribute:NSLayoutAttributeHeight
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeHeight
                                                              multiplier:0.5
                                                                constant:0];

[self addSubview:label];
[self addConstraints:@[labelWidth, labelTop, labelLeft, labelHeight]];

//2 - botom view
// width constant
NSLayoutContraint *bottomWidth = [NSLayoutConstraint constraintWithItem:bottomImageView
                                                               attribute:NSLayoutAttributeWidth
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:nil
                                                         attribute:NSLayoutAttributeNotAnAttribute
                                                              multiplier:0
                                                                constant:bottomWidth];

// same height constraint as label
NSLayoutConstraint *bottomHeight = [NSLayoutConstraint constraintWithItem:bottomImageView
                                                               attribute:NSLayoutAttributeHeight
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeHeight
                                                              multiplier:0.5
                                                                constant:0];

// sticks at container's bottom (pun intended)
NSLayoutConstraint *bottomBottom = [NSLayoutConstraint constraintWithItem:bottomImageView
                                                               attribute:NSLayoutAttributeBottom
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeBottom
                                                              multiplier:1
                                                                constant:0];

//we have height, width, y contraints, just x remains
// NOTE : this one is between bottom view and label
NSLayoutConstraint *bottomCenteredXAsLabel = [NSLayoutConstraint constraintWithItem:bottomImageView
                                                               attribute:NSLayoutAttributeCenterX
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:label
                                                               attribute:NSLayoutAttributeCenterX
                                                              multiplier:1
                                                                constant:0];

[self addSubview:bottomImageView];
[self addConstraints:@[bottomWidth, bottomHeight, bottomBottom, bottomCenteredXAsLabel]];

// 3 - last one !
NSLayoutConstraint *rightAligned = [NSLayoutConstraint constraintWithItem:rightImageView
                                                               attribute:NSLayoutAttributeRight
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeRight
                                                              multiplier:1
                                                                constant:0];

//right height
NSLayoutConstraint *rightHeight = [NSLayoutConstraint constraintWithItem:rightImageView
                                                               attribute:NSLayoutAttributeHeight
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeHeight
                                                              multiplier:1
                                                                constant:0];

//constant width...
NSLayoutConstraint *rightWidth =  [NSLayoutConstraint constraintWithItem:rightImageView
                                                               attribute:NSLayoutAttributeWidth
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:nil
                                                         attribute:NSLayoutAttributeNotAnAttribute
                                                              multiplier:0
                                                                constant:rightImageWidth];

//width, height, x constraints... 
//we still need one on y, let's say it sticks at the top

NSLayoutConstraint *rightTop = [NSLayoutConstraint constraintWithItem:rightImageView
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1
                                                                constant:0];

[self addSubview:rightImageView];
[self addConstraints:@[rightAligned, rightHeight, rightWidth, rightTop]];

И вуаля !

Метод всегда один и тот же: вам нужно как минимум 4 ограничения для каждого представления, установка ширины, высоты, x и y (размеры CGRect 4). Думайте об ограничении как об отношении:

item1.layoutAttribute1 >= a*item2.layoutAttribute2 + b 

перевести в такой форме

[NSLayoutConstraint constraintWithItem:item1
                             attribute:layoutAttribute1
                             relatedBy:NSLayoutRelationGreaterThanOrEqual
                                toItem:item2
                             attribute:layoutAttribute2
                            multiplier:a
                              constant:b];

Обратите внимание, что используя визуальный формат, вы можете выразить все это с меньшим количеством кода. (но я еще не играл с ним).

person Vinzzz    schedule 25.02.2013
comment
Спасибо за быстрый ответ! Мне пришлось внести некоторые изменения, но это сработало отлично. Для других, читающих это, мне нужно было обновить addSubView до addSubview, мне нужно было установить translatesAutoresizingMaskIntoConstraints во всех подпредставлениях, мне нужно было изменить self.view только на self, поскольку я создавал подкласс UIButton и вызывал [super layoutSubviews] до и после приведенного выше кода. - person SimonM; 26.02.2013
comment
ответ отредактирован! Но вы никогда не должны вызывать layoutSubviews напрямую (мне это никогда не нужно) см. комментарий к методу (developer.apple.com/library/ios/#documentation/uikit/reference/). Весь этот код - настройка подвидов - должен вызываться ПЕРЕД добавлением этой кнопки в иерархию представления (например: в методе инициализации подкласса вашей кнопки) - person Vinzzz; 26.02.2013
comment
Спасибо за пояснение, я переопределил layoutSubviews, чтобы сделать эту работу, поэтому мне пришлось вызвать [super layoutSubviews], иначе возникло исключение, в котором говорилось, что мне нужно сделать вызов! Я переместил код в init и удалил лишний код. - person SimonM; 26.02.2013
comment
Примечание; Я потратил пару часов, пытаясь изменить размер родительского UIButton, чтобы ширина была достаточной для UILabel, но я до сих пор не нашел решения. Я пересмотрю завтра. - person SimonM; 26.02.2013