iOS网格视图终极方案:AQGridView完全指南
项目简介
AQGridView是一个专为iPhone/iPad设计的网格视图组件,旨在提供类似于NSCollectionView的功能和外观。作为iPadDevCamp 2010圣何塞站"最佳开发者工具/助手"奖的得主,AQGridView已被众多知名应用采用,包括eBooks by Kobo、Netflix Actors和Stocks - The Finance App等。
核心特点
| 特点 | 描述 |
|---|---|
| 熟悉的编程模型 | 基于UITableView的编程模式,易于iOS开发者上手 |
| 高性能 | 实现了单元格重用机制,优化了滚动性能 |
| 丰富的动画 | 支持插入、删除、重排和重载等多种动画效果 |
| 高度可定制 | 支持自定义单元格、选择样式和布局调整 |
| 通用应用支持 | 同时支持iPhone和iPad设备 |
与UITableView的异同
安装指南
环境要求
- iOS 3.2+ SDK(最低支持运行在iOS 3.0系统上)
- Xcode 4.0+
- ARC兼容
安装步骤
1. 获取源代码
git clone https://gitcode.com/gh_mirrors/aq/AQGridView.git
2. 添加到项目
3. 配置构建设置
- 选择项目 Targets -> 你的项目 -> Build Phases
- 在Target Dependencies中点击"+",添加AQGridView
- 在Link Binary with Libraries中点击"+",添加libAQGridView.a
- 在Build Settings中,设置User Header Search Paths为AQGridView目录
- 在Other Linker Flags中添加"-ObjC"
4. 添加资源文件
将Resources目录下的选择背景图片添加到项目中:
- AQGridSelection.png
- AQGridSelectionGray.png
- AQGridSelectionGrayBlue.png
- AQGridSelectionGreen.png
- AQGridSelectionRed.png
快速开始
创建基本网格视图
1. 创建视图控制器
#import "AQGridViewController.h"
@interface MyGridViewController : AQGridViewController <AQGridViewDataSource, AQGridViewDelegate>
@end
2. 实现数据源方法
@implementation MyGridViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.gridView.dataSource = self;
self.gridView.delegate = self;
self.gridView.backgroundColor = [UIColor whiteColor];
}
#pragma mark - AQGridViewDataSource
// 返回网格项数量
- (NSUInteger)numberOfItemsInGridView:(AQGridView *)gridView {
return 20; // 假设我们有20个项目
}
// 创建并返回网格单元格
- (AQGridViewCell *)gridView:(AQGridView *)gridView cellForItemAtIndex:(NSUInteger)index {
static NSString *CellIdentifier = @"CellIdentifier";
// 尝试重用单元格
AQGridViewCell *cell = [gridView dequeueReusableCellWithIdentifier:CellIdentifier];
// 如果没有可重用的单元格,则创建新的
if (cell == nil) {
cell = [[AQGridViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100)
reuseIdentifier:CellIdentifier];
cell.selectionStyle = AQGridViewCellSelectionStyleBlue;
}
// 配置单元格内容
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", index];
cell.backgroundColor = [UIColor colorWithHue:(index%10)/10.0
saturation:0.7
brightness:0.9
alpha:1.0];
return cell;
}
// 指定单元格大小
- (CGSize)portraitGridCellSizeForGridView:(AQGridView *)gridView {
return CGSizeMake(100, 100); // 单元格大小为100x100
}
@end
3. 在应用中使用
// 在你的导航控制器或标签栏控制器中
MyGridViewController *gridVC = [[MyGridViewController alloc] init];
[self.navigationController pushViewController:gridVC animated:YES];
核心类与协议
AQGridView
网格视图的主要类,继承自UIScrollView。
// 主要属性
@property (nonatomic, unsafe_unretained) id<AQGridViewDataSource> dataSource;
@property (nonatomic, unsafe_unretained) id<AQGridViewDelegate> delegate;
@property (nonatomic, assign) AQGridViewLayoutDirection layoutDirection;
@property (nonatomic, readonly) NSUInteger numberOfItems;
@property (nonatomic, readonly) NSUInteger numberOfColumns;
@property (nonatomic, readonly) CGSize gridCellSize;
@property (nonatomic, assign) BOOL allowsSelection;
@property (nonatomic, retain) UIView *backgroundView;
@property (nonatomic, assign) AQGridViewCellSeparatorStyle separatorStyle;
@property (nonatomic, retain) UIColor *separatorColor;
AQGridViewDataSource 协议
// 必须实现的方法
- (NSUInteger)numberOfItemsInGridView:(AQGridView *)gridView;
- (AQGridViewCell *)gridView:(AQGridView *)gridView cellForItemAtIndex:(NSUInteger)index;
// 可选方法
- (CGSize)portraitGridCellSizeForGridView:(AQGridView *)gridView;
AQGridViewDelegate 协议
// 选择相关
- (void)gridView:(AQGridView *)gridView didSelectItemAtIndex:(NSUInteger)index;
- (void)gridView:(AQGridView *)gridView didDeselectItemAtIndex:(NSUInteger)index;
// 显示定制
- (void)gridView:(AQGridView *)gridView willDisplayCell:(AQGridViewCell *)cell forItemAtIndex:(NSUInteger)index;
// 布局调整
- (CGRect)gridView:(AQGridView *)gridView adjustCellFrame:(CGRect)cellFrame withinGridCellFrame:(CGRect)gridCellFrame;
AQGridViewCell
网格视图的单元格类,类似于UITableViewCell。
// 主要属性
@property (nonatomic, copy) NSString *reuseIdentifier;
@property (nonatomic, assign) AQGridViewCellSelectionStyle selectionStyle;
@property (nonatomic, retain) UIColor *selectionGlowColor;
@property (nonatomic, readonly, getter=isSelected) BOOL selected;
@property (nonatomic, readonly, getter=isHighlighted) BOOL highlighted;
高级功能
单元格动画
AQGridView支持多种单元格动画效果:
typedef enum {
AQGridViewItemAnimationFade, // 淡入淡出
AQGridViewItemAnimationRight, // 从右侧滑入
AQGridViewItemAnimationLeft, // 从左侧滑入
AQGridViewItemAnimationTop, // 从顶部滑入
AQGridViewItemAnimationBottom, // 从底部滑入
AQGridViewItemAnimationNone // 无动画
} AQGridViewItemAnimation;
使用批量更新实现动画效果:
// 开始更新
[self.gridView beginUpdates];
// 插入项目
NSIndexSet *insertIndices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 5)];
[self.gridView insertItemsAtIndices:insertIndices withAnimation:AQGridViewItemAnimationFade];
// 删除项目
NSIndexSet *deleteIndices = [NSIndexSet indexSetWithIndex:10];
[self.gridView deleteItemsAtIndices:deleteIndices withAnimation:AQGridViewItemAnimationRight];
// 移动项目
[self.gridView moveItemAtIndex:5 toIndex:7 withAnimation:AQGridViewItemAnimationLeft];
// 结束更新
[self.gridView endUpdates];
自定义选择样式
AQGridView提供了多种选择样式:
typedef enum {
AQGridViewCellSelectionStyleNone, // 无选择效果
AQGridViewCellSelectionStyleBlue, // 蓝色选择效果
AQGridViewCellSelectionStyleGray, // 灰色选择效果
AQGridViewCellSelectionStyleBlueGray, // 蓝灰色选择效果
AQGridViewCellSelectionStyleGreen, // 绿色选择效果
AQGridViewCellSelectionStyleRed // 红色选择效果
} AQGridViewCellSelectionStyle;
设置自定义选择样式:
cell.selectionStyle = AQGridViewCellSelectionStyleGreen;
// 自定义选择发光颜色(仅在iOS 3.2+可用)
cell.selectionGlowColor = [UIColor purpleColor];
cell.selectionGlowRadius = 10.0f;
手势识别与拖拽排序
SpringBoard示例展示了如何实现类似iOS主屏幕的拖拽排序功能:
// 添加长按手势识别器
UILongPressGestureRecognizer *gr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(moveActionGestureRecognizerStateChanged:)];
gr.minimumPressDuration = 0.5;
gr.delegate = self;
[self.gridView addGestureRecognizer:gr];
// 手势处理方法
- (void)moveActionGestureRecognizerStateChanged:(UIGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
// 开始拖拽,创建拖动的单元格副本
break;
case UIGestureRecognizerStateChanged:
// 更新拖拽位置,移动其他单元格
break;
case UIGestureRecognizerStateEnded:
// 结束拖拽,更新数据顺序
break;
default:
break;
}
}
布局方向与内容边距
AQGridView支持水平和垂直两种布局方向:
typedef enum {
AQGridViewLayoutDirectionVertical, // 垂直布局(默认)
AQGridViewLayoutDirectionHorizontal // 水平布局
} AQGridViewLayoutDirection;
// 设置布局方向
self.gridView.layoutDirection = AQGridViewLayoutDirectionHorizontal;
// 设置内容边距
self.gridView.leftContentInset = 20.0f;
self.gridView.rightContentInset = 20.0f;
示例项目解析
ImageDemo
ImageDemo展示了AQGridView的基本用法和动态重排功能:
// 随机打乱网格项
- (IBAction)shuffle {
NSMutableArray *sourceArray = [_imageNames mutableCopy];
NSMutableArray *destArray = [[NSMutableArray alloc] initWithCapacity:[sourceArray count]];
[self.gridView beginUpdates];
srandom(time(NULL));
while ([sourceArray count] != 0) {
NSUInteger index = (NSUInteger)(random() % [sourceArray count]);
id item = [sourceArray objectAtIndex:index];
// 动画移动项目
[self.gridView moveItemAtIndex:[_imageNames indexOfObject:item]
toIndex:[destArray count]
withAnimation:AQGridViewItemAnimationFade];
[destArray addObject:item];
[sourceArray removeObjectAtIndex:index];
}
_imageNames = [destArray copy];
[self.gridView endUpdates];
}
SpringBoard
SpringBoard示例实现了类似iOS主屏幕的图标排列和拖拽功能:
// 调整内容边距以确保列数正确
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
// 竖屏模式下4列
_gridView.leftContentInset = 0.0;
_gridView.rightContentInset = 0.0;
} else {
// 横屏模式下5列,调整边距使宽度可被5整除
_gridView.leftContentInset = 2.0;
_gridView.rightContentInset = 2.0;
}
}
ExpanderDemo
ExpanderDemo展示了如何实现从单个点扩展出网格视图的动画效果:
// 从指定矩形区域展开单元格
- (void)expandCellsFromRect:(CGRect)rect ofView:(UIView *)aView {
// 禁用动画
[UIView setAnimationsEnabled:NO];
self.gridView.backgroundColor = [UIColor clearColor];
// 收集可见单元格的原始位置
NSArray *cells = [self.gridView visibleCells];
NSMutableArray *locations = [[NSMutableArray alloc] initWithCapacity:[cells count]];
for (AQGridViewCell *cell in cells) {
[locations addObject:[NSValue valueWithCGRect:cell.frame]];
}
_expandedLocations = [locations copy];
// 记录起始位置
_startingRect = [aView convertRect:rect toView:self.gridView];
// 标记需要展开
_readyToExpand = YES;
// 重新启用动画
[UIView setAnimationsEnabled:YES];
}
性能优化
单元格重用
AQGridView采用了与UITableView类似的单元格重用机制,确保滚动性能:
- (AQGridViewCell *)gridView:(AQGridView *)gridView cellForItemAtIndex:(NSUInteger)index {
static NSString *CellIdentifier = @"MyCell";
// 尝试从重用队列获取单元格
MyCustomCell *cell = (MyCustomCell *)[gridView dequeueReusableCellWithIdentifier:CellIdentifier];
// 如果没有可重用的单元格,则创建新的
if (cell == nil) {
cell = [[MyCustomCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:CellIdentifier];
// 初始化单元格
}
// 配置单元格内容
cell.image = [UIImage imageNamed:[_imageNames objectAtIndex:index]];
cell.title = [NSString stringWithFormat:@"Item %d", index];
return cell;
}
图片异步加载
对于大量图片的网格,建议使用异步加载:
// 异步加载图片
- (void)loadImageAsyncForCell:(MyCustomCell *)cell atIndex:(NSUInteger)index {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageName = [_imageNames objectAtIndex:index];
UIImage *image = [UIImage imageNamed:imageName];
dispatch_async(dispatch_get_main_queue(), ^{
// 检查单元格是否仍然可见且对应正确的索引
if ([cell.reuseIdentifier isEqualToString:@"MyCell"] &&
[self.gridView indexForCell:cell] == index) {
cell.image = image;
}
});
});
}
减少绘制操作
优化单元格绘制性能:
// 在自定义单元格中
- (void)drawRect:(CGRect)rect {
// 仅在必要时重绘
if (self.needsDisplay) {
// 绘制代码
}
}
// 缓存绘制结果
- (UIImage *)cachedImageForIndex:(NSUInteger)index {
static NSCache *imageCache = nil;
if (!imageCache) {
imageCache = [[NSCache alloc] init];
imageCache.countLimit = 20; // 限制缓存大小
}
NSString *key = [NSString stringWithFormat:@"image_%d", index];
UIImage *image = [imageCache objectForKey:key];
if (!image) {
// 创建图像
image = [self createImageForIndex:index];
[imageCache setObject:image forKey:key];
}
return image;
}
常见问题与解决方案
问题1:单元格大小不适应不同屏幕尺寸
解决方案:根据不同设备和方向动态调整单元格大小
- (CGSize)portraitGridCellSizeForGridView:(AQGridView *)gridView {
CGFloat screenWidth = self.view.bounds.size.width;
NSUInteger columns = 3; // 默认3列
// 根据屏幕宽度调整列数
if (screenWidth > 768) { // iPad横屏
columns = 5;
} else if (screenWidth > 480) { // iPad竖屏或iPhone Plus横屏
columns = 4;
}
// 计算单元格宽度
CGFloat cellWidth = (screenWidth - self.gridView.leftContentInset - self.gridView.rightContentInset) / columns;
CGFloat cellHeight = cellWidth * 1.2; // 保持宽高比
return CGSizeMake(cellWidth, cellHeight);
}
问题2:滚动时出现卡顿
解决方案:优化单元格创建和配置过程
// 1. 简化单元格层次结构
// 2. 避免在willDisplayCell中执行复杂操作
// 3. 使用异步加载图片
// 4. 减少图层数量
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
问题3:选择样式不显示
解决方案:确保正确设置选择样式并添加资源文件
// 检查选择样式是否正确设置
cell.selectionStyle = AQGridViewCellSelectionStyleBlue;
// 确保资源文件已添加到项目中
// AQGridSelection.png等图片应包含在项目中,并在Copy Bundle Resources中
总结与展望
AQGridView为iOS开发者提供了一个功能强大、易于使用的网格视图解决方案,其设计借鉴了UITableView的编程模式,使熟悉iOS开发的工程师能够快速上手。通过单元格重用机制、丰富的动画效果和高度可定制性,AQGridView能够满足各种网格布局需求。
未来发展方向
- 分区支持:添加对类似UITableView的分区(section)功能的支持
- 高性能渲染:实现行级合成渲染,提高滚动性能
- 更多布局选项:支持瀑布流、流式布局等更多布局方式
- Swift支持:提供Swift版本的API和示例
AQGridView作为一个成熟的开源项目,已经在众多商业应用中得到验证。无论是构建图片浏览器、应用网格还是复杂的数据展示界面,AQGridView都能提供出色的性能和用户体验。
希望本指南能帮助你快速掌握AQGridView的使用,并在你的项目中发挥其强大功能。如有任何问题或建议,欢迎参与项目贡献或提交issue。
点赞👍 + 收藏⭐ + 关注,获取更多iOS开发优质内容!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



