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

158

Я использую 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 с закругленным углом.

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

Однако в iOS 7 все дочерние элементы внутри ячейки внезапно сжимаются до кадра (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 (проверьте ответ 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 **/
thkeen
источник
1
Я обнаружил, что эта проблема все еще существует в Xcode 6 Beta 5. Кто-нибудь тоже испытывал это?
thkeen
2
Я борюсь с этим прямо сейчас в моем проекте. iOS 7 и iOS 8, созданные с использованием Xcode 5, выглядят хорошо. iOS 8, созданная с использованием Xcode 6 beta 6, выглядит отлично. iOS 7, созданная с использованием Xcode 6 beta 6, испытывает проблему, которую вы описываете. Используя Reveal, я вижу, что мой UICollectionViewCell имеет правильный размер. Но размер содержимого viewView ячейки не изменился, хотя UICollectionViewCell и его родительский элемент имеют функцию Autoresize Subviews. Размер contentView установлен на то, что имеет раскадровка. Я не использую autolayout в этом проекте. Мой проект полностью объективен.
Дель Браун
2
Я только хотел добавить, что эта проблема все еще существует в Xcode 6 / iOS 8 GM seed. Ответ @ DanielPlamann для принудительного contentViewизменения размера ячейки работает нормально, чтобы обойти проблему. Я предполагаю, что в iOS 8 Apple что-то изменила в способе обработки представлений содержимого ячейки при создании в Интерфейсном Разработчике (который в любом случае все еще является чем-то вроде черного ящика). Но тот факт, что он изменяет поведение при настройке на iOS 7, безусловно, является ошибкой.
Стюарт
То же самое здесь с Xcode 6 GM, автоматическим макетом и ячейкой на основе пера. Я исправляю это, прикрепляя contentViewкрая к краям ячейки.
sergiou87
3
Я скачал xcode 6.1, но все еще вижу ту же проблему в симуляторе.
Хайтао Ли

Ответы:

169

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]
}
Игорь Палагута
источник
3
Сделал трюк без self.contentView.frame = self.bounds; Думаешь мне это нужно? Спасибо, в любом случае!
Михал Шац
Привет Михал, согласен с тобой. Но на 100%, чтобы быть уверенным, как это будет работать в следующих iOS, лучше добавить его, я думаю.
Игорь Палагута
5
Мне нравится этот ответ, но вам нужно позвонить [super awakeFromNib]; Кроме того, я думаю о добавлении проверки для iOS 7.1 и менее, потому что я не уверен, как добавление этих масок изменения размера влияет на поведение по умолчанию на iOS8.
GingerBreadMane
Если вы не используете перья или раскадровки, это также работает, если вы поместите его в applyLayoutAttributes:
cetcet
Это не работает для меня. Я не использую Autolayout !! Любая помощь?
thatzprem
61

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

ecotax
источник
Это интересно, но я все еще надеюсь, что они это исправят. Такое поведение было введено только в Xcode 6 GM, который добавил поддержку iPhone 6. Это работало нормально в более ранних бета-версиях. Я даже вернул проект к более ранней бета-версии после того, как заметил это, и он работал как ожидалось. Я надеюсь, что все сообщают об ошибках в Apple по этому вопросу.
Артон
@arton Я подал отчет об ошибке в тот же день, когда обратился в DTS за помощью. Он был закрыт как Дубликат 18312246. Не уверен, насколько это помогает.
экотакс
Я использовал обходной путь путем определения размера ContentView и работает для меня. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Хесус Уртадо
60

Я столкнулся с той же проблемой и надеюсь, что 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;
  }
}
Даниэль Пламанн
источник
Да, я использовал похожую работу, но я сделал это в cellForItem / cellForRow, который также работает.
Чт
1
Это лучшее решение. Добавление этого в cellForItem / cellForRow вызовет странные артефакты, если вы поворачиваете устройство и размер ячейки изменяется.
Spybart
Здравствуй! У меня проблема с этим кодом. Впервые логическое contentViewIsAutoresized будет иметь значение true, если оно загружено из раскадровки или ячейки прототипа. Только когда вы делаете reloadData, второе будет правильным. Так что вам не нужно проверять размер. Вместо этого просто сделайте: self.contentView.frame = self.bounds;
14:10
Подтверждено: мы должны сделать это в cellForItem или cellForRow, потому что layoutSubviews вызывается только после возврата ячейки. Все, что вы делаете до этого, например, рисование, будет неправильно рассчитано.
14:18
1
Хм, может быть, лучше поместить [cell layoutIfNeeded];в cellForItem или cellForRow?
wtorsi
38

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

KoCMoHaBTa
источник
2
Это превосходное решение, так как оно работает с любым типом ячеек без подклассов и не требует изменений в нескольких местах, когда вы используете более одного типа ячеек в представлении коллекции.
14:00
6

В 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)]];
SerJ_G
источник
Только это работает сейчас, вы должны сделать это только для IOS 8, и я должен вызывать его после каждой очереди! Но это также не идеальное решение, потому что множественный выбор не работает так, как должен визуально ...
Renetik
Лучше всего использовать autolayout действительно, с этой ошибкой вы экономите время, просто используя событие
autolayout
Маска не работает для меня; Однако установка ограничений работает!
энтропид
4

Это не будет работать правильно без каких-либо других упомянутых обходных путей из-за ошибки в 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 больше не добавляет.

Тузик
источник
Я не уверен, могут ли они на законных основаниях выпускать другой код xCode, чем версия GM
Mabedan
Хаха, они пойдут в тюрьму? = P Но на полном серьезе, конечно, они могут. У них было несколько версий GM для Mavericks, если вы не помните. Это не слишком часто, но это может случиться.
Эйси
4

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

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

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

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

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

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

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

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

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

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

kgaidis
источник
4

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

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

по окончании
источник
извините за мой плохой английский, я имел в виду "работает как шарм" :)
Aznix
@Aznix Нет проблем .. :)
onCompletion
2

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

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ян
источник
Это был ответ, который я искал. Я использовал переопределить setBounds в Objective-C, чтобы исправить эту проблему (не был уверен, как написать это в Swift). Спасибо :)
Мэтью Коули
1

Я обнаружил, что 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
}

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

phatmann
источник
Было бы идеально, если бы этот метод вызывался автоматически. Я не люблю это, но это может быть сделано путем мошенничества dequeueReusableCellWithReuseIdentifier:forIndexPath:.
phatmann
Apple, кажется, исправила эту проблему в версии 8.1 SDK для iOS в Xcode 6.1 GM (сборка 6A1042b). Поэтому я обновил код выше, чтобы не запускаться при использовании 8.1 SDK. Как только ваша команда перешла на Xcode 6.1, вы можете полностью удалить этот хак.
phatmann
1

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

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

см: здесь

Serluca
источник
-1

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

Дипак Г.М.
источник
Нет, это не так. Это встроенный прототип ячейки.
thkeen