连续滚动查看; 水平和垂直

现在我一直在为这个任务苦苦挣扎。 我想开发的是一个scrollview或collectionview,它可以连续滚动垂直和水平。

这是我认为这应该看起来像一个形象。 透明框是从内存中重新加载的视图/单元格。 一旦视图/单元格出现在屏幕之外,就应该为即将到来的新单元格重用它。就像UITableViewController工作方式一样。

连续滚动

我知道一个UICollectionView只能制作为无限滚动横向或纵向,而不是两个。 但是,我不知道如何使用UIScrollView来做到这一点。 我试着附加在这个问题上的答案的代码,我可以得到它重新创build的意见(例如%20),但这不是我真正需要的..此外,它不是连续的。

我知道这是可能的,因为HBO Go应用程序这样做..我想完全相同的function。

我的问题:我怎样才能达到目标? 有什么指南/教程可以告诉我如何? 我找不到任何东西。

在距离中心一定距离后,使用UIScrollView重新居中的技巧,可以实现无限滚动。 首先,你需要让contentSize足够大,以便可以滚动一点,所以我返回4倍的我的部分中的项目数和4倍的部分数,并使用mod运算符在cellForItemAtIndexPath方法来获得正确的索引到我的数组中。 然后,您必须重写UICollectionView的子类中的UICollectionView以进行重新UICollectionView (这在WWDC 2011video“高级滚动视图技术”中进行了演示)。 以下是具有集合视图(在IB中设置)作为子视图的控制器类:

 #import "ViewController.h" #import "MultpleLineLayout.h" #import "DataCell.h" @interface ViewController () @property (weak,nonatomic) IBOutlet UICollectionView *collectionView; @property (strong,nonatomic) NSArray *theData; @end @implementation ViewController - (void)viewDidLoad { self.theData = @[@[@"1",@"2",@"3",@"4",@"5"], @[@"6",@"7",@"8",@"9",@"10"],@[@"11",@"12",@"13",@"14",@"15"],@[@"16",@"17",@"18",@"19",@"20"]]; MultpleLineLayout *layout = [[MultpleLineLayout alloc] init]; self.collectionView.collectionViewLayout = layout; self.collectionView.showsHorizontalScrollIndicator = NO; self.collectionView.showsVerticalScrollIndicator = NO; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; self.view.backgroundColor = [UIColor blackColor]; [self.collectionView registerClass:[DataCell class] forCellWithReuseIdentifier:@"DataCell"]; [self.collectionView reloadData]; } - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section { return 20; } - (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView { return 16; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { DataCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"DataCell" forIndexPath:indexPath]; cell.label.text = self.theData[indexPath.section %4][indexPath.row %5]; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { // UICollectionViewCell *item = [collectionView cellForItemAtIndexPath:indexPath]; NSLog(@"%@",indexPath); } 

这里是UICollectionViewFlowLayout子类:

 #define space 5 #import "MultpleLineLayout.h" @implementation MultpleLineLayout { // a subclass of UICollectionViewFlowLayout NSInteger itemWidth; NSInteger itemHeight; } -(id)init { if (self = [super init]) { itemWidth = 60; itemHeight = 60; } return self; } -(CGSize)collectionViewContentSize { NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (itemWidth + space); // "space" is for spacing between cells. NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + space); return CGSizeMake(xSize, ySize); } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path { UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path]; attributes.size = CGSizeMake(itemWidth,itemHeight); int xValue = itemWidth/2 + path.row * (itemWidth + space); int yValue = itemHeight + path.section * (itemHeight + space); attributes.center = CGPointMake(xValue, yValue); return attributes; } -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect { NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(itemWidth + space) : 0; // need to check because bounce gives negative values for x. NSInteger maxRow = rect.size.width/(itemWidth + space) + minRow; NSMutableArray* attributes = [NSMutableArray array]; for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) { for (NSInteger j=minRow ; j < maxRow; j++) { NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i]; [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]]; } } return attributes; } 

