iOS--开发技巧程序员iOS Developer

UICollectionView仿微信图片浏览器

2017-07-05  本文已影响665人  isletn

图片浏览器可以说是绝大多数APP不可或缺的基本框架。因为项目的需求,仿照微信的图片浏览器自己写了一个。

先放上效果图:
NLPhotoBrowser.gif
用法:
//传入相册图片URL数组与该相册描述即可
NLPhotoBrowserViewController *photoBrowser = [[NLPhotoBrowserViewController alloc] initWithItems:urls describ:@"你拥有了一个 GitHub 账号之后,就可以自由的 clone 或者下载其他项目,也可以创建自己的项目,但是你没法提交代码。仔细想想也知道,肯定不可能随意就能提交代码的"];
[self.navigationController pushViewController:photoBrowser animated:YES];

考虑到代码的可用性,这东西用起来可以说是非常方便了;

下面就记录下这个东西的实现思路吧:

1. 首先实现图片浏览器最重要的“画布”部分——CollectionViewCell,它的构造为UISCrollView+UIImageView;
[self.contentView addSubview:self.scrollView];
[self.scrollView addSubview:self.imageView];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
    doubleTap.numberOfTapsRequired = 2;
    [singleTap requireGestureRecognizerToFail:doubleTap];
    [self.scrollView addGestureRecognizer:singleTap];
    [self.scrollView addGestureRecognizer:doubleTap];
//  单击,这个逻辑需要在ViewController中实现,所以使用delegate将任务指派给ViewController,这个先不细说。
      - (void)singleTap:(UITapGestureRecognizer *)singleTap {
          if ([self.delegate respondsToSelector:@selector(singleTap)]) {
              [self.delegate singleTap];
          }
      }
//  双击,双击放大图片效果,用UIScrollView的zoomToRect:方法将图片缩放到指定rect里面。
      - (void)doubleTap:(UITapGestureRecognizer *)doubleTap {
          if (self.scrollView.zoomScale > 1) {
              [self.scrollView setZoomScale:1 animated:YES];
          } else {
              CGPoint touchPoint = [doubleTap locationInView:self.imageView];
              CGFloat newZoomScale = self.scrollView.maximumZoomScale;
              CGFloat xsize = self.width / newZoomScale;
              CGFloat ysize = self.height / newZoomScale;
              [self.scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2,       touchPoint.y - ysize/2, xsize, ysize) animated:YES];
          }
      }
//  捏合手势
      - (void)pinchAction:(UIPinchGestureRecognizer *)pinch {
          self.imageView.transform = CGAffineTransformScale(self.imageView.transform, pinch.scale, pinch.scale);
          pinch.scale = 1;
      }
// 计算不同情况下ImageView的frame
  if (image.size.height / image.size.width > self.contentView.height / self.contentView.width) {
      self.imageView.height = floor(image.size.height / (image.size.width / self.imageView.width));
      self.imageView.y = 0;
  } else {
      CGFloat height = image.size.height / image.size.width * self.contentView.width;
      if (height < 1 || isnan(height)) height = self.contentView.height;
      height = floor(height);
      self.imageView.height = height;
      self.imageView.centerY = self.contentView.height / 2;
  }
  if (self.imageView.height > self.height && self.imageView.height - self.contentView.height <= 1) {
      self.imageView.height = self.height;
  }
// 给ScrollView赋值contentSize
  _scrollView.contentSize = CGSizeMake(SCREEN_WIDTH, MAX(self.imageView.height, self.contentView.height));
  [_scrollView scrollRectToVisible:_scrollView.bounds animated:NO];

在给ImageView赋值成功时需要调用这个方法。

#pragma mark - UIScrollViewDelegate
  - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    
    CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?
    (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;
    CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?
    (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;
    self.imageView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
                                         scrollView.contentSize.height * 0.5 + offsetY);
}

通过上面的代码可以知道, 我们在获取图片时重新设置了ScrollView的contentSize。那么通过ScrollView的bounds与contentSize比较我们可以得出ImageView的宽高是否超出屏幕。若ImageView完全处于屏幕中,那么不需要改变ImageView的位置;反之,在滑动ScrollView时我们需要改变ImageView的位置使它跟着“滑动”。

2. 然后,给“画布”一个“画框”——它的基本构造是一个装着UICollectionView的ViewController,一般通过push访问这个界面;
// 在ViewController中创建一个控制器并通过数据源类的block给它赋值
[self.view addSubview:self.collectionView];
CollectionViewCellConfigure cellConfigure = ^(NLPhotoCell *cell, NLPhotoModel *photo){
    cell.photo = photo;
    cell.delegate = self;
};
self.dataSource = [[NLCollectionViewDataSource alloc] initWithItems:self.photos cellReuseIdentifier:kPhotoCellReuseIdentifier configureCellBlock:cellConfigure];
self.collectionView.dataSource = self.dataSource;
注意:这里的重点在于:
_flowLayout.sectionInset = UIEdgeInsetsMake(0, kSpacing, 0, kSpacing);
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(-kSpacing, 0, SCREEN_WIDTH + 2 * kSpacing, SCREEN_HEIGHT) collectionViewLayout:self.flowLayout];
QQ20170705-095932.png
3. ViewController添加toolBar与describView
[self.view addSubview:self.toolbar];
[self.view addSubview:self.describView];
image.png

两个视图都是基于UIView的封装, 比较简单,其中需要注意的是:describView上的文字使用coreText进行绘制,性能应该是要比label高一些的。

// 创建TextLayer
static CATextLayer *text_layer(CGFloat contentScale, CTFontRef font, NSString *text) {
    CATextLayer *layer = [CATextLayer layer];
    layer.font = font;
    layer.fontSize = TEXT_FONT_SIZE;
    layer.string = text;
    layer.contentsScale = contentScale;
    layer.wrapped = YES; // 文字自动换行
    layer.alignmentMode = kCAAlignmentLeft;
    return layer;
}
// 确定TextLayer的bounds和锚点
- (void)layoutTextLayer {
    CGRect bounds = self.bounds;
    CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
    _textLayer.bounds = CGRectMake(-kSpacing, -kSpacing, SCREEN_WIDTH-2*kSpacing, [NLUtils fetchHeightWithText:_describ font:[UIFont fontWithName:TEXT_FONT_NAME size:TEXT_FONT_SIZE]]);
    _textLayer.position = center;
}
// 赋值
- (void)setDescrib:(NSString *)describ {
    _describ = describ;
    _textLayer.string = describ;
}
这里需要提到一个动画——单击屏幕隐藏ToolBar与NavigationBar
toolBarAnimation.gif

我们把这个动画拆分来看:

[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBar.isHidden animated:YES];
if (!self.toolbar.isHidden) {
      [UIView animateWithDuration:.25 animations:^{
          self.toolbar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, [UIScreen mainScreen].bounds.size.height - CGRectGetMinY(self.toolbar.frame));
          self.describView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, CGRectGetHeight(self.toolbar.frame));
      } completion:^(BOOL finished) {
          self.toolbar.hidden = YES;
      }];
  }else {
      self.toolbar.hidden = NO;
      [UIView animateWithDuration:.25 animations:^{
          self.toolbar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, 0);
          self.describView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, 0);
      } completion:^(BOOL finished) {
            
      }];
  }

将两者连贯即可实现想要的功能啦。


Tips:
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];

最后放上代码,NLPhotoBrowser,希望能帮到你_

上一篇下一篇

猜你喜欢

热点阅读