Проблема с автоматической компоновкой iOS 8
У меня есть неприятная проблема с автоматической компоновкой, которая встречается в iOS 8, в то время как одна и та же настройка отлично работает в iOS 7.
Я упростил и изолировал проблемную ситуацию, и вот как она должна работать - и как она работает в iOS 7:
![Constraints working in iOS 7]()
Функция состоит из одного вида контейнера (синий) и трех подзадач:
- Ярлык
- Изображение (с нарезанным, масштабированным изображением)
- Кнопка
Я добавил слайдер, который управляет шириной представления контейнера, изменяя ограничение ширины контейнера.
Ограничения автоматической компоновки привязывают ярлык к левой стороне, кнопка к правой стороне, а вид изображения привязан к обеим сторонам без ограничения ширины.
В iOS 8 точно такая же настройка выглядит так:
![Constraints broken in iOS 8]()
Обратите внимание, что вид изображения (черная линия), как представляется, привязан к смещению снаружи справа от вида контейнера.
Я пробовал все, и я не могу понять, почему это происходит по-разному в iOS 8. Я удалил и заменил все ограничения несколько раз, но я все равно получаю тот же результат.
Это ограничения, заданные для каждого из подматричек, с уважением:
![Sub views constraints]()
И это ограничения для представления контейнера:
![Container view constraints]()
Ограничение ширины представления контейнера управляется с помощью ползунка следующим образом:
![Width constraint connection]()
- (IBAction)handleSliderChange:(id)sender
{
self.buttonWidth.constant = self.slider.value * 1024.0;
[self.containerView setNeedsLayout];
[self.containerView setNeedsUpdateConstraints];
[self.containerView layoutIfNeeded];
[self.view setNeedsLayout];
[self.view setNeedsUpdateConstraints];
[self.view layoutIfNeeded];
}
Другие, у которых возникли проблемы с автоматической компоновкой в iOS 8, похоже, смогли решить аналогичные проблемы, вызвав setNeedsLayout
и/или setNeedsUpdateConstraints
непосредственно на содержащем представлении, поэтому я очень четко вызывая его как для представления, так и для корневого представления. Я пробовал все комбинации выше, но ничего, похоже, не имеет значения.
Я начинаю терять надежду найти решение, но, возможно, кто-то здесь столкнулся с подобной ситуацией и может внести определенную ясность в эту проблему?
Изменить: Я должен добавить, что я также попытался установить ограничения из представления изображения на метку и кнопку в противоположность краям содержащего представления с тем же результатом.
Изменить 2: Здесь тестовый проект, который я использовал при попытке изолировать проблему: Загрузить
Ответы
Ответ 1
Это не проблема автоматического макета, но должна быть ошибкой в iOS 8 об обработке срезанных изображений.
С вашим примером проекта:
UIImage *img = [UIImage imageNamed:@"pointer-dark-left"];
NSLog(@"alignmentRect: %@", NSStringFromUIEdgeInsets(img.alignmentRectInsets));
NSLog(@"capInsets: %@", NSStringFromUIEdgeInsets(img.capInsets));
Этот код печатает
// iOS 7
alignmentRect: {0, 0, 0, 0}
capInsets: {0, 5.5, 0, 134}
// iOS 8
alignmentRect: {0, 0, 0, 65.5}
capInsets: {0, 5.5, 0, 134}
alignmentRectInsets.right
есть 65.5
!
Как задокументировано, Autolayout работает с прямоугольниками выравнивания, а UIImageView
использует свои образы alignmentRectInsets
. Таким образом, ваш self.line
имеет 65.5
pt дополнительную правую ширину.
Конечно, вы не установили вставки выравнивания в редакторе xcassets
, я считаю, что это ошибка.
![screenshot]()
Вот уродливый, но рабочий, обходной путь: (
- (void)viewDidLoad {
[super viewDidLoad];
self.line.image = [self.line.image imageWithAlignmentRectInsets:UIEdgeInsetsZero];
}
Ответ 2
Я думаю, что вы неправильно связали свои ограничения.
Я создаю приложение с одинаковыми компонентами, и он работает отлично. Возможно, вы можете снова очистить все ограничения и настроить.
Я добавляю код, чтобы сделать это программно.
#import "ViewController.h"
@interface ViewController () {
NSLayoutConstraint* csViewWidth;
}
@end
@implementation ViewController
- (void)viewDidLoad {
NSDictionary *views = @{
@"label": self.uiLabel,
@"image": self.uiImage,
@"button": self.uiButton
};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[label]-[image]-[button]-|"
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
NSArray *panelConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[panel(320)]" options:0 metrics:nil views:@{ @"panel": self.uiPanel }];
csViewWidth = [panelConstraint firstObject];
[self.view addConstraints:@[csViewWidth]];
}
- (IBAction)onSliderChanged:(UISlider*)sender {
float containerWidth = self.view.frame.size.width;
csViewWidth.constant = sender.value * containerWidth;
}
@end
Это решение проверено и работает нормально.
Нет необходимости вызывать setNeedsLayout и layoutIfNeeded.
Ответ 3
Это не из-за ошибки iOS
Изображение неправильно нарезано
Я тестировал ваш проект, и он работает сейчас, как и ожидалось.
Нарезка должна выглядеть так:
O | - | ----------- |
- Сначала: за кругом
- Второе: 1 единица после первого
- В-третьих: на самом конце
С вашим разрезом вы говорите ios, чтобы взять длинный хвост, который разбивает ваш макет.