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

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

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

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

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

http://i.stack.imgur.com/uDNDY.png

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

http://i.stack.imgur.com/lOZH9.png

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


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

person thkeen    schedule 15.07.2014    source источник
comment
Я обнаружил, что эта проблема все еще существует в Xcode 6 Beta 5. Кто-нибудь тоже сталкивался с этим?   -  person thkeen    schedule 13.08.2014
comment
Я сейчас борюсь с этим в своем проекте. iOS 7 и iOS 8, созданные с использованием Xcode 5, выглядят нормально. iOS 8, созданная с использованием Xcode 6 beta 6, выглядит нормально. iOS 7, созданная с использованием Xcode 6 beta 6, имеет проблему, которую вы описываете. Используя Reveal, я вижу, что мой UICollectionViewCell имеет правильный размер. Но размер contentView ячейки не был изменен, хотя у его родительского элемента UICollectionViewCell включен Autoresize Subviews. Размер contentView устанавливается равным раскадровке. В этом проекте я не использую автоматическую раскладку. Мой проект полностью объективен.   -  person Del Brown    schedule 28.08.2014
comment
Я просто хотел добавить, что эта проблема все еще существует с семенами Xcode 6 / iOS 8 GM. Ответ @ DanielPlamann, чтобы заставить contentView изменить размер с помощью ячейки, отлично работает, чтобы обойти проблему. Я предполагаю, что в iOS 8 Apple что-то изменила в способе обработки представлений содержимого ячеек при создании в Interface Builder (который в любом случае все еще является чем-то вроде черного ящика). Но тот факт, что он меняет поведение при нацеливании на iOS 7, безусловно, является ошибкой.   -  person Stuart    schedule 10.09.2014
comment
То же самое здесь с Xcode 6 GM, автоматическим макетом и ячейкой на основе пера. Я исправляю это, прикрепляя contentView края к краям ячеек.   -  person sergiou87    schedule 10.09.2014
comment
Ага, это нехорошо. Я вообще не хочу использовать этот обходной путь в производственном приложении. Кто-нибудь из вас уже отправил это сообщение об ошибке? Боюсь, они не воспринимают это достаточно серьезно.   -  person thkeen    schedule 10.09.2014
comment
@ sergiou87, как вам удалось прикрепить contentView к краям с помощью наконечника, когда UICollectionViewCells не показывает contentView в наконечнике?   -  person Acey    schedule 10.09.2014
comment
@Acey, к сожалению, это нужно делать в коде: - \ Мы используем собственный подкласс UICollectionViewCell в качестве базового класса для наших пользовательских ячеек.   -  person sergiou87    schedule 12.09.2014
comment
Что ж, на самом деле это не прискорбно, потому что, делая это таким образом, мы избегаем того, чтобы делать это для каждой ячейки: D   -  person sergiou87    schedule 12.09.2014
comment
@Acey Я только что обновил лучшее решение, независимо от того, используете ли вы перо или нет. Помещение блока кода в cellForItem - самый безопасный способ.   -  person thkeen    schedule 23.09.2014
comment
Произошло как на Xcode 6.0 (из AppStore), так и на Xcode 6.1 Beta для Yosemite. «Обновление 2» работает как шарм. Я потратил на это полдня ...   -  person Aviel Gross    schedule 23.09.2014
comment
Apple, похоже, исправила эту проблему в версии 8.1 iOS SDK в Xcode 6.1 GM (сборка 6A1042b). Я удалил хак, и размер ячеек представления моей коллекции был установлен правильно. Кто-нибудь еще может это подтвердить?   -  person phatmann    schedule 07.10.2014
comment
Вы тестировали на iOS 7?   -  person thkeen    schedule 08.10.2014
comment
Я загрузил xcode 6.1, но все еще вижу ту же проблему в симуляторе.   -  person Haitao Li    schedule 23.10.2014
comment
У меня никогда не было этой проблемы до обновления iOS 8.1, потом она начала происходить (в коде, скомпилированном с помощью 6.0.1, а также когда я перекомпилировал в 6.1). Мне пришлось добавить обходной путь, чтобы избавиться от этого.   -  person meelawsh    schedule 24.10.2014
comment
Ни одно из решений у меня до сих пор не работает !! Любая помощь?   -  person thatzprem    schedule 03.12.2014
comment
stackoverflow.com/questions/27265576/ У меня другая проблема со статической ячейкой   -  person thkeen    schedule 03.12.2014
comment
Ребята, печальная новость заключается в том, что Apple все еще оставляет эту ошибку до последней версии iOS SDK.   -  person thkeen    schedule 15.01.2015
comment
Для тех, кто сталкивается с подобной проблемой и работает с современными инструментами iOS, например Swift, ознакомьтесь с этим сообщением mydaytodo.com/build-a-no-storyboard-ios-ap   -  person cptdanko    schedule 05.01.2021


