如何用UICollectionView组合布局按图片比例动态调整高度?
UICollectionView组合布局实现图片比例自适应展示
问题
我有一个包含UIImageView的NSCollectionLayoutItem,希望它铺满设备横向,已将item和group的heightDimension设为estimatedDimension。因图片通过HTTP请求下载,我为UIImageView设置了高度258的占位图,但图片下载完成后,NSCollectionLayoutItem仍保持258高度,无法根据下载图片的实际比例调整。请问如何用UICollectionView组合布局实现按图片内容比例展示?
原布局代码
- (NSCollectionLayoutSection*)bulletinSection { NSCollectionLayoutSize* itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:[NSCollectionLayoutDimension estimatedDimension:100]]; NSCollectionLayoutItem* item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize]; NSCollectionLayoutSize* groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:[NSCollectionLayoutDimension estimatedDimension:100]]; NSCollectionLayoutGroup* group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitems:@[item]]; NSCollectionLayoutSection* section = [NSCollectionLayoutSection sectionWithGroup:group]; return section; }
原Cell配置代码
UICollectionViewCellRegistration* bulletinCellRegistration = [UICollectionViewCellRegistration registrationWithCellNib:[UINib nibWithNibName:@"MeBulletinCell" bundle:nil] configurationHandler:^(__kindof UICollectionViewCell * _Nonnull cell, NSIndexPath * _Nonnull indexPath, id _Nonnull item) { MeBulletinCell* bulletinCell = (MeBulletinCell*)cell; if ([weakSelf.bulletinDic isKindOfClass:[NSDictionary class]]) { bulletinCell.bulletinInfo = weakSelf.bulletinDic; } }]; - (void)setBulletinInfo:(NSDictionary *)bulletinInfo { _bulletinInfo = bulletinInfo; [self.bulletinImageView km_setImageWithUrl:bulletinInfo[@"image_url"] placeholder:[UIImage imageNamed:@"NewHomeDefault43"]]; }
解决方案
要实现图片加载完成后自动按比例调整Cell高度,需从布局配置和图片加载回调刷新两方面处理:
1. 调整CollectionView布局配置
将Item和Group的高度维度改为基于宽度的比例维度,确保图片铺满横向时高度按比例缩放,同时用占位图比例作为初始值:
- (NSCollectionLayoutSection*)bulletinSection { // 用占位图比例作为初始布局比例 UIImage *placeholder = [UIImage imageNamed:@"NewHomeDefault43"]; CGFloat initialRatio = placeholder.size.height / placeholder.size.width; NSCollectionLayoutDimension *heightDimension = [NSCollectionLayoutDimension fractionalWidthDimension:initialRatio]; NSCollectionLayoutSize* itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:heightDimension]; NSCollectionLayoutItem* item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize]; NSCollectionLayoutSize* groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:heightDimension]; NSCollectionLayoutGroup* group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitems:@[item]]; NSCollectionLayoutSection* section = [NSCollectionLayoutSection sectionWithGroup:group]; return section; }
2. 监听图片加载完成,动态刷新布局
步骤1:修改Cell的图片加载逻辑,添加完成回调
在MeBulletinCell中添加属性存储图片比例,并在图片加载完成后获取实际比例、刷新Cell:
// 给MeBulletinCell添加属性存储图片比例 @property (nonatomic, assign) CGFloat imageRatio; - (void)setBulletinInfo:(NSDictionary *)bulletinInfo { _bulletinInfo = bulletinInfo; __weak typeof(self) weakSelf = self; [self.bulletinImageView km_setImageWithUrl:bulletinInfo[@"image_url"] placeholder:[UIImage imageNamed:@"NewHomeDefault43"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, BOOL isFinished) { if (isFinished && image) { // 计算并存储图片实际比例 weakSelf.imageRatio = image.size.height / image.size.width; // 刷新当前Cell的布局 UICollectionView *collectionView = [weakSelf.superview.superview collectionView]; NSIndexPath *indexPath = [collectionView indexPathForCell:weakSelf]; if (indexPath) { [collectionView reloadItemsAtIndexPaths:@[indexPath]]; } } }]; }
步骤2:通过布局代理动态更新比例
实现UICollectionViewCompositionalLayout的代理方法,根据Cell中的图片比例动态生成布局:
// 假设当前公告Section的索引为0,根据实际情况修改 - (NSCollectionLayoutSection *)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout layoutSectionForSectionIndex:(NSInteger)sectionIndex { if (sectionIndex == 0) { MeBulletinCell *targetCell = (MeBulletinCell*)[collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; // 优先用图片实际比例,未加载完成则用占位图比例 CGFloat targetRatio = targetCell.imageRatio > 0 ? targetCell.imageRatio : ([UIImage imageNamed:@"NewHomeDefault43"].size.height / [UIImage imageNamed:@"NewHomeDefault43"].size.width); NSCollectionLayoutDimension *heightDimension = [NSCollectionLayoutDimension fractionalWidthDimension:targetRatio]; NSCollectionLayoutSize* itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:heightDimension]; NSCollectionLayoutItem* item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize]; NSCollectionLayoutSize* groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:heightDimension]; NSCollectionLayoutGroup* group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitems:@[item]]; NSCollectionLayoutSection* section = [NSCollectionLayoutSection sectionWithGroup:group]; return section; } // 返回其他Section的布局(如果有) return [self bulletinSection]; }
3. 额外注意事项
- 确保UIImageView的
contentMode设置为UIViewContentModeScaleAspectFill或UIViewContentModeScaleAspectFit,保证图片按比例显示。 - 如果接口返回图片的宽高比参数,可直接用该参数初始化布局,无需等待图片加载完成,提升加载速度。
内容的提问来源于stack exchange,提问作者ximmyxiao
相关产品推荐
相关产品推荐

