模仿qq圖片瀏覽器


效果圖:

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上添加UISwipeGestureRecognizerUISwipeGestureRecognizer事件響應(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接收手勢

  • UIPanGestureRecognizerUIScrollViewPanGestureRecognizer一起接收了手勢建椰,在左右滑動的時候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支持下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市乃坤,隨后出現(xiàn)的幾起案子苛让,更是在濱河造成了極大的恐慌,老刑警劉巖湿诊,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狱杰,死亡現(xiàn)場離奇詭異,居然都是意外死亡厅须,警方通過查閱死者的電腦和手機仿畸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朗和,“玉大人错沽,你說我怎么就攤上這事】衾” “怎么了千埃?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忆植。 經(jīng)常有香客問我镰禾,道長皿曲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任吴侦,我火速辦了婚禮屋休,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘备韧。我一直安慰自己劫樟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布织堂。 她就那樣靜靜地躺著叠艳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪易阳。 梳的紋絲不亂的頭發(fā)上附较,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音潦俺,去河邊找鬼拒课。 笑死,一個胖子當(dāng)著我的面吹牛事示,可吹牛的內(nèi)容都是我干的早像。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼肖爵,長吁一口氣:“原來是場噩夢啊……” “哼卢鹦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起劝堪,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冀自,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后秒啦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熬粗,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年帝蒿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巷怜。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡葛超,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出延塑,到底是詐尸還是另有隱情绣张,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布关带,位于F島的核電站侥涵,受9級特大地震影響沼撕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芜飘,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一务豺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗦明,春花似錦笼沥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诗良,卻和暖如春汹桦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鉴裹。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工舞骆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壹罚。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓葛作,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猖凛。 傳聞我的和親對象是個殘疾皇子赂蠢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容