Проблема автоматического изменения размера UICollectionViewCell frameView в ячейке прототипа Storyboard (Xcode 6, iOS 8 SDK) возникает только при работе на iOS 7

Я использую Xcode 6 Beta 3, iOS 8 SDK. Сборка целевой iOS 7.0 с использованием Swift. Пожалуйста, обратитесь к моей проблеме шаг за шагом со скриншотами ниже.

У меня есть UICollectionView в раскадровке. 1 Прототип UICollectionViewCell, который содержит 1 метку в центре (без правила авторазмера). Фиолетовый фон должен был отмечать contentView, который генерируется во время выполнения Ячейкой. Это представление будет изменено должным образом на основе моего UICollectionViewLayoutDelegate в конечном итоге, но не на iOS 7. Обратите внимание, что я использую Xcode 6, и проблема возникает только на iOS 7.

Когда я создаю приложение на iOS 8. Все в порядке.

Примечание: фиолетовый - это contentView, синий - моя UIButton с закругленным углом.

https://stackru.com/images/b02d55b4f10303da4a39186925077bff63eeb25e.png

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

https://stackru.com/images/f2d3e2def89b4d1fff94f90a7b947f6ad05fbf72.png

Я предполагаю, что это ошибка в iOS 8 SDK или Swift или, может быть, Xcode?


Обновление 1: эта проблема все еще существует в официальном Xcode 6.0.1! Лучшее обходное решение - это то, что KoCMoHaBTa предложил ниже, установив кадр в cellForItem ячейки (хотя вы должны создать подкласс для своей ячейки). Оказалось, что это несовместимость между iOS 8 SDK и iOS 7 (проверьте ответ ecotax ниже, цитируемый Apple).

Обновление 2: вставьте этот код в начало вашего cellForItem, и все должно быть в порядке:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/

12 ответов

Решение

contentView не работает Это также можно исправить в awakeFromNib

ObjC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}

Я столкнулся с той же проблемой и попросил Apple DTS о помощи. Их ответ был:

В iOS 7 представления содержимого ячеек изменяли свои размеры с помощью масок авторазмера. В iOS 8 это изменилось, ячейки перестали использовать маски авторазмера и начали изменять размер представления контента в layoutSubviews. Если перо закодировано в iOS 8, а затем декодировано на iOS 7, у вас будет представление контента без маски авторазмера и других средств для ее измерения. Поэтому, если вы когда-нибудь измените рамку ячейки, представление контента не будет следовать.

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

Я предполагаю, что это означает, что это не ошибка в XCode 6, а несовместимость между iOS 8 SDK и iOS 7 SDK, которая поразит вас, если вы обновитесь до Xcode 6, потому что он автоматически начнет использовать iOS 8 SDK.

Как я уже говорил, обходной путь Дэниел Пламанн описал для меня работы. Те, что описывают Игорь Палагута и KoCMoHaBTa, выглядят проще, и, кажется, имеет смысл дать ответ Apple DTS, так что я попробую их позже.

Я столкнулся с той же проблемой и надеюсь, что Apple исправит это в следующей версии XCode. Тем временем я использую обходной путь. В моем UICollectionViewCell Подкласс Я только что переопределил layoutSubviews и измените размер contentView вручную, если размер отличается от collectionViewCell размер.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}

Другое решение состоит в том, чтобы установить размер contentView и маски авторазмера в -collectionView:cellForItemAtIndexPath: как следующее:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Это работает и с Auto Layout, поскольку маски автоматического изменения размера переводятся в ограничения.

В Xcode 6.0.1 contentView для UICollectionViewCell не работает для устройств iOS7. Это также можно исправить, добавив надлежащие ограничения в UICollectionViewCell и его contentView в awakeFromNib или методы init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];

Ответы в этом посте работают, но я так и не понял, почему это работает.

