Вы можете получить то, что хотите, с автозагрузкой. Демонстрация:
Однако, если зеленые точки зрения не имеют никакого вертикального перекрытия, результаты могут не быть то, что вы хотите:
Итак, как я сделал это ?
Прежде всего, существует пять видов. Имеется представление содержимого окна, два перетаскиваемых зеленых вида, текстовое поле («Центр») и вид с зазором загар. Перетаскиваемые представления, текстовое поле и разделитель являются прямым подзоном просмотра содержимого окна. В частности, текстовое поле не подглядывание проставки.
Во-вторых, я должен установить приоритет некоторых ограничений, поэтому я определить вспомогательную функцию:
static NSLayoutConstraint *constraintWithPriority(NSLayoutConstraint *constraint,
NSLayoutPriority priority)
{
constraint.priority = priority;
return constraint;
}
Далее я создаю левый перемещаемой вид. Я установил ограничения для его X-позиции и ширины, высоты и положения Y.
- (void)createLeftView {
leftView = [[DraggableView alloc] init];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:leftView];
NSDictionary *views = NSDictionaryOfVariableBindings(leftView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-(20)-[leftView(80)]"
options:0 metrics:nil views:views]];
[leftView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[leftView(120)]"
options:0 metrics:nil views:views]];
leftView.yConstraint = [NSLayoutConstraint
constraintWithItem:leftView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:rootView attribute:NSLayoutAttributeTop
multiplier:1 constant:20];
[rootView addConstraint:leftView.yConstraint];
}
Обратите внимание, что мой DraggableView
класс обрабатывает мышь тащит, регулируя проницаемость ее yConstraint
. Поскольку мне нужен доступ к ограничению Top, я устанавливаю его непосредственно вместо использования визуального формата.
Я создаю правый перетаскиваемый вид очень точно, за исключением того, что он привязан к задней кромке корневого представления.
- (void)createRightView {
rightView = [[DraggableView alloc] init];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:rightView];
NSDictionary *views = NSDictionaryOfVariableBindings(rightView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[rightView(80)]-(20)-|"
options:0 metrics:nil views:views]];
[rightView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[rightView(120)]"
options:0 metrics:nil views:views]];
rightView.yConstraint = [NSLayoutConstraint
constraintWithItem:rightView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:rootView attribute:NSLayoutAttributeTop
multiplier:1 constant:20];
[rootView addConstraint:rightView.yConstraint];
}
Теперь наступает сложная часть. Я создаю дополнительный вид, который я называю разделителем. Я оставил вид видимым, чтобы было легче понять, как работает эта демонстрация. Обычно вы делаете спейсер скрытым; скрытые виды все еще участвуют в макете.
- (void)createSpacerView {
spacerView = [[SpacerView alloc] init];
spacerView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:spacerView];
Горизонтальные ограничения распорки просты. Передний край проставки прикреплен к задней кромке левого перетаскиваемого вида, а задний край проставки прикреплен к передней кромке правого перетаскиваемого вида. Таким образом, распорка всегда точно охватывает горизонтальный зазор между перетаскиваемыми (зелеными) видами.
NSDictionary *views = NSDictionaryOfVariableBindings(leftView, rightView, spacerView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[leftView][spacerView][rightView]"
options:0 metrics:nil views:views]];
Далее, мы ограничить верхний и нижний край спейсера к равным зрения корневого верхнего и нижнего краев, но с приоритетом 1 (чрезвычайно низкий приоритет):
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-([email protected])-[spacerView]-([email protected])-|"
options:0 metrics:nil views:views]];
Затем мы дополнительно ограничить распорные-х верхний край, чтобы быть больше или равно верхние края обоих верхних краев перетаскиваемую Views', с приоритетом 2 (почти как крайне низкий):
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:leftView attribute:NSLayoutAttributeTop
multiplier:1 constant:0], 2)];
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:rightView attribute:NSLayoutAttributeTop multiplier:1 constant:0], 2)];
так верхний край распорной имеет три Ограничения:
- spacerView.top == rootView.top с приоритетом 1
- spacerView.top> = leftView.топ с приоритетом 2
- spacerView.top> = rightView.top с приоритетом 2
Эти ограничения объединяются, чтобы поместить верхний край спейсера, как высоко, как это возможно, не будучи выше верхних краев обеих Draggable Views'. Таким образом, верхний край проставки будет равен нижнему из верхних краев перетаскиваемых видов.
Нам нужно использовать низкие приоритеты, потому что автоматическая компоновка откажется сделать высоту проставки отрицательной. Если мы использовали приоритет 1000 (по умолчанию), автоматический макет начнет изменять размер перетаскиваемых представлений, чтобы заставить их всегда иметь некоторое вертикальное перекрытие.
Мы создали аналогичные ограничения на нижнем крае спейсера по:
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:leftView attribute:NSLayoutAttributeBottom
multiplier:1 constant:0], 2)];
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:rightView attribute:NSLayoutAttributeBottom
multiplier:1 constant:0], 2)];
}
Таким образом, распорка всегда будет занимать вертикальное перекрытие перемещаемых взглядов, если есть перекрытие. Если нет перекрытия, автомакет будет разорвать некоторое ограничение, чтобы не дать разделителю отрицательную высоту. Проставка будет иметь высоту 0, прикрепленную к одному из ближайших краев перетаскиваемых видов.
Наконец, я создал текстовое поле, чтобы оставаться в центре по горизонтали и по вертикали над распоркой:
- (void)createMiddleView {
middleView = [[NSTextField alloc] init];
middleView.translatesAutoresizingMaskIntoConstraints = NO;
middleView.stringValue = @"Center";
[middleView setEditable:NO];
[middleView setSelectable:NO];
[rootView addSubview:middleView];
[rootView addConstraint:[NSLayoutConstraint
constraintWithItem:middleView attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:spacerView attribute:NSLayoutAttributeCenterX
multiplier:1 constant:0]];
[rootView addConstraint:[NSLayoutConstraint
constraintWithItem:middleView attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:spacerView attribute:NSLayoutAttributeCenterY
multiplier:1 constant:0]];
}
С учетом всех этих ограничений между пятью видами (в том числе ввиду корневой), автоматическая раскладка держит середину сфокусированный по тому, как вы просили.
Я не уверен, что можно установить ограничения, подобные этому, в кодировке Xcode 4.6.3. Если это возможно, я уверен, что это очень больно.
Вы можете найти мой полный исходный код проекта демо-проекта in this github repository. Я создал проект в Xcode 5-DP2.
Вы можете получить зеленые, где вы их ожидаете? Они двигаются? – uchuugaka
Да. Зеленые можно перемещать и изменять размер, используя только ограничения. Это красное представление, что у меня проблемы с ограничением. –
Я имею в виду, что они ели не всегда на одинаковых позициях? Если так, то это может быть сложно, но я могу придумать несколько решений. – uchuugaka