• iOS基础


    一、需求分析

    点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片,新的视图控制器为全屏显示,背景为黑色,再次点击照片动画缩小至当前选中的照片位置,双击放大照片,如果已经放大则缩小,在新的视图控制器中滑动手指,可以在照片之间切换,当前显示的照片支持手指捏合手势,放大或缩小照片,照片切换时,在照片的下方显示标签提示当前显示的照片数量及照片总数。

    性能优化:点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片(非modal、非push

    新的视图控制器为全屏显示,背景为黑色(修改UIApplication的状态栏),再次点击照片动画缩小至当前选中的照片位置(照片视图需要与父视图中的UIImageView建立关联),在新的视图控制器中滑动手指,可以在照片之间切换(UIScrollView的分页支持),当前显示的照片支持手指捏合手势,放大或缩小照片(UIScrollView的代理方法支持图片缩放),照片切换时,在照片的下方显示标签提示当前显示的照片数量及照片总数(开启新的视图控制器前,需要传入照片数组)

    二、调整瀑布流

    因网络抓取JSON数据的问题,改为从本地plist文件加载照片数据文件,修改MGJData,增加大图URL,修改WaterFlowView中的generateCacheData方法,在重新加载数据前删除所有子视图,创建PhotoBowser分组,保存照片浏览器代码

    三、建立照片浏览器所需文件

    PhotoBowserViewController:负责照片浏览控制

    PhotoView:显示单张照片,继承自UIScrollView

    PhotoToolbarView:照片工图栏视图,显示保存按钮和照片索引

    PhotoModel:照片数据模型类,保存照片浏览器的数据

    四、建立数据模型

    + (id)photoModelWithUrl:(NSURL *)url index:(NSInteger)index;

    // 图像Url

    @property (strong, nonatomic) NSURL *url;

    // 图像索引

    @property (assign, nonatomic) NSInteger index;

    五、实例化视图

    // 1. 隐藏状态栏

    UIApplication *app = [UIApplication sharedApplication];

    // 记录初始的状态栏隐藏情况

    _defaultStatusBarHidden = app.statusBarHidden;

    [app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

     

    // 2. 实例化滚动视图

    _scrollView = [[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds];

    [_scrollView setBackgroundColor:[UIColor blackColor]];

     

    // 暂时添加点按手势,关闭视图控制器

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];

    [_scrollView addGestureRecognizer:tap];

     

    // 3. 将滚动视图设置为根视图

    self.view = _scrollView;

    六、显示&隐藏

    l 显示

    // 为了与调用视图器完全解耦,使用appwindow实现视图控制器的显示

    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    // 将根视图添加到window中,会叠加在上级窗口上

    [window addSubview:self.view];

    // 将当前视图控制器作为子视图控制器,添加到window的根视图控制器中

    [window.rootViewController addChildViewController:self];

    l 隐藏

    [[UIApplication sharedApplication]setStatusBarHidden:_defaultStatusBarHidden];

    [self.view removeFromSuperview];

    [self removeFromParentViewController];

    七、修改WaterFlowView视图

    cellFramesArray属性移动到.h文件中,以保证照片浏览器视图控制器可以得到所有照片的位置

    八、调整显示照片浏览器方法

    for (MGJData *data in self.dataList) {

        // 获取单元格视图的位置

          CGRect frame = [self.waterFlowView.cellFramesArray[index]CGRectValue];

        CGRect srcFrame = [self.waterFlowView convertRect:frame toView:controller.view];

        PhotoModel *p = [PhotoModel photoModelWithUrl:data.largeImage index:index srcFrame:srcFrame];

        [arrayM addObject:p];

        index++;

    }

    controller.photoList = arrayM;

    controller.currentPhotoIndex = indexPath.row;

    九、照片视图

    通过setter属性设置照片

    // 占位图像

    UIImage *image = [[UIImage alloc]init];

    // 从网络加载图像

    __unsafe_unretained PhotoView *photoView = self;

    [_imageView setImageWithURL:_photo.url placeholderImage:image options:SDWebImageRetryFailed | SDWebImageProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) {

    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {

        // 记录图像

        [photo setImage:image];

        [photoView adjustImageFrame];

    }];

    十、设置照片显示尺寸

    // 2) 以宽度计算比例

    CGFloat minScale = boundW / imageW;

    if (minScale > 1.0) {

        minScale = 1.0;

    }

    CGFloat maxScale = 2.0;

     

    // 3) 设置视图的缩放比例

    self.minimumZoomScale = minScale;

    self.maximumZoomScale = maxScale;

     

    // 如果图片超出屏幕大小,需要缩放显示

    self.zoomScale = minScale;

     

    // 4) 设置内容尺寸

    CGRect imageFrame = CGRectMake(0, 0, boundW, imageH * minScale);

    self.contentSize = CGSizeMake(imageFrame.size.width, imageFrame.size.height);

     

    // 5) 计算图像位置

    if (imageFrame.size.height < boundH) {

        imageFrame.origin.y = (boundH - imageFrame.size.height) / 2.0;

    }

    十一、如果是初次显示,播放动画效果

    // 6) 如果是初次显示,显示动画效果

    if (_photo.isFirstShow) {

        [_imageView setFrame:_photo.srcFrame];

        [UIView animateWithDuration:0.3f animations:^{

            [_imageView setFrame:imageFrame];

        }];

    } else {

        [_imageView setFrame:imageFrame];

    }

    十二、照片视图的缩放处理

    #pragma mark - UIScrollView代理方法

    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

    {

        return _imageView;

    }

    十三、照片视图,增加双击手势

    如果照片已经放大则缩小至初始比例

    否则根据用户点击位置放大图像

    if (self.zoomScale == self.maximumZoomScale) {

        [self setZoomScale:self.minimumZoomScale animated:YES];

    } else {

        CGPoint location = [recognizer locationInView:self];

        [self zoomToRect:CGRectMake(location.x, location.y, 1, 1) animated:YES];

    }

    十四、照片视图,增加单击手势隐藏照片(坐标转换)

    #pragma mark 隐藏照片视图

    - (void)hide {

        if (_isDoubleTap) return;

        // 滚动视图复位

          self.contentOffset = CGPointZero;

        [UIView animateWithDuration:1.0f animations:^{

            [_imageView setFrame:_photo.srcFrame];

            [self.photoViewDelegate photoViewDidSingleTap:self];

        } completion:^(BOOL finished) {

            [self.photoViewDelegate photoViewDidZoomOut:self];

        }];

    }

    #pragma mark 单击手势

    - (void)singleTap:(UITapGestureRecognizer *)recognizer {

        _isDoubleTap = NO;

        // 增加时间延时,确保变量数值更改生效

        [self performSelector:@selector(hide) withObject:nil afterDelay:0.2f];

    }

    十五、提示,隐藏照片时需要两个代理方法处理

    #pragma mark 照片视图代理方法

    - (void)photoViewDidSingleTap:(PhotoView *)photoView

    {

        [[UIApplication sharedApplication]setStatusBarHidden:_defaultStatusBarHidden];

        // 设置成透明颜色

        [self.view setBackgroundColor:[UIColor clearColor]];

    }

     

    - (void)photoViewDidZoomOut:(PhotoView *)photoView

    {

        [self.view removeFromSuperview];

        [self removeFromParentViewController];

    }

    十六、查询可重用照片视图

    #pragma mark 查询可重用照片视图

    - (PhotoView *)dequeueReusablePhotoView

    {

        // 预设缓存集合已经存在并且工作正常

          PhotoView *photoView = [_reusablePhotoViews anyObject];

     

        // 从缓存集合中删除照片视图

          if (photoView) {

            [_reusablePhotoViews removeObject:photoView];

        }

        return photoView;

    }

    十七、利用photoList setter方法

    #pragma mark photoList setter方法,处理视图缓存

    - (void)setPhotoList:(NSArray *)photoList

    {

        _photoList = photoList;

        _visiblePhotoViewDict = [NSMutableDictionary dictionary];

        _reusablePhotoViews = [NSMutableSet set];

    }

    十八、通过可见视图字典做照片视图优化

    // 在可见视图字典中查询是否存在照片视图

    PhotoView *photoView = [_visiblePhotoViewDict objectForKey:@(index)];

    if (photoView == nil) {

        photoView = [self dequeueReusablePhotoView];

        if (photoView == nil) {

            photoView = [[PhotoView alloc]init];

            [photoView setPhotoViewDelegate:self];

        }

        PhotoModel *photo = _photoList[index];

        CGFloat w = self.view.bounds.size.width;

        CGFloat h = self.view.bounds.size.height;

        CGRect photoFrame = CGRectMake(index * w, 0, w, h);

        [photoView setFrame:photoFrame];

        [photoView setPhoto:photo];

        [self.view addSubview:photoView];

        // 加入可见视图字典

        [_visiblePhotoViewDict setObject:photoView forKey:@(index)];

    }

    十九、在视图滚动停止时,调整缓存数据

    NSInteger index = (NSInteger)_scrollView.contentOffset.x / _scrollView.bounds.size.width;

    if (index != _currentPhotoIndex) {

        PhotoView *photoView = _visiblePhotoViewDict[@(_currentPhotoIndex)];

        [_visiblePhotoViewDict removeObjectForKey:@(_currentPhotoIndex)];

        [photoView removeFromSuperview];

        [_reusablePhotoViews addObject:photoView];

        _currentPhotoIndex = index;

  • 相关阅读:
    grid 布局
    mongoose
    Nestjs 上传文件
    Nestjs 设置静态文件,public
    Centos 为Nginx 搭建https
    react组件
    namecheap 添加二级域名
    electron+react
    遍历文件,读取.wxss文件,在头部添加一条注释
    react 中的绑定事件
  • 原文地址:https://www.cnblogs.com/monicaios/p/3531857.html
Copyright © 2020-2023  润新知