模仿qq图片浏览器

2017-01-14 10:21:30来源:http://www.jianshu.com/p/551ae8b86d27作者:艾江山人点击

第七城市



效果图:



qq相册效果.gif

    如何实现,首先想到的是用collectionView然后再添加手势,如果是使用scrollView图片多的时候复用就是问题。以直接选择collectionView然后设置flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;


-(UICollectionView *)collectionView{
if (!_collectionView) {
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.itemSize = CGSizeMake((MainSize.width - 3*padding)/3, pictureHeight);
_collectionView =/
[[UICollectionView alloc]initWithFrame:self.fixedRect collectionViewLayout:flowLayout];
_collectionView.backgroundColor = [UIColor lightGrayColor];
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.dataSource = self;
_collectionView.contentSize = CGSizeMake(flowLayout.itemSize.width+padding * self.imageArrayM.count, pictureHeight);
_collectionView.directionalLockEnabled = YES;
[_collectionView registerClass:[AIPictureCollectionViewCell class] forCellWithReuseIdentifier:identifier];
}
return _collectionView;
}

现在的qq相片浏览器是在最下方,我这么暂时把frame写在最下方了,如果需要可以调整


- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//固定大小
self.frame = self.fixedRect;
//collectionView
[self addSubview:self.collectionView];
}
return self;
}

很快完成了collectionView但是接下来处处是坑。


坐标转换问题,由于图片的父控件是在cell上的cell又是在collectionView上,就会出现这种效果:

不在window的效果.png

让后我就把图片加到window上这里需要注意的是,世界坐标和当前view的坐标转换
/**
竖直滑动
@param recognizer 手势
*/
-(void)verticalActionWithRecognizer:(UIPanGestureRecognizer*)recognizer{
CGPoint cellCenterPoint = CGPointZero;
CGPoint worldCenterPoint = CGPointZero;
//手势移动了多远
CGPoint translation = [recognizer translationInView:self.contentView];
UIWindow *lastWindow = [[UIApplication sharedApplication].windows lastObject];
// AILog(@"竖直移动");
cellCenterPoint = CGPointMake(recognizer.view.center.x,
translation.y + recognizer.view.center.y);
//转回原来的坐标 不是第一次的时候
if (self.isOnWindow) {
cellCenterPoint = [self.contentView convertPoint:cellCenterPoint fromView:lastWindow];
}
[lastWindow addSubview:recognizer.view];
//转换为世界坐标
worldCenterPoint = [self.contentView convertPoint:cellCenterPoint toView:lastWindow];
recognizer.view.center = worldCenterPoint;
self.onWindow = YES;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.contentView];
}

手势选择问题,一开始我就选择 UIPanGestureRecognizer但是我发现使用 UIPanGestureRecognizer横向滑动事件也被 Cell拦截了。这时候你可能会想到用 UISwipeGestureRecognizer但是UISwipeGestureRecognizer不是连续手势,不能有拖拽效果。
解决方案1: (不适用)
先在 cell上添加UISwipeGestureRecognizerUISwipeGestureRecognizer事件响应后再添加UIPanGestureRecognizer,这个方法可行但是第一次手势已经过去了才添加的UIPanGestureRecognizer手势并没有识别到当前手势,所以要上滑两次才能把图片滑出。
解决方案2:(不适用)
只填加UIPanGestureRecognizer,判断出是横向滑动的时候通过代理方式把移动的距离传给collectionView然后通过contentOffset使外面的collectionView滑动,但是这个方法没有了scollview自带的边缘弹簧效果。所以弃用
解决方案3:
使用UIGestureRecognizerDelegate在手势代理方法里有个方法
大概意思是:是否支持多手势触发,返回YES,则可以多个手势一起触发方法,返回NO则为互斥
是否允许多个手势识别器共同识别,一个控件的手势识别后是否阻断手势识别继续向下传播,默认返回NO;如果为YES,响应者链上层对象触发手势识别后,如果下层对象也添加了手势并成功识别也会继续执行,否则上层对象识别后则不再继续传播

// note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

现在只需要在这个方法里返回YES就可以直接把手势的事件传递到下一个手势,也就是让scollview接收手势