最后,这里是UICollectionView的子类:

 -(void)layoutSubviews { [super layoutSubviews]; CGPoint currentOffset = self.contentOffset; CGFloat contentWidth = self.contentSize.width; CGFloat contentHeight = self.contentSize.height; CGFloat centerOffsetX = (contentWidth - self.bounds.size.width)/ 2.0; CGFloat centerOffsetY = (contentHeight - self.bounds.size.height)/ 2.0; CGFloat distanceFromCenterX = fabsf(currentOffset.x - centerOffsetX); CGFloat distanceFromCenterY = fabsf(currentOffset.y - centerOffsetY); if (distanceFromCenterX > contentWidth/4.0) { // this number of 4.0 is arbitrary self.contentOffset = CGPointMake(centerOffsetX, currentOffset.y); } if (distanceFromCenterY > contentHeight/4.0) { self.contentOffset = CGPointMake(currentOffset.x, centerOffsetY); } } 

@更新为swift 3,并改变了maxRow的计算方式,否则最后一列被截断,并可能导致错误

 import UIKit class NodeMap : UICollectionViewController { var rows = 10 var cols = 10 override func viewDidLoad(){ self.collectionView!.collectionViewLayout = NodeLayout(itemWidth: 400.0, itemHeight: 300.0, space: 5.0) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return rows } override func numberOfSections(in collectionView: UICollectionView) -> Int { return cols } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return collectionView.dequeueReusableCell(withReuseIdentifier: "node", for: indexPath) } } class NodeLayout : UICollectionViewFlowLayout { var itemWidth : CGFloat var itemHeight : CGFloat var space : CGFloat var columns: Int{ return self.collectionView!.numberOfItems(inSection: 0) } var rows: Int{ return self.collectionView!.numberOfSections } init(itemWidth: CGFloat, itemHeight: CGFloat, space: CGFloat) { self.itemWidth = itemWidth self.itemHeight = itemHeight self.space = space super.init() } required init(coder aDecoder: NSCoder) { self.itemWidth = 50 self.itemHeight = 50 self.space = 3 super.init() } override var collectionViewContentSize: CGSize{ let w : CGFloat = CGFloat(columns) * (itemWidth + space) let h : CGFloat = CGFloat(rows) * (itemHeight + space) return CGSize(width: w, height: h) } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) let x : CGFloat = CGFloat(indexPath.row) * (itemWidth + space) let y : CGFloat = CGFloat(indexPath.section) + CGFloat(indexPath.section) * (itemHeight + space) attributes.frame = CGRect(x: x, y: y, width: itemWidth, height: itemHeight) return attributes } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let minRow : Int = (rect.origin.x > 0) ? Int(floor(rect.origin.x/(itemWidth + space))) : 0 let maxRow : Int = min(columns - 1, Int(ceil(rect.size.width / (itemWidth + space)) + CGFloat(minRow))) var attributes : Array<UICollectionViewLayoutAttributes> = [UICollectionViewLayoutAttributes]() for i in 0 ..< rows { for j in minRow ... maxRow { attributes.append(self.layoutAttributesForItem(at: IndexPath(item: j, section: i))!) } } return attributes } } 

@ rdelmar的答案像一个魅力,但我需要迅速做到这一点。 这是转换:)

 class NodeMap : UICollectionViewController { @IBOutlet var activateNodeButton : UIBarButtonItem? var rows = 10 var cols = 10 override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return rows } override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return cols } override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { return collectionView.dequeueReusableCellWithReuseIdentifier("node", forIndexPath: indexPath) } override func viewDidLoad() { self.collectionView!.collectionViewLayout = NodeLayout(itemWidth: 100.0, itemHeight: 100.0, space: 5.0) } } class NodeLayout : UICollectionViewFlowLayout { var itemWidth : CGFloat var itemHeight : CGFloat var space : CGFloat init(itemWidth: CGFloat, itemHeight: CGFloat, space: CGFloat) { self.itemWidth = itemWidth self.itemHeight = itemHeight self.space = space super.init() } required init(coder aDecoder: NSCoder) { self.itemWidth = 50 self.itemHeight = 50 self.space = 3 super.init() } override func collectionViewContentSize() -> CGSize { let w : CGFloat = CGFloat(self.collectionView!.numberOfItemsInSection(0)) * (itemWidth + space) let h : CGFloat = CGFloat(self.collectionView!.numberOfSections()) * (itemHeight + space) return CGSizeMake(w, h) } override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! { let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath) let x : CGFloat = CGFloat(indexPath.row) * (itemWidth + space) let y : CGFloat = CGFloat(indexPath.section) + CGFloat(indexPath.section) * (itemHeight + space) attributes.frame = CGRectMake(x, y, itemWidth, itemHeight) return attributes } override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? { let minRow : Int = (rect.origin.x > 0) ? Int(floor(rect.origin.x/(itemWidth + space))) : 0 let maxRow : Int = Int(floor(rect.size.width/(itemWidth + space)) + CGFloat(minRow)) var attributes : Array<UICollectionViewLayoutAttributes> = [UICollectionViewLayoutAttributes]() for i in 0...self.collectionView!.numberOfSections()-1 { for j in minRow...maxRow { attributes.append(self.layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: j, inSection: i))) } } return attributes } }