質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

4169閲覧

UICollectionViewDelegateFlowLayoutで変更したセルのサイズがスクロールすると変化する

yousei

総合スコア7

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2017/04/05 07:11

編集2017/04/05 08:09

前提・実現したいこと

UICollectionViewで1行目は横幅一杯に1枚、2行目以降は1行に2枚画像を表示する画面を開発しています。

イメージ説明

発生している問題・エラーメッセージ

初回表示はされるですが、画面を下にスクロールし、再度一番上に戻ると1行目の横幅が半分になり、途中の行の片方の画像の横幅が広がってしまいます。スクロールしても最初に表示された状態を保つためにはどのようにすべきでしょうか。

イメージ説明

###該当のソースコード

PagingMenuControllerを使用しており、メインのビューコントロラーからimageUrlListをセットしてMyViewControllerを呼び出しています

swift

1class MyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 2 3 var imageUrlList: Array<String>! 4 5 override func loadView() { 6 super.loadView() 7 // Do any additional setup after loading the view, typically from a nib. 8 9 let myCollectionView = UICollectionView(frame: CGRect(origin: CGPoint.zero, size: self.view.frame.size), collectionViewLayout: UICollectionViewFlowLayout()) 10 myCollectionView.backgroundColor = UIColor.white 11 myCollectionView.register(MyCollection.self, forCellWithReuseIdentifier: "MyCell") 12 13 myCollectionView.delegate = self 14 myCollectionView.dataSource = self 15 16 self.view.addSubview(myCollectionView) 17 } 18 19 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 20 if indexPath.row == 0 { 21 return CGSize(width: collectionView.frame.width, height: collectionView.frame.height * 0.3) 22 } 23 24 return CGSize(width: collectionView.frame.width * 0.5, height: collectionView.frame.height * 0.3) 25 } 26 27 28 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 29 30 let collection = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCollection 31 let url = imageUrlList[indexPath.row] 32 33 let thumbnail = collection.contentView.viewWithTag(MyCollection.TAG_THUMBNAIL) as! ThumbnailImageView 34 thumbnail.loadImage(urlString: url) 35 36 return collection 37 } 38} 39 40class MyCollection: UICollectionViewCell { 41 42 static let TAG_THUMBNAIL: Int = 0 43 44 required init(coder aDecoder: NSCoder) { 45 super.init(coder: aDecoder)! 46 } 47 48 override init(frame: CGRect) { 49 super.init(frame: frame) 50 51 initView(width: frame.size.width, height: frame.size.height) 52 } 53 54 func initView(width: CGFloat, height: CGFloat) { 55 56 let thumbnail: ThumbnailImageView = ThumbnailImageView(frame: CGRect(x: 0, y: 0, width: width, height: height * 0.3)) 57 thumbnail.tag = MyCollection.TAG_THUMBNAIL 58 self.contentView.addSubview(thumbnail) 59 } 60} 61 62class ThumbnailImageView: UIImageView { 63 64 let CACHE_SEC: TimeInterval = 60 * 60 * 24 // 1 day 65 66 var baseWidth: CGFloat = 0 67 var baseHeight: CGFloat = 0 68 69 required init(coder aDecoder: NSCoder) { 70 super.init(coder: aDecoder)! 71 } 72 73 override init (frame: CGRect) { 74 super.init(frame: frame) 75 } 76 77 func loadImage(urlString: String) { 78 self.alpha = 0 79 let req = URLRequest(url: NSURL(string: urlString)! as URL, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: CACHE_SEC); 80 let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: OperationQueue.main); 81 82 session.dataTask(with: req, completionHandler: { (data, resp, err) in 83 if ((err) == nil) { 84 85 // Success 86 self.image = UIImage(data: data!); 87 88 UIView.animate(withDuration: 1.0) { () -> Void in 89 self.alpha = 1 90 } 91 92 } else { 93 // TODO: show default if error 94 print("AsyncImageView:Error \(err?.localizedDescription)"); 95 } 96 }).resume(); 97 } 98}

###試したこと

セルのサイズを変更しているcollectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSizeの引数のindexPathが描画したセルの順番と合っていないのかと疑いブレークポイントを貼ってデバッグしましたが、スクロール時にブレークポイントに引っかかりませんでした。

###補足情報(言語/FW/ツール等のバージョンなど)

  • Swift 3
  • xcode 8.2.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2017/04/05 07:44

collection​View(_:​layout:​size​For​Item​At:​)のコードに問題はありません。原因は他にあります。
yousei

2017/04/05 08:10

ご確認頂きありがとう御座います。当該のビューコントローラの他の処理を追記しました。
guest

回答2

0

ベストアンサー

CollectionViewはセルを再利用しますので、ちゃんと初期化し直さないと前回表示した状態が残ってしまいます。
今回の件は、ThumbnailImageViewのframeが最初に生成した時に設定した時のままなためかと思います。

例えば1番目のセルを再度表示した時に画像が半分になるのは、(例えば)6番目のセルを表示した時の大きさのままなためです。
同じように途中のセルが横幅いっぱいになるのは、最初に1番目のセルを表示した時の大きさのまま(MyCollectionをはみ出している)ためです。

対応策は2つあります。
どちらを使用するかは場合によります。1行目のセルとそれ以外は横幅以外に違いがないなら対応策1の方が簡単かと思いますが。

対応策1

どうやらThumbnailImageViewはセルの横幅いっぱいになればいいみたいですから、autoresizingMaskを設定する。
つまり、MyCollection のinitView(width:, height:)

thumbnail.autoresizingMask = [.flexible​Width]

を追加する。
(コードは試してないのでエラーが出るかもです)

対応策2

1行目とそれ以外でセルのidentifierを別にして、再利用する際に横幅いっぱいのセルと半分のセルを混在させなくする。

具体的には、

override func loadView() { // ...前略 myCollectionView.backgroundColor = UIColor.white myCollectionView.register(MyCollection.self, forCellWithReuseIdentifier: "MyCell") myCollectionView.register(MyCollection.self, forCellWithReuseIdentifier: "MyCellTop") // <- この行を追加 // 以下略... } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let identifier = indexPath.row == 0 ? "MyCellTop" : "MyCell" let collection = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! MyCollection // 以下略... }

とすればとりあえず動かすことはできるかと思います。
(例によって動作確認はしてないのでエラーが出たら適宜修正してください)

蛇足

内容とは関係ないですが、static let TAG_THUMBNAIL: Int = 0となっていますが、UIViewのtagは0がデフォルト値なので、0をセットしても意味がありません。
1以上の値にした方がいいかと思います。

投稿2017/04/05 12:42

編集2017/04/05 13:23
u39ueda

総合スコア950

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yousei

2017/04/05 12:49

ご回答頂きありがとう御座います。対応策2で解決することができました!
guest

0

MyCollectionのinitView(width:height:)の中で

swift

1self.contentView.addSubview(thumbnail)

としていますが、これではthumbnailがどんどん積み重なっていきます。
(再利用されたセルは初期化されるわけではありません)

とりあえずの修正方法として、addする前にremoveしてみます。

swift

1self.contentView.viewWithTag(MyCollection.TAG_THUMBNAIL)?.removeFromSuperview()

他の方法としては、すでにaddされていたらaddせずに最小限の初期化だけ行う、などがあります。

おまけ

画像を裏で読み込んでいるようですが、読み込み中にセルが画面外に出たときの処理(読み込みキャンセルなど)を作らないと、高速にスクロールしたときに表示が乱れるような気がします。
(追記)UIImageViewの中で読み込み処理を行っているので、上に書いたremoveする方法であれば、表示は乱れないですね‥たぶん)

投稿2017/04/05 08:42

編集2017/04/05 08:49
fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yousei

2017/04/05 08:53

ご回答頂きありがとう御座います。ロード前にremoveFromSuperviewするようにしました。スクロール時に画像の乱れは出なくなりましたが、変わらずセルの横幅は崩れるようです。
fuzzball

2017/04/05 09:06

let thumbnail: ThumbnailImageView = ThumbnailImageView(frame:...) の代わりに、 let thumbnail = UIImageView(frame:...) thumbnail.backgroundColor = 適当な色 として、素のUIImageViewを使うとどうなりますか?
yousei

2017/04/05 10:01

試してみましたが、やはりセルの横幅が崩れます。
fuzzball

2017/04/05 10:13 編集

セルのcontentViewのbackgroundColorに色を付けて試してもらえますか?画像が小さくなっているだけかも。
yousei

2017/04/05 11:09

contentView.backgroundColorを設定してみましたが、1行目の右のセル(空白?)は背景色が表示されました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問