Проблема автоматического изменения размера 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 с закругленным углом.
Однако в iOS 7 все дочерние элементы внутри ячейки внезапно сжимаются до фрейма (0,0,50,50) и больше не соответствуют моему правилу авторазмера.
Я предполагаю, что это ошибка в 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)]];
Ответы в этом посте работают, но я так и не понял, почему это работает.
Во-первых, есть два "правила":
- Для представлений, созданных программно (напр.
[UIView new]
), недвижимостьtranslatesAutoresizingMaskIntoConstraints
установлен вYES
- Представления, созданные в конструкторе интерфейсов с включенной функцией 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"/>
Итак, есть два вывода:
contentView
translatesAutoresizingMaskIntoConstraints
установлен вYES
,contentView
не хватает определения макета.
Это приводит нас к двум решениям, о которых говорилось.
Вы можете установить маски авторазмера вручную в awakeFromNib
:
self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Или вы можете установить contentView
translatesAutoresizingMaskIntoConstraints
в 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 и не связано со временем выполнения, я очень уверен - вот как я могу это показать:
- Построить + запустить приложение в Xcode 5.1.
- Перейдите в каталог приложения симулятора и скопируйте скомпилированный файл.nib для xib, с которой у вас возникли проблемы.
- Построить + запустить приложение в Xcode 6 GM.
- Остановите приложение.
- Замените файл.nib в папке симулятора недавно созданного приложения на файл.nib, созданный с помощью Xcode 5.1
- Перезапустите приложение из симулятора, а не из Xcode.
- Ваша ячейка, загруженная из этого.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.