Подтвердить что ты не робот

Проблема с автоматической компоновкой 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: Здесь тестовый проект, который я использовал при попытке изолировать проблему: Загрузить

4b9b3361

Ответ 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, чтобы взять длинный хвост, который разбивает ваш макет.