我必须构建一个水平和垂直的UICollectionView可滚动,我知道网格布局只能沿着一个轴滚动,水平或垂直,所以我读了一些帖子,我尝试了不同的解决方案,但最简单的是放UIScrollView中的UICollectionview。通过这种方式,CollectionView可以垂直滚动,UIScrollView可以水平滚动。 问题是垂直滚动是困难的,不是流动的,并且经常停止,直到你再次点击并再次拖动。 你能建议一个解决方案吗?感谢
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
UIScrollView *backgroundScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
backgroundScroll.scrollEnabled = YES;
[self.view addSubview:backgroundScroll];
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(10, 15, 1020, [UIScreen mainScreen].bounds.size.height - 35) collectionViewLayout:layout];
[backgroundScroll addSubview:_collectionView];
_collectionView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
_collectionView.scrollEnabled = YES;
我已经实施了这个方法:
- (void)viewDidLayoutSubviews {
backgroundScroll.contentSize = self.collectionView.frame.size;
}
答案 0 :(得分:2)
执行此操作的方法是创建自定义UICollectionViewLayout
子类。
我最近不得不这样做。
让我去拿文件......一秒......
首先,您不能轻易地使用UICollectionViewFlowLayout
的子类。流动布局设计为在一个方向上适合内容并在另一个方向上滚动。这不是你想要的。
虽然创建一个自定义布局来为您完成此任务并非常困难。
标题文件
@interface GridCollectionViewLayout : UICollectionViewLayout
// properties to configure the size and spacing of the grid
@property (nonatomic) CGSize itemSize;
@property (nonatomic) CGFloat itemSpacing;
// this method was used because I was switching between layouts
- (void)configureCollectionViewForLayout:(UICollectionView *)collectionView;
@end
<强>实施强>
#import "GridCollectionViewLayout.h"
@interface GridCollectionViewLayout ()
@property (nonatomic, strong) NSDictionary *layoutInfo;
@end
@implementation GridCollectionViewLayout
为代码和界面构建器创建inits ...
- (id)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
安装程序默认属性值...
- (void)setup
{
self.itemSize = CGSizeMake(50.0, 50.0);
self.itemSpacing = 10.0;
}
这是因为我在不同的布局之间进行了更改,但它显示了设置布局所需的内容。
- (void)configureCollectionViewForLayout:(UICollectionView *)collectionView
{
collectionView.alwaysBounceHorizontal = YES;
[collectionView setCollectionViewLayout:self animated:NO];
}
必需的方法。这将迭代项目并为每个项目创建框架CGRect
。将它们保存到字典中。
- (void)prepareLayout
{
NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary];
NSInteger sectionCount = [self.collectionView numberOfSections];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
for (NSInteger section = 0; section < sectionCount; section++) {
NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
for (NSInteger item = 0; item < itemCount; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = [self frameForAssessmentAtIndexPath:indexPath];
cellLayoutInfo[indexPath] = itemAttributes;
}
}
self.layoutInfo = cellLayoutInfo;
}
这是一种在给定索引处快速获取帧的便捷方法。
- (CGRect)frameForIndexPath:(NSIndexPath *)indexPath
{
NSInteger column = indexPath.section;
NSInteger row = indexPath.item;
CGFloat originX = column * (self.itemSize.width + self.itemSpacing);
CGFloat originY = row * (self.itemSize.height + self.itemSpacing);
return CGRectMake(originX, originY, self.itemSize.width, self.itemSize.height);
}
计算内容大小的必需方法。这只是将部分或项目的数量乘以大小和间距属性。这允许在两个方向上滚动,因为内容大小可以大于集合视图的宽度和高度。
- (CGSize)collectionViewContentSize
{
NSInteger sectionCount = [self.collectionView numberOfSections];
if (sectionCount == 0) {
return CGSizeZero;
}
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
CGFloat width = (self.itemSize.width + self.itemSpacing) * sectionCount - self.itemSpacing;
CGFloat height = (self.itemSize.height + self.itemSpacing) * itemCount - self.itemSpacing;
return CGSizeMake(width, height);
}
必需的方法。这些告诉集合视图需要放置每个项目的位置。
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.layoutInfo[indexPath];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [NSMutableArray array];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *stop) {
if (CGRectIntersectsRect(attributes.frame, rect)) {
[allAttributes addObject:attributes];
}
}];
return allAttributes;
}
@end
当然,这种情况下的布局特定于我个人的问题。
通过使每个部分成为列并且每个部分中的项目是行来进行布局。所以像这样......
xy = item y in section x
00 10 20 30 ...
01 11 21 31 ...
02 12 22 32 ...
. . . .
. . . .
. . . .
显然,部分中可以有无限数量的部分或项目,所以我必须在两个方向上滚动。
创建布局类后,只需将其设置为集合视图的布局即可。您可以在代码collectionView.collectionViewLayout = myLayout
中执行此操作,也可以使用&#34; layout&#34;在Interface Builder中执行此操作。集合视图中的属性。
答案 1 :(得分:0)
我想介绍一种不同的方法来创建一个向一个方向滚动的UICollectionView,同时显示包含向相反方向滚动的CollectionView的单元格。通过实现此集合视图,在用于集合视图的UICollectionViewFlowLayout实例上设置scrollDirection,此解决方案提供了对用户交互的无缝响应。
解决方案是UICollectionView的子类,并添加了一个延迟手势识别器,它拦截用户的触摸,延迟它们一段时间以确定用户想要滚动的方向,然后在集合视图上取消panningRecognizer滚动那个特定的方向。
import Foundation
import UIKit
class UIDirectionAbidingCollectionView : UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
setupDelayRecognizer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupDelayRecognizer()
}
func setupDelayRecognizer() {
addGestureRecognizer(delayPanGestureRecognizer)
// Delay the touches on the default recognizer on the collection view
panGestureRecognizer.delaysTouchesBegan = true
}
// This gesture recognizer controls the response to the user's touches
// by cancelling by failing panGesture recognizer on the collection view
// that scrolls in the opposite direction.
lazy var delayPanGestureRecognizer: UIPanGestureRecognizer = {
var recognizer = UIPanGestureRecognizer()
recognizer.delegate = self
return recognizer
}()
}
extension UIDirectionAbidingCollectionView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// Ensure that the delay recognizer needs to fails for the
// default panning recognizers to receives the touches
if (gestureRecognizer == delayPanGestureRecognizer &&
otherGestureRecognizer == panGestureRecognizer)
{
return true
}
return false
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
// If the recognizer in question is our delay recognizer
// lets check to see if it should begin receiving touches
if gestureRecognizer == delayPanGestureRecognizer {
// First retrieve the direction our flowlayout intends to scroll
if let flowLayout = self.collectionViewLayout as? UICollectionViewFlowLayout {
let scrollDirection = flowLayout.scrollDirection
// Retrieve the translation of the delayPanningRecognizer
let translation = delayPanGestureRecognizer.translation(in: self)
// Calculate the magnitude of change for the y and x axis
let xTransaltionValue = (translation.x * translation.x)
let yTransaltionValue = (translation.y * translation.y)
if scrollDirection == .vertical && xTransaltionValue > yTransaltionValue {
// If the scroll direction of the flowlayout is vertical,
// and the magnitude in the horizontal direction
// is greater than the horizontal, begin receiving touches.
// Since the delay recognizer doesn't fail, the vertical
// panning recognizer will fail to start on the collection view
return true
}
else if scrollDirection == .horizontal && xTransaltionValue < yTransaltionValue {
// If the scroll direction of the flowlayout is horizontal,
// and the magnitude in the vertical direction
// is greater than the horizontal, begin receiving touches.
// Since the delay recognizer doesn't fail, the horizontal
// panning recognizer will fail to start on the collection view
return true
}
else {
// Fail the delay recognizer, and allows the collection
// view to continue as usual
return false
}
}
}
return true
}
}