Это связано с, но отличный от в использовании макета потока, или настроить?.
Вот иллюстрация того, что я пытаюсь сделать:
Я интересно, если я могу сделать это с UICollectionViewFlowLayout`, его производному, или если мне нужно создать полностью настраиваемый макет? Основана на конференции WWDC 2012 видео на UICollectionView, кажется, что если вы используете макет потока с вертикальной прокруткой, ваши линии разметки горизонтальных, и если вы прокрутите по горизонтали, линий разметки по вертикали. Я хочу, чтобы горизонтальные линии разметки в горизонтальной прокруткой, просмотреть коллекции.
Я также не имеют каких-либо частей в моей модели - это всего лишь один набор элементов. Я могу сгруппировать их в разделы, а ознакомиться с коллекцией можно изменять, так что количество элементов, которое может поместиться на странице будет меняться иногда, и кажется, что выбор, на какой странице каждого товара идет лучше оставить на макет, чем на модели, если у меня нет каких-либо значимых разделов.
Итак, я могу сделать это с макета потока, или мне нужно создать пользовательский макет?
Здесь я делюсь своими простая реализация!
Интернет .H-файл:
/**
* CollectionViewLayout for an horizontal flow type:
*
* | 0 1 | 6 7 |
* | 2 3 | 8 9 | ----> etc...
* | 4 5 | 10 11 |
*
* Only supports 1 section and no headers, footers or decorator views.
*/
@interface HorizontalCollectionViewLayout : UICollectionViewLayout
@property (nonatomic, assign) CGSize itemSize;
@end
Интернет .файл м:
@implementation HorizontalCollectionViewLayout
{
NSInteger _cellCount;
CGSize _boundsSize;
}
- (void)prepareLayout
{
// Get the number of cells and the bounds size
_cellCount = [self.collectionView numberOfItemsInSection:0];
_boundsSize = self.collectionView.bounds.size;
}
- (CGSize)collectionViewContentSize
{
// We should return the content size. Lets do some math:
NSInteger verticalItemsCount = (NSInteger)floorf(_boundsSize.height / _itemSize.height);
NSInteger horizontalItemsCount = (NSInteger)floorf(_boundsSize.width / _itemSize.width);
NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
NSInteger numberOfItems = _cellCount;
NSInteger numberOfPages = (NSInteger)ceilf((CGFloat)numberOfItems / (CGFloat)itemsPerPage);
CGSize size = _boundsSize;
size.width = numberOfPages * _boundsSize.width;
return size;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// This method requires to return the attributes of those cells that intsersect with the given rect.
// In this implementation we just return all the attributes.
// In a better implementation we could compute only those attributes that intersect with the given rect.
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:_cellCount];
for (NSUInteger i=0; i<_cellCount; ++i)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
UICollectionViewLayoutAttributes *attr = [self _layoutForAttributesForCellAtIndexPath:indexPath];
[allAttributes addObject:attr];
}
return allAttributes;
}
- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [self _layoutForAttributesForCellAtIndexPath:indexPath];
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
// We should do some math here, but we are lazy.
return YES;
}
- (UICollectionViewLayoutAttributes*)_layoutForAttributesForCellAtIndexPath:(NSIndexPath*)indexPath
{
// Here we have the magic of the layout.
NSInteger row = indexPath.row;
CGRect bounds = self.collectionView.bounds;
CGSize itemSize = self.itemSize;
// Get some info:
NSInteger verticalItemsCount = (NSInteger)floorf(bounds.size.height / itemSize.height);
NSInteger horizontalItemsCount = (NSInteger)floorf(bounds.size.width / itemSize.width);
NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
// Compute the column & row position, as well as the page of the cell.
NSInteger columnPosition = row%horizontalItemsCount;
NSInteger rowPosition = (row/horizontalItemsCount)%verticalItemsCount;
NSInteger itemPage = floorf(row/itemsPerPage);
// Creating an empty attribute
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame = CGRectZero;
// And finally, we assign the positions of the cells
frame.origin.x = itemPage * bounds.size.width + columnPosition * itemSize.width;
frame.origin.y = rowPosition * itemSize.height;
frame.size = _itemSize;
attr.frame = frame;
return attr;
}
#pragma mark Properties
- (void)setItemSize:(CGSize)itemSize
{
_itemSize = itemSize;
[self invalidateLayout];
}
@end
И наконец, если вы хотите, чтобы поведение пронумерован, вам просто нужно настроить свой UICollectionView:
_collectionView.pagingEnabled = YES;
Надеялся, что будет достаточно полезно.
Преобразованный код vilanovi к SWIFT в случае если кто-то нуждается в будущем.
public class HorizontalCollectionViewLayout : UICollectionViewLayout {
private var cellWidth = 90 // Don't kow how to get cell size dynamically
private var cellHeight = 90
public override func prepareLayout() {
}
public override func collectionViewContentSize() -> CGSize {
let numberOfPages = Int(ceilf(Float(cellCount) / Float(cellsPerPage)))
let width = numberOfPages * Int(boundsWidth)
return CGSize(width: CGFloat(width), height: boundsHeight)
}
public override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for (var i = 0; i < cellCount; ++i) {
let indexPath = NSIndexPath(forRow: i, inSection: 0)
let attr = createLayoutAttributesForCellAtIndexPath(indexPath)
allAttributes.append(attr)
}
return allAttributes
}
public override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
return createLayoutAttributesForCellAtIndexPath(indexPath)
}
public override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
private func createLayoutAttributesForCellAtIndexPath(indexPath:NSIndexPath)
-> UICollectionViewLayoutAttributes {
let layoutAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
layoutAttributes.frame = createCellAttributeFrame(indexPath.row)
return layoutAttributes
}
private var boundsWidth:CGFloat {
return self.collectionView!.bounds.size.width
}
private var boundsHeight:CGFloat {
return self.collectionView!.bounds.size.height
}
private var cellCount:Int {
return self.collectionView!.numberOfItemsInSection(0)
}
private var verticalCellCount:Int {
return Int(floorf(Float(boundsHeight) / Float(cellHeight)))
}
private var horizontalCellCount:Int {
return Int(floorf(Float(boundsWidth) / Float(cellWidth)))
}
private var cellsPerPage:Int {
return verticalCellCount * horizontalCellCount
}
private func createCellAttributeFrame(row:Int) -> CGRect {
let frameSize = CGSize(width:cellWidth, height: cellHeight )
let frameX = calculateCellFrameHorizontalPosition(row)
let frameY = calculateCellFrameVerticalPosition(row)
return CGRectMake(frameX, frameY, frameSize.width, frameSize.height)
}
private func calculateCellFrameHorizontalPosition(row:Int) -> CGFloat {
let columnPosition = row % horizontalCellCount
let cellPage = Int(floorf(Float(row) / Float(cellsPerPage)))
return CGFloat(cellPage * Int(boundsWidth) + columnPosition * Int(cellWidth))
}
private func calculateCellFrameVerticalPosition(row:Int) -> CGFloat {
let rowPosition = (row / horizontalCellCount) % verticalCellCount
return CGFloat(rowPosition * Int(cellHeight))
}
}
Вы'вновь направо – что's не как акции горизонтальной прокруткой, просмотреть коллекции располагаются ячейки. Я'м боюсь, что вы'вновь придется реализовать свой собственный UICollectionViewLayout
подкласс. Либо так, либо отдельные модели на секции.
Ранее реализация была не полной, багги, а также с фиксированным размером ячейки. Здесь's более буквальном переводе код:
import UIKit
class HorizontalFlowLayout: UICollectionViewLayout {
var itemSize = CGSizeZero {
didSet {
invalidateLayout()
}
}
private var cellCount = 0
private var boundsSize = CGSizeZero
override func prepareLayout() {
cellCount = self.collectionView!.numberOfItemsInSection(0)
boundsSize = self.collectionView!.bounds.size
}
override func collectionViewContentSize() -> CGSize {
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let numberOfItems = cellCount
let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))
var size = boundsSize
size.width = CGFloat(numberOfPages) * boundsSize.width
return size
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for var i = 0; i < cellCount; i++ {
let indexPath = NSIndexPath(forRow: i, inSection: 0)
let attr = self.computeLayoutAttributesForCellAtIndexPath(indexPath)
allAttributes.append(attr)
}
return allAttributes
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
return self.computeLayoutAttributesForCellAtIndexPath(indexPath)
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
func computeLayoutAttributesForCellAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes {
let row = indexPath.row
let bounds = self.collectionView!.bounds
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let columnPosition = row % horizontalItemsCount
let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
let attr = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
var frame = CGRectZero
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
frame.origin.y = CGFloat(rowPosition) * itemSize.height
frame.size = itemSize
attr.frame = frame
return attr
}
}
Свифт 4 Код#:
public class HorizontalFlowLayout: UICollectionViewLayout {
var itemSize = CGSize(width: 0, height: 0) {
didSet {
invalidateLayout()
}
}
private var cellCount = 0
private var boundsSize = CGSize(width: 0, height: 0)
public override func prepare() {
cellCount = self.collectionView!.numberOfItems(inSection: 0)
boundsSize = self.collectionView!.bounds.size
}
public override var collectionViewContentSize: CGSize {
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let numberOfItems = cellCount
let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))
var size = boundsSize
size.width = CGFloat(numberOfPages) * boundsSize.width
return size
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for i in 0...(cellCount-1) {
let indexPath = IndexPath(row: i, section: 0)
let attr = self.computeLayoutAttributesForCellAt(indexPath: indexPath)
allAttributes.append(attr)
}
return allAttributes
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return computeLayoutAttributesForCellAt(indexPath: indexPath)
}
public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
private func computeLayoutAttributesForCellAt(indexPath:IndexPath)
-> UICollectionViewLayoutAttributes {
let row = indexPath.row
let bounds = self.collectionView!.bounds
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let columnPosition = row % horizontalItemsCount
let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
frame.origin.y = CGFloat(rowPosition) * itemSize.height
frame.size = itemSize
attr.frame = frame
return attr
}
}
Это Свифт 3 версии @GuilhermeSprint ответить Код#:
public class HorizontalCollectionViewLayout : UICollectionViewLayout {
var itemSize = CGSize(width: 0, height: 0) {
didSet {
invalidateLayout()
}
}
private var cellCount = 0
private var boundsSize = CGSize(width: 0, height: 0)
public override func prepare() {
cellCount = self.collectionView!.numberOfItems(inSection: 0)
boundsSize = self.collectionView!.bounds.size
}
public override var collectionViewContentSize: CGSize {
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let numberOfItems = cellCount
let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))
var size = boundsSize
size.width = CGFloat(numberOfPages) * boundsSize.width
return size
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for i in 0...(cellCount-1) {
let indexPath = IndexPath(row: i, section: 0)
let attr = self.computeLayoutAttributesForCellAt(indexPath: indexPath)
allAttributes.append(attr)
}
return allAttributes
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return computeLayoutAttributesForCellAt(indexPath: indexPath)
}
public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
private func computeLayoutAttributesForCellAt(indexPath:IndexPath)
-> UICollectionViewLayoutAttributes {
let row = indexPath.row
let bounds = self.collectionView!.bounds
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let columnPosition = row % horizontalItemsCount
let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
var frame = CGRectMake(0, 0, 0, 0)
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
frame.origin.y = CGFloat(rowPosition) * itemSize.height
frame.size = itemSize
attr.frame = frame
return attr
}
}
// I want to have 4 items in the page / see screenshot below
let itemWidth = collectionView.frame.width / 2.0
let itemHeight = collectionView.frame.height / 2.0
let horizontalCV = HorizontalCollectionViewLayout();
horizontalCV.itemSize = CGSize(width: itemWidth, height: itemHeight)
collectionView.collectionViewLayout = horizontalCV
extension MyViewController : UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
// Delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Clicked")
}
// DataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BottomMenuCCell.xib, for: indexPath) as? BottomMenuCCell {
cell.ibi = bottomMenuButtons[indexPath.row]
cell.layer.borderWidth = 0
return cell
}
return BaseCollectionCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: (collectionView.width / 2.0), height: collectionView.height / 2.0)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return bottomMenuButtons.count
}
// removing spacing between items
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
@interface HorizontalCollectionViewLayout : UICollectionViewFlowLayout
@end
@implementation HorizontalCollectionViewLayout
- (instancetype)init
{
self = [super init];
if (self) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.minimumLineSpacing = 0;
self.minimumInteritemSpacing = 0;
}
return self;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributesArray = [super layoutAttributesForElementsInRect:rect];
NSInteger verticalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.height / self.itemSize.height);
NSInteger horizontalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.width / self.itemSize.width);
NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
for (NSInteger i = 0; i < attributesArray.count; i++) {
UICollectionViewLayoutAttributes *attributes = attributesArray[i];
NSInteger currentPage = (NSInteger)floor((double)i / (double)itemsPerPage);
NSInteger currentRow = (NSInteger)floor((double)(i - currentPage * itemsPerPage) / (double)horizontalItemsCount);
NSInteger currentColumn = i % horizontalItemsCount;
CGRect frame = attributes.frame;
frame.origin.x = self.itemSize.width * currentColumn + currentPage * self.collectionView.bounds.size.width;
frame.origin.y = self.itemSize.height * currentRow;
attributes.frame = frame;
}
return attributesArray;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
@end
Ваш последний курорт, конечно, будет использование нескольких вертикально видом коллекции внутри каждого раздела в наружной горизонтальной прокрутки вид коллекции. Помимо увеличения сложности кода и сложности в выполнении Интер-секция сотового анимации, я могу'т думаю, что серьезных проблем при таком подходе с моей головы.