在非滚动 UICollectionView 中动态调整 UICollectionViewCell 的大小
Dynamically resize UICollectionViewCell in non-scrolling UICollectionView
我在屏幕顶部有一个小的单行水平布局 UICollectionView。它最多可以包含 6 个项目。问题是我希望所有 6 个项目在不滚动的情况下可见(此集合视图也将在不允许滚动的 Today 扩展中使用)。我想要做的是稍微减小单元格大小和项目间间距,以允许所有 6 个单元格都适合。
基本上我在努力避免这种情况:
我已经研究了一段时间,但不确定如何处理它。我创建了一个方法,每次在集合视图中添加或删除项目时都会触发该方法,就在调用 [self.collectionview reloadData]
之前。
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
//Figure out if cells are wider than screen
CGFloat screenwidth = self.view.frame.size.width;
CGFloat sectionInsetLeft = 10;
CGFloat sectionInsetRight = 10;
CGFloat minItemSpacing = flowLayout.minimumInteritemSpacing;
CGSize itemsize = CGSizeMake(58,58);
CGFloat itemsizeWidth = itemsize.width;
CGFloat totalWidth = sectionInsetLeft + sectionInsetRight +
(itemsizeWidth * _armedRemindersArray.count) +
(minItemSpacing * (_armedRemindersArray.count -2));
CGFloat reductionAmount = itemsizeWidth;
if (totalWidth > screenwidth) {
while (totalWidth > screenwidth) {
totalWidth = totalWidth - 1;
reductionAmount = reductionAmount - 1;
}
CGSize newCellSize = CGSizeMake(reductionAmount, reductionAmount);
flowLayout.itemSize = newCellSize;
}
else flowLayout.itemSize = itemsize;
}
这是结果。
不完全符合我的预期。它不仅将左侧的所有内容都压扁了,还添加了第二行,而且我似乎也遇到了单元格重用问题。说实话,如果可以的话,我只会使用静态单元格,但不幸的是,这似乎是不可能的。
我应该做什么?子类化 UICollectionViewFlowLayout?这不会基本上做我在这里用内置流布局做的同样的事情吗?
编辑:
Kujey 的回答肯定更接近我所需要的。不过,我仍然有细胞再利用的问题。
尝试使用此代码。我得到宽度并使用 _armedRemindersArray
(我猜你用这个数组来表示项目)。
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
//Figure out if cells are wider than screen
CGFloat screenwidth = self.view.frame.size.width;
CGFloat width = screenwidth - ((sectionInsetLeft + sectionInsetRight) *_armedRemindersArray.count + minItemSpacing * (_armedRemindersArray.count -2));
CGSize itemsize = CGSizeMake(width,width);
flowLayout.itemSize = itemsize;
}
我不知道你为什么要先设置 itemsize
,然后再减少它。我认为你应该反过来做:
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
CGFloat sectionInsetLeft = 10;
CGFloat sectionInsetRight = 10;
CGFloat minItemSpacing = flowLayout.minimumInteritemSpacing;
// Find the appropriate item width (<= 58)
CGFloat screenwidth = self.view.frame.size.width;
CGFloat itemsizeWidth = (screenwidth - sectionInsetLeft - sectionInsetRight - (minItemSpacing * (_armedRemindersArray.count -2))) / _armedRemindersArray.count
itemsizeWidth = itemsizeWidth > 58 ? 58 : itemsizeWidth;
flowLayout.itemSize = CGSizeMake(itemsizeWidth, itemsizeWidth);
}
这个有用吗?如果没有,能否包含更多代码?
Xcode 提供了一个为您的需要而设计的对象。它称为 UICollectionViewFlowLayout,您需要做的就是将其子类化并按照您想要的方式放置单元格。每次集合视图需要更新布局时都会调用 prepareForLayout 函数。
您需要的代码如下:
#import "CustomLayout.h"
#define MainCell @"MainCell"
@interface CustomLayout ()
@property (nonatomic, strong) NSMutableDictionary *layoutInfo;
@end
@implementation CustomLayout
-(NSMutableDictionary *) layoutInfo
{
if (!_layoutInfo) {
_layoutInfo = [NSMutableDictionary dictionary];
}
return _layoutInfo;
}
-(void) prepareLayout
{
NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary];
NSIndexPath *indexPath;
CGFloat itemWidth;
CGFloat itemSpacing;
CGFloat widthWithoutSpacing = [self collectionViewContentSize].width / ([self.collectionView numberOfItemsInSection:0]);
if (widthWithoutSpacing > [self collectionViewContentSize].height) {
itemWidth = [self collectionViewContentSize].height;
itemSpacing = ([self collectionViewContentSize].width - itemWidth*[self.collectionView numberOfItemsInSection:0])/
([self.collectionView numberOfItemsInSection:0]+1);
}
else {
itemWidth = widthWithoutSpacing;
itemSpacing = 0;
}
CGFloat xPosition = itemSpacing;
for (NSInteger section = 0; section < [self.collectionView numberOfSections]; section++) {
for (NSInteger index = 0 ; index < [self.collectionView numberOfItemsInSection:section] ; index++) {
indexPath = [NSIndexPath indexPathForItem:index inSection:section];
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect currentFrame=itemAttributes.frame;
currentFrame.origin.x = xPosition;
currentFrame.size.width = itemWidth;
currentFrame.size.height = itemWidth;
itemAttributes.frame=currentFrame;
cellLayoutInfo[indexPath] = itemAttributes;
xPosition += itemWidth + itemSpacing;
}
}
self.layoutInfo[MainCell] = cellLayoutInfo;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSString *elementIdentifier, NSDictionary *elementsInfo, BOOL *stop) {
[elementsInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[allAttributes addObject:attributes];
}
}];
}];
return allAttributes;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.layoutInfo[MainCell][indexPath];
}
-(CGSize) collectionViewContentSize
{
return self.collectionView.frame.size;
}
@end
如果需要垂直居中,您还可以更改单元格的 y 原点。
我在屏幕顶部有一个小的单行水平布局 UICollectionView。它最多可以包含 6 个项目。问题是我希望所有 6 个项目在不滚动的情况下可见(此集合视图也将在不允许滚动的 Today 扩展中使用)。我想要做的是稍微减小单元格大小和项目间间距,以允许所有 6 个单元格都适合。
基本上我在努力避免这种情况:
我已经研究了一段时间,但不确定如何处理它。我创建了一个方法,每次在集合视图中添加或删除项目时都会触发该方法,就在调用 [self.collectionview reloadData]
之前。
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
//Figure out if cells are wider than screen
CGFloat screenwidth = self.view.frame.size.width;
CGFloat sectionInsetLeft = 10;
CGFloat sectionInsetRight = 10;
CGFloat minItemSpacing = flowLayout.minimumInteritemSpacing;
CGSize itemsize = CGSizeMake(58,58);
CGFloat itemsizeWidth = itemsize.width;
CGFloat totalWidth = sectionInsetLeft + sectionInsetRight +
(itemsizeWidth * _armedRemindersArray.count) +
(minItemSpacing * (_armedRemindersArray.count -2));
CGFloat reductionAmount = itemsizeWidth;
if (totalWidth > screenwidth) {
while (totalWidth > screenwidth) {
totalWidth = totalWidth - 1;
reductionAmount = reductionAmount - 1;
}
CGSize newCellSize = CGSizeMake(reductionAmount, reductionAmount);
flowLayout.itemSize = newCellSize;
}
else flowLayout.itemSize = itemsize;
}
这是结果。
不完全符合我的预期。它不仅将左侧的所有内容都压扁了,还添加了第二行,而且我似乎也遇到了单元格重用问题。说实话,如果可以的话,我只会使用静态单元格,但不幸的是,这似乎是不可能的。
我应该做什么?子类化 UICollectionViewFlowLayout?这不会基本上做我在这里用内置流布局做的同样的事情吗?
编辑:
Kujey 的回答肯定更接近我所需要的。不过,我仍然有细胞再利用的问题。
尝试使用此代码。我得到宽度并使用 _armedRemindersArray
(我猜你用这个数组来表示项目)。
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
//Figure out if cells are wider than screen
CGFloat screenwidth = self.view.frame.size.width;
CGFloat width = screenwidth - ((sectionInsetLeft + sectionInsetRight) *_armedRemindersArray.count + minItemSpacing * (_armedRemindersArray.count -2));
CGSize itemsize = CGSizeMake(width,width);
flowLayout.itemSize = itemsize;
}
我不知道你为什么要先设置 itemsize
,然后再减少它。我认为你应该反过来做:
-(void)setupCollectionViewLayout{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.buttonBarCollectionView.collectionViewLayout;
CGFloat sectionInsetLeft = 10;
CGFloat sectionInsetRight = 10;
CGFloat minItemSpacing = flowLayout.minimumInteritemSpacing;
// Find the appropriate item width (<= 58)
CGFloat screenwidth = self.view.frame.size.width;
CGFloat itemsizeWidth = (screenwidth - sectionInsetLeft - sectionInsetRight - (minItemSpacing * (_armedRemindersArray.count -2))) / _armedRemindersArray.count
itemsizeWidth = itemsizeWidth > 58 ? 58 : itemsizeWidth;
flowLayout.itemSize = CGSizeMake(itemsizeWidth, itemsizeWidth);
}
这个有用吗?如果没有,能否包含更多代码?
Xcode 提供了一个为您的需要而设计的对象。它称为 UICollectionViewFlowLayout,您需要做的就是将其子类化并按照您想要的方式放置单元格。每次集合视图需要更新布局时都会调用 prepareForLayout 函数。 您需要的代码如下:
#import "CustomLayout.h"
#define MainCell @"MainCell"
@interface CustomLayout ()
@property (nonatomic, strong) NSMutableDictionary *layoutInfo;
@end
@implementation CustomLayout
-(NSMutableDictionary *) layoutInfo
{
if (!_layoutInfo) {
_layoutInfo = [NSMutableDictionary dictionary];
}
return _layoutInfo;
}
-(void) prepareLayout
{
NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary];
NSIndexPath *indexPath;
CGFloat itemWidth;
CGFloat itemSpacing;
CGFloat widthWithoutSpacing = [self collectionViewContentSize].width / ([self.collectionView numberOfItemsInSection:0]);
if (widthWithoutSpacing > [self collectionViewContentSize].height) {
itemWidth = [self collectionViewContentSize].height;
itemSpacing = ([self collectionViewContentSize].width - itemWidth*[self.collectionView numberOfItemsInSection:0])/
([self.collectionView numberOfItemsInSection:0]+1);
}
else {
itemWidth = widthWithoutSpacing;
itemSpacing = 0;
}
CGFloat xPosition = itemSpacing;
for (NSInteger section = 0; section < [self.collectionView numberOfSections]; section++) {
for (NSInteger index = 0 ; index < [self.collectionView numberOfItemsInSection:section] ; index++) {
indexPath = [NSIndexPath indexPathForItem:index inSection:section];
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect currentFrame=itemAttributes.frame;
currentFrame.origin.x = xPosition;
currentFrame.size.width = itemWidth;
currentFrame.size.height = itemWidth;
itemAttributes.frame=currentFrame;
cellLayoutInfo[indexPath] = itemAttributes;
xPosition += itemWidth + itemSpacing;
}
}
self.layoutInfo[MainCell] = cellLayoutInfo;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSString *elementIdentifier, NSDictionary *elementsInfo, BOOL *stop) {
[elementsInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[allAttributes addObject:attributes];
}
}];
}];
return allAttributes;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.layoutInfo[MainCell][indexPath];
}
-(CGSize) collectionViewContentSize
{
return self.collectionView.frame.size;
}
@end
如果需要垂直居中,您还可以更改单元格的 y 原点。