Ответы (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]
}
person Igor Palaguta    schedule 10.09.2014
comment
Сделал трюк без self.contentView.frame = self.bounds; Думаешь, мне это нужно? Спасибо, в любом случае! - person Michal Shatz; 21.09.2014
comment
Привет Михал, согласен с тобой. Но для 100% уверенности, как это будет работать в следующих iOS, лучше добавить, я думаю. - person Igor Palaguta; 21.09.2014
comment
Мне нравится этот ответ, но вам нужно вызвать [super awakeFromNib]; Кроме того, я подумываю добавить проверку для iOS 7.1 и ниже, потому что я не уверен, как добавление этих масок изменения размера влияет на поведение по умолчанию на iOS8. - person GingerBreadMane; 23.09.2014
comment
Если вы не используете перья или раскадровки, это также работает, если вы поместите их в applyLayoutAttributes: - person cetcet; 26.09.2014
comment
У меня это не работает. Я не использую Autolayout !! Любая помощь? - person thatzprem; 03.12.2014
comment
Как создать ячейку? Из пера? - person Igor Palaguta; 03.12.2014
comment
Мы наблюдали проблему с ios 8.x, из-за которой ячейка представления коллекции иногда не отображала все свои подпредставления. Это устранило наши проблемы, но это немного отличается от основного вопроса. - person ort11; 08.12.2014
comment
Я быстро добавил тот же код. Но приложение по-прежнему аварийно завершает работу и показывает ошибку плохого выполнения на layout2.estimatedItemSize = CGSize(width: 300, height: 50). Я добавил код в подкласс Cell self.contentView.frame = self.bounds; self.contentView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight. Эми одна знает, как исправить быстро - person sudheer; 30.12.2014
comment
Реальный трюк - это добавление подвидов с автоматическим изменением размера в Nib. Вы больше ничего не делаете. - person Deepak G M; 20.01.2015
comment
Что, если в таблице раскадровки есть статические ячейки? - person Mohammad Sadiq Shaikh; 19.03.2015
comment
Как еще contentView может быть сломан? - person Alper; 20.09.2018

Я столкнулся с той же проблемой и обратился за помощью к 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, поэтому я попробую их позже.

person ecotax    schedule 17.09.2014
comment
Это интересно, но я все еще надеюсь, что они это исправят. Такое поведение было введено только в Xcode 6 GM, в котором добавлена ​​поддержка iPhone 6. В более ранних бета-версиях он работал нормально. Я даже вернул проект на более раннюю бета-версию после того, как заметил это, и он работал, как ожидалось. Я надеюсь, что все сообщают об ошибках в Apple по этому поводу. - person arton; 30.09.2014
comment
@arton Я отправил отчет об ошибке в тот же день, когда обратился за помощью к DTS. Он был закрыт как Дубликат 18312246. Не уверен, насколько это помогает. - person ecotax; 01.10.2014
comment
Я использовал обходной путь, изменив размер ContentView, и он работает на меня. - (CGSize) collectionViewContentSize {вернуть CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); } - person Jesús Hurtado; 30.04.2015