UIPanGestureRecognizerUIScrollViewPanGestureRecognizer一起接收了手势,在左右滑动的时候ScrollView也会跟着滑动


未锁定scrollview.gif

这个时候我通过判断imageView是否在window上,然后用代理方式通知collectionView设置scrollEnabled属性


-(void)pictureCollection:(AIPictureCollectionViewCell *)pictureCollectionCell lockScollViewWithOnWindow:(BOOL)isOnWindow{
self.collectionView.scrollEnabled = !isOnWindow;
}

    好了坑填得差不多了,接下来是当松手的时候判断是发送出去还是返回,这部分比较简单我就直接贴代码了
松手时:


  if (recognizer.state == UIGestureRecognizerStateEnded ) {//松手的时候执行
if (self.direction == kPictureMoveDirectionUp ||
self.direction == kPictureMoveDirectionDown) {
//返回最后的本地viewCenter的坐标
CGPoint endPoint = [self.contentView convertPoint:recognizer.view.center fromView:lastWindow];
//判断距离
if (endPoint.y < 0 && self.isOnWindow) {//发送出去
[self sendImageRecognizer:recognizer];
}else {//返回cell上
[self backImageRecognizer:recognizer];
}
}
self.onWindow = NO;
//解锁scolliew
if (self.delegate && [self.delegate respondsToSelector:@selector(pictureCollection:lockScollViewWithOnWindow:)]) {
[self.delegate pictureCollection:self lockScollViewWithOnWindow:self.isOnWindow];
}
}

发送


/**
发送图片
@param recognizer 手势
*/
-(void)sendImageRecognizer:(UIPanGestureRecognizer*)recognizer{
AILog(@"发出去");
UIImageView *imageV = (UIImageView*)recognizer.view;
__weak typeof(self) weakSelf = self;
if (self.delegate && [self.delegate respondsToSelector:@selector(pictureCollection:didGestureSelectedImage:andImageWorldRect:)]) {
[self.delegate pictureCollection:self didGestureSelectedImage:imageV.image andImageWorldRect:recognizer.view.frame];
}
//设置动画初始frame
imageV.frame = CGRectMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5, 0, 0);
[self.contentView addSubview:imageV];
[UIView animateWithDuration:.3 animations:^{
imageV.frame = weakSelf.bounds;
} completion:^(BOOL finished) {
}];
}

发送Controller中代码


#pragma mark -AIPictureViewerDelegate
-(void)pictureViewer:(AIPictureViewer *)pictureViewer didGestureSelectedImage:(UIImage *)image andImageWorldRect:(CGRect)imageWorldRect{
UIImageView *imageView = [[UIImageView alloc]initWithImage:image];
imageView.frame = imageWorldRect;
[self.view addSubview:imageView];
POPBasicAnimation *popAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition];
popAnimation.toValue = [NSValue valueWithCGPoint:self.imageV.center];
popAnimation.duration = 0.5;
popAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAAnimationLinear];
[imageView.layer pop_addAnimation:popAnimation forKey:nil];
//动画完成后赋值
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(popAnimation.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[imageView removeFromSuperview];
self.imageV.image = image;
});
}

返回


/**
返回图片
@param recognizer 手势
*/
-(void)backImageRecognizer:(UIPanGestureRecognizer*)recognizer{
AILog(@"返回");
__weak typeof(self) weakSelf = self;
UIWindow *lastWindow = [[UIApplication sharedApplication].windows lastObject];
//添加动画
CGRect worldOrginalRect = [self.contentView convertRect:self.bounds toView:lastWindow];
// 一开始要记下frame,动画在window上做,完成后再加到contentView上
[UIView animateWithDuration:.5 animations:^{
recognizer.view.frame = worldOrginalRect;
} completion:^(BOOL finished) {
[weakSelf.contentView addSubview:recognizer.view];
recognizer.view.frame = self.bounds;
}];
}

    demo中有使用reactiveCocoa但是完全可以替换不使用,cell传递选中Image的时候是使用两次代理,实际项目中使用通知方式更为方便,这里为了方便他人使用而以代理形式传值。
完整项目请点github喜欢的点个start支持下




第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台