問題
カスタムUICollectionViewCellを使用してUICollectionViewControllerを作成しました。カスタムセルには、大きな長方形のUIView(colorViewという名前)とUILabel(nameLabelという名前)が含まれています。
コレクションに最初にセルが入力され、colorView.frameを印刷すると、印刷されたフレームの値が正しくありません。 colorViewが正しく描画されても、colorViewフレームはセルフレーム自体よりも大きいため、それらが正しくないことはわかっています。
ただし、collectionViewを十分にスクロールして、以前に作成したセルの再利用をトリガーすると、colorView.frameの値が正しくなります!丸い角をcolorViewレイヤーに適用し、これを行うには正しいcoloViewサイズが必要なので、正しいフレームが必要です。ちなみに、不思議に思うかもしれませんが、colorView.boundsにはcolorView.frameと同じ間違ったサイズ値もあります。
質問
セルを作成するときにフレームが正しくないのはなぜですか?
そして今いくつかのコード
これは私のUICollectionViewCellです:
class BugCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var colorView: UIView!
@IBOutlet weak var nameLabel: UILabel!
}
これはUICollectionViewControllerです:
import UIKit
let reuseIdentifier = "Cell"
let colors = [UIColor.redColor(), UIColor.blueColor(),
UIColor.greenColor(), UIColor.purpleColor()]
let labels = ["red", "blue", "green", "purple"]
class BugCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return colors.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as BugCollectionViewCell
println("ColorView frame: \(cell.colorView.frame) Cell frame: \(cell.frame)")
cell.colorView.backgroundColor = colors[indexPath.row]
cell.nameLabel.text = labels[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width = self.collectionView?.frame.width
let height = self.collectionView?.frame.height
return CGSizeMake(width!, height!/2)
}
}
コレクションビューは、一度に2つのセルを垂直に表示するために設定されます。各セルには、色で塗りつぶされた大きな長方形と色の名前のラベルが含まれます。
シミュレータで上記のコードを実行すると、次の印刷結果が得られます。
ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,0.0,375.0,333.5)
ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,343.5,375.0,333.5)
奇妙な結果です-colorView.frameの高さは568ポイントですが、セルフレームの高さはわずか333.5ポイントです。
CollectionViewを下にドラッグしてセルを再利用すると、次の結果が出力されます。
ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,1030.5,375.0,333.5)
ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,343.5,375.0,333.5)
ColorViewのフレームを修正する過程で、私には理解できない何かが起こりました。
セルがNibからロードされるという事実と関係があると思うので、init(frame:frame)イニシャライザーを使用する代わりに、コントローラーはinit(coder:aCoder)イニシャライザーを使用します。作成されたものには、おそらく編集できないデフォルトフレームが付属している可能性があります。
何が起こっているのかを理解できるように、どんな助けでも感謝します!
Xcode 6.1.1を使用しています。 iOS SDK 8.1で。
次のようにカスタムCellクラスでlayoutIfNeeded()をオーバーライドすることで、セルの最終フレームを取得できます。
override func layoutIfNeeded() {
super.layoutIfNeeded()
self.subView.layer.cornerRadius = self.subView.bounds.width / 2
}
次に、UICollectionViewデータソースメソッドcellForRowAtIndexPathで:これを行います。
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.setNeedsLayout()
cell.layoutIfNeeded()
自動レイアウト制約を使用したUICollectionViewCell
でも同じ問題が発生しました。
ビューのフレーム幅に依存するサブビューを構成する前に、layoutIfNeeded
を呼び出す必要がありました。
iOS 10、Swift 3.0.1。
このメソッドをUICollectionView
サブクラスに追加します。
_override func didMoveToSuperview() {
super.didMoveToSuperview()
setNeedsLayout()
layoutIfNeeded()
}
_
私の問題は、layoutSubviews()
が呼び出されなかったため、Core Graphicsの形状が適切に計算されなかったことです。
OK、自動レイアウトがフレームを定義する前にセルが作成されることを理解しました。それが、作成の瞬間に境界が間違っている理由です。セルが再利用されるとき、フレームはすでに修正されています。
特定の座標にいくつかのレイヤーとサブビューを配置するカスタムUIViewを作成しているときに、この問題が発生していました。このUIViewのインスタンスが作成されたとき、サブビューの配置はすべて間違っていました(自動レイアウトがまだ開始されていないため)。
init(coder: aCoder)
のビューサブビューを構成する代わりに、メソッドlayoutSubviews()
をオーバーライドする必要があることがわかりました。これは、自動レイアウトが各ビューに独自のサブビューをレイアウトするように要求するときに呼び出されるため、この時点で少なくとも親ビューに正しいフレームがあり、サブビューを正しくレイアウトするために使用できます。
おそらく、フレームサイズと位置を処理する代わりにカスタムビューコードに制約を使用した場合、レイアウトは適切に行われ、layoutSubviews()
をオーバーライドする必要はないでしょう。