闲暇时做的一个可以浏览图片以及视频的框架。效果图如下:
基本功能
- 视频和图片(含GIF)的混合浏览。
- 支持屏幕旋转,横屏浏览。
- 支持双击放大图片、拖曳退出浏览等多种手势操作。
说明:本项目是在GKPhotoBrowser基础上修改及扩展的,GKPhotoBrowser是一款支持图片浏览的图片浏览器,本项目新增了视频播放的功能,播放器修改自SBPlayer。此外,本项目保留了GKPhotoBrowser的原有功能。
如何使用
具体方法详见下载的demo。其他功能或样式可参考GKPhotoBrowser原项目的说明。
下载地址
https://github.com/imsz5460/SZPhotoVideoBrowser
核心代码
*图片的复用机制:
简单来说,会预先加载3张紧邻的图片,比如现在屏幕显示的是第2张图,则第1张与第3张也一并加载了,这3张图放在_visiblePhotoViews数组,这样用户左滑右滑都能有较好的体验。_visiblePhotoViews是随着显示图片的变化而更新的,当新的图片加进来,则有原图片从这个数组移除,并把移除的图片放到缓存池。这样下次加载图片时首先去缓存池中获取,如果没有才重新创建新图片。同时,如果需要展示的是视频,则在图片上直接加载视频view。具体可参看代码实现。
//在scrollview监听滚动的代理方法中设置图片
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.isRotation) return;
[self updateReusableViews];
[self setupPhotoViews];
}
- (void)updateReusableViews {
NSMutableArray *viewsForRemove = [NSMutableArray new];
for (GKPhotoView *photoView in _visiblePhotoViews) {
if ((photoView.frame.origin.x + photoView.frame.size.width < self.photoScrollView.contentOffset.x - self.photoScrollView.frame.size.width) || (photoView.frame.origin.x > self.photoScrollView.contentOffset.x + 2 * self.photoScrollView.frame.size.width)) {
[photoView._playerView stop];
[photoView removeFromSuperview];
GKPhoto *photo = nil;
[photoView setupPhoto:photo];
[viewsForRemove addObject:photoView];
[_reusablePhotoViews addObject:photoView];
}
}
[_visiblePhotoViews removeObjectsInArray:viewsForRemove];
}
- (void)setupPhotoViews {
CGFloat index2 = self.photoScrollView.contentOffset.x / self.photoScrollView.frame.size.width;
NSInteger index = index2 + 0.5;
NSInteger i = index2;
for (NSInteger i = index - 1; i <= index + 1; i++) {
if (i < 0 || i >= self.photos.count) {
continue;
}
GKPhotoView *photoView = [self photoViewForIndex:i];
if (photoView == nil) {
photoView = [self dequeueReusablePhotoView];
CGRect frame = self.photoScrollView.bounds;
CGFloat photoScrollW = frame.size.width;
CGFloat photoScrollH = frame.size.height;
// 调整当前显示的photoView的frame
CGFloat w = photoScrollW - kPhotoViewPadding * 2;
CGFloat h = photoScrollH;
CGFloat x = kPhotoViewPadding + i * (kPhotoViewPadding * 2 + w);
CGFloat y = 0;
photoView.frame = CGRectMake(x, y, w, h);
photoView.tag = i;
[self.photoScrollView addSubview:photoView];
GKPhoto *photo = self.photos[i];
if (photo.isVideo) {
[photoView addSubview: photoView.playerView];
photoView.clipsToBounds = YES;//防止上次的横屏挡住旁边的view
}
[_visiblePhotoViews addObject:photoView];
[photoView resetFrame];
}
if (photoView.photo == nil && self.isShow) {
[photoView setupPhoto:self.photos[i]];
}
}
if (index2 == i) {
if (preindex != index2) {
[_playerView pause];
preindex = i;
[self currentPlayViewPlay:i];
}
}
// 更换photoView
if (index != self.currentIndex && self.isShow && (index >= 0 && index < self.photos.count)) {
self.currentIndex = index;
GKPhotoView *photoView = [self currentPhotoView];
if (photoView.scrollView.zoomScale > 1.0 ) {
[self removePanGesture];
}else {
[self addPanGesture:NO];
}
[self updateLabel];
if ([self.delegate respondsToSelector:@selector(photoBrowser:didChangedIndex:)]) {
[self.delegate photoBrowser:self didChangedIndex:self.currentIndex];
}
}
}
// 重用页面
- (GKPhotoView *)dequeueReusablePhotoView {
GKPhotoView *photoView = [self.reusablePhotoViews anyObject];
if (photoView) {
[photoView._playerView stop];
[_reusablePhotoViews removeObject:photoView];
}else {
photoView = [[GKPhotoView alloc] initWithFrame:self.photoScrollView.bounds imageProtocol:_imageProtocol];
}
photoView.tag = -1;
return photoView;
}