このような同じコード
collectionLayout.estimatedItemSize = CGSize(width: 50, height: 25)
collectionLayout.itemSize = UICollectionViewFlowLayoutAutomaticSize
collectionLayout.minimumInteritemSpacing = 10
for _ in 0 ..< 1000 {
let length = Int(arc4random() % 8)
let string = randomKeyByBitLength(length)
array.append(string!)
}
collectionView.reloadData()
セルの制約:
iOS 12で実行すると、違います。左のシミュレータはiOS 11、右のシミュレータはiOS 12です。
しかし、スクロールすると、セルのフレームは正常になります。
問題を再現するサンプルプロジェクト: https://github.com/Coeur/StackOverflow51375566
すべてのソリューションについて、reloadData
でviewDidLoad
を明示的に呼び出す必要がないことに注意してください。これは自動的に行われます。
Samantha idea :invalidateLayout
に非同期でインスパイアされたviewDidLoad
。
override func viewDidLoad() {
super.viewDidLoad()
//[...]
for _ in 0 ..< 1000 {
array.append(randomKeyByBitLength(Int(arc4random_uniform(8)))!)
}
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
(不完全、DHennessy13の改善を参照)
Peter Lapisu answer に基づいています。 invalidateLayout
in viewWillLayoutSubviews
。
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
DHennessy13で述べたように、viewWillLayoutSubviews
を使用したこの現在のソリューションは、画面を回転させるときにレイアウトを無効にするため、不完全です。
このソリューションに関して DHennessy13の改善 に従うことができます。
タイラー・シェファーの回答 、 Swiftへのショーン・アウクスタック港 とサマンサのアイデアの組み合わせに基づいています。 CollectionViewをサブクラス化して、invalidateLayout
でlayoutSubviews
を実行します。
class AutoLayoutCollectionView: UICollectionView {
private var shouldInvalidateLayout = false
override func layoutSubviews() {
super.layoutSubviews()
if shouldInvalidateLayout {
collectionViewLayout.invalidateLayout()
shouldInvalidateLayout = false
}
}
override func reloadData() {
shouldInvalidateLayout = true
super.reloadData()
}
}
このソリューションは、ViewControllerコードを変更する必要がないため、エレガントです。このサンプルプロジェクトのブランチAutoLayoutCollectionViewに実装しました https://github.com/Coeur/StackOverflow51375566/tree/AutoLayoutCollectionView 。
UICollectionViewCellのデフォルトの制約を書き換えます。 Larry answer を参照してください。
collectionView(_:layout:sizeForItemAt:)
を実装し、cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
を返します。 matt answer を参照してください。
Cœur のコードサンプルで動作し、他の回答では機能しなかった特定のケースでも機能する別のソリューションを次に示します。以下のコードは、ViewController.Swift
のCollectionViewCell
サブクラスの以前の実装を置き換えます。
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor)
let rightConstraint = contentView.rightAnchor.constraint(equalTo: rightAnchor)
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor)
let bottomConstraint = contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
NSLayoutConstraint.activate([leftConstraint, rightConstraint, topConstraint, bottomConstraint])
}
}
これは ale84 from ICollectionViewFlowLayout expectedItemSizeはiOS11では正常に動作しますがiOS12では正常に動作しません* による回答に触発されました
問題は、ここに配置されている機能(内部制約に基づいてサイズを調整するコレクションビューセル)が存在しないことです存在しない。 neverは存在していません。 Appleはそうだと主張しますが、そうではありません。コレクションビューが導入され、この主張が最初になされて以来、私は毎年これについてバグを報告しています。バグは本物だからです。自己サイズのコレクションビューセルのようなものはありません。
こちらの私の回答もご覧ください: https://stackoverflow.com/a/51585910/341994 \
数年で、自己サイズのセルを使用しようとしてクラッシュしました。他の年では、クラッシュしませんが、レイアウトが間違っています。ただし、は機能しません。
このようなことを行うonly方法は、デリゲートメソッドsizeForItemAt
を実装し、サイズyourself。簡単に呼び出すことができます
cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
事前に設定したモデルセル上。それが、ランタイムshouldがあなたのためにすべきことですが、そうではありません。
だから、ここに元の質問に対する私の解決策があります。文字列の単純な配列の代わりに、文字列とサイズのペアの配列を(タプルとして)生成しました。次に:
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCell
cell.label.text = self.array[indexPath.row].0
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.array[indexPath.row].1
}
私は同じ問題を抱えています。セルはスクロールされるまで(自動サイズの代わりに)推定サイズを使用します。 Xcode 9.xでビルドされた同じコードはiOS 11および12で完全に動作し、Xcode 10でビルドされたiOS 11では正しく動作しますが、iOS 12では動作しません。
これを解決する唯一の方法は、viewDidAppear
でコレクションビューのレイアウトを無効にすることです。これにより、多少のびびりが発生するか、viewWillAppear
内の非同期ブロックで(ソリューションの信頼性が不明です)。
override func viewDidLoad() {
super.viewDidLoad()
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = CGSize(width: 50, height: 50)
layout?.itemSize = UICollectionViewFlowLayout.automaticSize
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// The following block also "fixes" the problem without jumpiness after the view has already appeared on screen.
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// The following line makes cells size properly in iOS 12.
collectionView.collectionViewLayout.invalidateLayout()
}
Cœur のソリューション2は、レイアウトがユーザーの前で点滅したり更新したりするのを防ぎます。ただし、デバイスを回転させると問題が発生する可能性があります。 viewWillLayoutSubviewsで変数「shouldInvalidateLayout」を使用し、viewDidAppearでfalseに設定しています。
private var shouldInvalidateLayout = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
shouldInvalidateLayout = false
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if shouldInvalidateLayout {
collectionView.collectionViewLayout.invalidateLayout()
}
}
これを試して
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
viewDidAppear
およびviewWillAppear
への追加はもちろん機能します。ただし、viewDidAppear
はユーザーに誤作動を引き起こします。
私たちのプロジェクトでも同じ問題がありました。また、iOS 12の複数のデバイスの違いに気づき、layoutIfNeeded
とinvalidateLayout
の呼び出しが必要になりました。このソリューションは@ DHennessy13のアプローチに基づいていますが、少しハックのように見える状態を管理するためにブール値を必要としません。
ここでは、Rxコードに基づいています。基本的に、最初の行はデータが変更されるときです。subscribe
内は、厄介なiOS 12 UIグリッチを修正するために必要なことです。
viewModel.cellModels.asObservable()
.subscribe(onNext: { [weak self] _ in
// iOS 12 bug in UICollectionView for cell size
self?.collectionView.layoutIfNeeded()
// required for iPhone 8 iOS 12 bug cell size
self?.collectionView.collectionViewLayout.invalidateLayout()
})
.disposed(by: rx.disposeBag)
ところで、それはiOS 12の既知の問題のようです: https://developer.Apple.com/documentation/ios_release_notes/ios_12_release_notes (UIKitセクション)。