Я столкнулся с той же проблемой и надеюсь, что 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;
  }
}
person Daniel Plamann    schedule 02.09.2014
comment
Да, я использовал аналогичную работу, но я сделал это в cellForItem / cellForRow, который также работает. - person thkeen; 04.09.2014
comment
Это лучшее решение. Добавление этого в cellForItem / cellForRow вызовет странные артефакты, если вы поворачиваете устройство и размер ячейки изменяется. - person spybart; 12.09.2014
comment
Привет! У меня проблема с этим кодом. Впервые логическое значение contentViewIsAutoresized будет истинным, если оно загружено из ячейки раскадровки или прототипа. Только когда сделаешь reloadData, второй будет правильно. Так что вам действительно не нужно проверять размер. Вместо этого просто выполните: self.contentView.frame = self.bounds; - person thkeen; 18.09.2014
comment
Подтверждено: мы должны сделать это в cellForItem или cellForRow, потому что layoutSubviews вызывается только после возврата ячейки. Все, что вы делаете до этого, например, рисуете, будет неправильно рассчитано. - person thkeen; 18.09.2014
comment
Хм, может лучше поместить [cell layoutIfNeeded]; в cellForItem или cellForRow? - person wtorsi; 22.09.2014

Другое решение - установить размер 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;
}

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

person KoCMoHaBTa    schedule 10.09.2014
comment
Это превосходное решение, поскольку оно работает с любым типом ячеек без подкласса и не требует изменений в нескольких местах, когда вы используете более одного типа ячеек в своем представлении коллекции. - person nacross; 14.10.2014

В 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)]];
person SerJ_G    schedule 15.10.2014
comment
Только сейчас это работает, вы должны делать это только для iOS 8, и я должен вызывать это после каждого удаления из очереди! Но это тоже не идеальное решение, потому что множественное выделение не работает так, как должно визуально ... - person Renetik; 02.01.2015
comment
Лучше всего использовать автопрокладку на самом деле, с этой ошибкой вы, по иронии судьбы, экономите время, используя событие автопрокладки в простом случае .. - person Renetik; 02.01.2015
comment
У меня маска не работает; однако установка ограничений работает! - person entropid; 18.03.2015

Это не будет работать правильно без каких-либо других упомянутых обходных путей из-за ошибки в Xcode 6 GM с тем, как Xcode компилирует файлы xib в формат пера. Хотя я не могу сказать со 100% уверенностью, что это связано с 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 больше не добавляет.

person Acey    schedule 10.09.2014
comment
Я не уверен, могут ли они по закону выпустить другой xCode, чем версия GM - person Mabedan; 16.09.2014
comment
Ха-ха, они сядут в тюрьму? = P Но если серьезно, конечно, могут. У них было несколько GM-версий для Mavericks, если вы не помните. Это не так уж часто, но бывает. - person Acey; 16.09.2014

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

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

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

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

Глядя на ячейку раскадровки, обратите внимание, что в ячейке не отображается contentView. Мы не «контролируем» contentView, это Apple.

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

<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. contentView translatesAutoresizingMaskIntoConstraints установлен на YES.
  2. contentView не хватает определения макета.

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

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

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

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

person kgaidis    schedule 19.06.2015

Это 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, работает безотказно.

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

person onCompletion    schedule 15.06.2016
comment
извините за мой плохой английский, я имел ввиду работает как шарм :) - person Aznix; 23.08.2016
comment
@Aznix Нет проблем .. :) - person onCompletion; 23.08.2016

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

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
person Ian    schedule 28.11.2014
comment
Это был ответ, который я искал. Раньше я переопределял setBounds в Objective-C, чтобы исправить эту проблему (не знал, как написать это в Swift). Спасибо :) - person Matthew Cawley; 19.10.2016

Я обнаружил, что в iOS 8 также есть проблемы с размером contentView. Оно имеет тенденцию выкладываться очень поздно в цикле, что может вызвать временные конфликты ограничений. Чтобы решить эту проблему, я добавил следующий метод в категорию 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
}

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

person phatmann    schedule 03.10.2014
comment
Было бы идеально, если бы этот метод вызывался автоматически. Мне это не нравится, но это можно сделать, выпив dequeueReusableCellWithReuseIdentifier:forIndexPath:. - person phatmann; 04.10.2014
comment
Apple, похоже, исправила эту проблему в версии 8.1 iOS SDK в Xcode 6.1 GM (сборка 6A1042b). Поэтому я обновил приведенный выше код, чтобы он не запускался при использовании SDK 8.1. Как только ваша команда перейдет на Xcode 6.1, вы можете полностью удалить этот взлом. - person phatmann; 07.10.2014

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

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

см. здесь

person Serluca    schedule 03.06.2016

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

person Deepak G M    schedule 20.01.2015
comment
Нет, это не так. Это встроенный прототип ячейки. - person thkeen; 21.01.2015