效果圖:
qq相冊效果.gif
如何實現(xiàn)烦衣,首先想到的是用collectionView
然后再添加手勢,如果是使用scrollView
圖片多的時候復(fù)用就是問題。以直接選擇collectionView
然后設(shè)置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;
}
現(xiàn)在的qq相片瀏覽器是在最下方拆讯,我這么暫時把frame寫在最下方了屏富,如果需要可以調(diào)整
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//固定大小
self.frame = self.fixedRect;
//collectionView
[self addSubview:self.collectionView];
}
return self;
}
很快完成了collectionView
但是接下來處處是坑晴竞。
- 坐標(biāo)轉(zhuǎn)換問題,由于圖片的父控件是在
cell
上的cell
又是在collectionView
上狠半,就會出現(xiàn)這種效果:
不在window的效果.png
讓后我就把圖片加到window上這里需要注意的是噩死,世界坐標(biāo)和當(dāng)前view
的坐標(biāo)轉(zhuǎn)換
/**
豎直滑動
@param recognizer 手勢
*/
-(void)verticalActionWithRecognizer:(UIPanGestureRecognizer*)recognizer{
CGPoint cellCenterPoint = CGPointZero;
CGPoint worldCenterPoint = CGPointZero;
//手勢移動了多遠(yuǎn)
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);
//轉(zhuǎn)回原來的坐標(biāo) 不是第一次的時候
if (self.isOnWindow) {
cellCenterPoint = [self.contentView convertPoint:cellCenterPoint fromView:lastWindow];
}
[lastWindow addSubview:recognizer.view];
//轉(zhuǎn)換為世界坐標(biāo)
worldCenterPoint = [self.contentView convertPoint:cellCenterPoint toView:lastWindow];
recognizer.view.center = worldCenterPoint;
self.onWindow = YES;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.contentView];
}
- 手勢選擇問題,一開始我就選擇
UIPanGestureRecognizer
但是我發(fā)現(xiàn)使用UIPanGestureRecognizer
橫向滑動事件也被Cell
攔截了神年。這時候你可能會想到用UISwipeGestureRecognizer
但是UISwipeGestureRecognizer
不是連續(xù)手勢已维,不能有拖拽效果。
- 解決方案1: (不適用)
先在cell
上添加UISwipeGestureRecognizer
等UISwipeGestureRecognizer
事件響應(yīng)后再添加UIPanGestureRecognizer
已日,這個方法可行但是第一次手勢已經(jīng)過去了才添加的UIPanGestureRecognizer
手勢并沒有識別到當(dāng)前手勢垛耳,所以要上滑兩次才能把圖片滑出。 - 解決方案2:(不適用)
只填加UIPanGestureRecognizer
,判斷出是橫向滑動的時候通過代理方式把移動的距離傳給collectionView
然后通過contentOffset
使外面的collectionView
滑動堂鲜,但是這個方法沒有了scollview自帶的邊緣彈簧效果栈雳。所以棄用 - 解決方案3:
使用UIGestureRecognizerDelegate
在手勢代理方法里有個方法
大概意思是:是否支持多手勢觸發(fā),返回YES缔莲,則可以多個手勢一起觸發(fā)方法哥纫,返回NO則為互斥
是否允許多個手勢識別器共同識別,一個控件的手勢識別后是否阻斷手勢識別繼續(xù)向下傳播痴奏,默認(rèn)返回NO蛀骇;如果為YES,響應(yīng)者鏈上層對象觸發(fā)手勢識別后抛虫,如果下層對象也添加了手勢并成功識別也會繼續(xù)執(zhí)行松靡,否則上層對象識別后則不再繼續(xù)傳播
// 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;
現(xiàn)在只需要在這個方法里返回YES就可以直接把手勢的事件傳遞到下一個手勢,也就是讓scollview
接收手勢
-
UIPanGestureRecognizer
和UIScrollViewPanGestureRecognizer
一起接收了手勢建椰,在左右滑動的時候ScrollView
也會跟著滑動
未鎖定scrollview.gif
這個時候我通過判斷imageView
是否在window
上,然后用代理方式通知collectionView
設(shè)置scrollEnabled
屬性
-(void)pictureCollection:(AIPictureCollectionViewCell *)pictureCollectionCell lockScollViewWithOnWindow:(BOOL)isOnWindow{
self.collectionView.scrollEnabled = !isOnWindow;
}
好了坑填得差不多了雕欺,接下來是當(dāng)松手的時候判斷是發(fā)送出去還是返回,這部分比較簡單我就直接貼代碼了
松手時:
if (recognizer.state == UIGestureRecognizerStateEnded ) {//松手的時候執(zhí)行
if (self.direction == kPictureMoveDirectionUp ||
self.direction == kPictureMoveDirectionDown) {
//返回最后的本地viewCenter的坐標(biāo)
CGPoint endPoint = [self.contentView convertPoint:recognizer.view.center fromView:lastWindow];
//判斷距離
if (endPoint.y < 0 && self.isOnWindow) {//發(fā)送出去
[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];
}
}
發(fā)送
/**
發(fā)送圖片
@param recognizer 手勢
*/
-(void)sendImageRecognizer:(UIPanGestureRecognizer*)recognizer{
AILog(@"發(fā)出去");
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];
}
//設(shè)置動畫初始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) {
}];
}
發(fā)送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支持下