2014UICollectionViewでのCellごとのページングiOS端末の画面の上部分に横スクロールメニューを作りたいって場合、UICollectionViewを使うと割と簡単に作れる。これがただスクロールするだけのもので良いなら楽なんだけど、ユーザがスクロールする毎にセル毎にページングさせて、そのセルを画面の中心に持ってきたい、って場合があった。言葉で書くと分かりにくいので画面例を出す。以下のように横に選択肢が並んでるイメージ。%}この時に、メニュー部分を横にスクロールすると、途中で止まらずに勝手にセル毎にページング(ページングって言うのかあやしいけど)され、セルの中心が画面の中心に来るようになってほしい。ストアに出てるアプリを例に挙げるとMercariさんのような感じ。Pressoさんとかはセルをタップしたらそのセルが中心になるように動くけど、それはdidSelectItemAtIndexPath内で座標指定してスクロールするようにすればいいだけ(だと思ってる)のでまた別の話。UICollectionViewはUIScrollViewを継承しているので、pagingEnabledも設定できる。最初はこれをいじって何かしらのパラメータ変えれば良いのかなくらいに思っていたんだけど、これだとうまくいかない。YESにしても画面の横幅分のスクロールするようになるだけ。上記画面をスクロールすると、以下の状態にページングしてしまう。%}丁度さっきの画面で見切れていたMenu2の部分の途中から画面が始まる形になった。320px分右に移動した形。ScrollView側で移動幅制御できないかなとも思って色々試したんだけどそれもうまくいかず、stackoverflowでの次の質問に行き着いた。UICollectionViewFlowLayout基本はUICollectionViewFloaLayoutのサブクラスを作って、そのtargetContentOffsetForProposedContentOffsetをオーバーライドする形で実装する。このメソッドはUICollectionViewLayout.hで以下のように説明されている。behaviorつまり、スクロール後にどこに留まるかを指定するようなコード書けばよい。これはUIScrollViewのプロパティであるcontentOffsetを指定する形で書くことになる。stackoverflowの回答の場合、常に対象セルを画面左端に5px空けた上で置くようにしているけれど、今回は画面中央に置きたいので、例えば次のように実装する。proposedContentOffset.y);}上記のコードだと、proposedContentOffsetがユーザが手を離した瞬間のcontentOffsetを指す。入力としてこれ(とvelocity)を受け取り、遷移完了時に指定したいcontentOffsetを出力として返す、という流れを作っている。(ブレークポイントを打つなりなんなりするとスクロールして手を離した瞬間にこのメソッドが呼ばれていることが分かる。)kWindowWidthに画面横幅、kMenuCellWidthにセルの横幅が入っているという想定でまずhorizontalOffsetを計算している。(そのため個人的には別な名称の方が良い気がするんだけれどあまりいいのが思いつかないし参照したstackoverflowに合わせる。自然なページングとなるような)セルを選択する。具体的には、UICollectionViewFloaLayoutのlayoutAttributesForElementsInRectで画面上に表示されている部分のlayoutAttributes、すなわち各セルに関してのレイアウトの情報を、配列としてまず取得する。このレイアウト情報にはoffsetも含まれているので、horizontalOffsetとの差分を移動距離(offsetAdjustment)として計算し最小のものを選択する。後はその差分を使って最終的なoffsetを返してやれば良い。(注意点として、この記述をしたとしても、collectionViewに対してさっきのpagingEnabledをYESにしたままだとそちらの設定が優先されるようで、正しくセル毎のページングができない。)結果として、さっきのやつをセルごとにページングできるようになる。静止画じゃ分かりにくいけれど。