效果图:
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
上添加UISwipeGestureRecognizer
等UISwipeGestureRecognizer
事件响应后再添加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
接收手势
-
UIPanGestureRecognizer
和UIScrollViewPanGestureRecognizer
一起接收了手势,在左右滑动的时候ScrollView
也会跟着滑动
这个时候我通过判断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;
}];
}