Во-первых, есть два "правила":

  1. Для представлений, созданных программно (напр. [UIView new]), недвижимость translatesAutoresizingMaskIntoConstraints установлен в YES
  2. Представления, созданные в конструкторе интерфейсов с включенной функцией AutoLayout, будут иметь свойство translatesAutoresizingMaskIntoConstraints установлен в NO

Второе правило, похоже, не применяется к представлениям верхнего уровня, для которых вы не определяете ограничения. (Напр. Просмотр содержимого)

При взгляде на ячейку раскадровки обратите внимание, что в ячейке нет contentView подвергаются. Мы не "контролируем" contentView Яблоко есть.

Погрузитесь в исходный код раскадровки и посмотрите, как contentView ячейка определяется:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Теперь подвиды клетки (обратите внимание на translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

contentView не имеет это translatesAutoresizingMaskIntoConstraints установлен в NO, Кроме того, в нем отсутствует определение макета, возможно из-за того, что сказал @ecotax.

Если мы посмотрим на contentView, у него есть маска авторазмера, но нет определения для нее: <autoresizingMask key="autoresizingMask"/>

Итак, есть два вывода:

  1. contentViewtranslatesAutoresizingMaskIntoConstraints установлен в YES,
  2. contentView не хватает определения макета.

Это приводит нас к двум решениям, о которых говорилось.

Вы можете установить маски авторазмера вручную в awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Или вы можете установить contentViewtranslatesAutoresizingMaskIntoConstraints в NO в awakeFromNib и определить ограничения в - (void)updateConstraints,

Это Swift-версия ответа @Igor, которая принята, и спасибо за приятный ответ.

Первый Перейти к вашему UICollectionViewCell Подкласс и вставьте следующий код, как он есть внутри класса.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

Кстати, я использую Xcode 7.3.1 и Swift 2.3. Решение протестировано на iOS 9.3, которая работает без нареканий.

Спасибо, надеюсь, это помогло.

Это не будет работать правильно без каких-либо других упомянутых обходных путей из-за ошибки в Xcode 6 GM, когда Xcode компилирует файлы xib в формат nib. Хотя я не могу с уверенностью сказать, что это связано с XCode и не связано со временем выполнения, я очень уверен - вот как я могу это показать:

  1. Построить + запустить приложение в Xcode 5.1.
  2. Перейдите в каталог приложения симулятора и скопируйте скомпилированный файл.nib для xib, с которой у вас возникли проблемы.
  3. Построить + запустить приложение в Xcode 6 GM.
  4. Остановите приложение.
  5. Замените файл.nib в папке симулятора недавно созданного приложения на файл.nib, созданный с помощью Xcode 5.1
  6. Перезапустите приложение из симулятора, а не из Xcode.
  7. Ваша ячейка, загруженная из этого.nib, должна работать как положено.

Я надеюсь, что каждый, кто читает этот вопрос, подаст радар в Apple. Это ОГРОМНАЯ проблема, и ее необходимо устранить до окончательного выпуска XCode.

Изменить: В свете поста ecotax, я просто хотел обновить это, чтобы сказать, что теперь подтверждены различия в поведении между сборкой в iOS 8 против iOS 7, но не ошибка. Мой хак исправил проблему, потому что сборка на iOS 7 добавила маску автоматического изменения размера к представлению контента, необходимому для этой работы, который Apple больше не добавляет.

В swift поместите следующий код в подкласс ячейки представления коллекции:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}

Я исправил это:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

см: здесь

Я обнаружил, что есть также проблемы с contentView Определение размера в iOS 8. Это имеет тенденцию выкладываться очень поздно в цикле, что может вызвать временные конфликты ограничений. Чтобы решить эту проблему, я добавил следующий метод в категорию UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Этот метод следует вызывать после удаления ячейки.

Просто убедитесь, что вы отметили флажок "Autoresize subviews" в кончике для этой ячейки представления коллекции. Он будет отлично работать как на iOS 8, так и на iOS 7.

Другие вопросы по тегам