scrollView实现图片的缩小放大

啰嗦在前面:
之前实现view的放大缩小的时候是使用手势,然后通过改变transform或者frame来实现,最近抽空看了下使用scrollView的实现方式

支持pinch手势

先看一段官方文档的说明:

To support zooming, you must set a delegate for your scroll view. The delegate object must conform to the UIScrollViewDelegate protocol. In many cases, the delegate will be the scroll view’s controller class. That delegate class must implement the viewForZoomingInScrollView: method and return the view to zoom.

大致意思就是,是如果支持缩放 必须设置 scrollView的代理,该代理类必须实现viewForZoomingInScrollView:方法,以返回一个view进行缩放;

下面这段代码就返回了一个imageView来进行缩放

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

要指定用户可以缩放的数量,可以设置minimumZoomScalemaximumZoomScale属性的值,这两个值最初都设置为1.0

- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView.minimumZoomScale=0.5;
    self.scrollView.maximumZoomScale=6.0;
    self.scrollView.contentSize=CGSizeMake(1280, 960);
    self.scrollView.delegate=self;
   [self.scrollView addSubview:_imageView];
}

这样我们就实现了imageView的一个缩放效果,


pinchZoom.gif

需要注意的是:如果想要通过捏合手势来进行缩放,那至少要指定缩放因子(minimumZoomScalemaximumZoomScale,值不能相同)和实现viewForZoomingInScrollView:代理方法

以编程方式缩放

scrollView可能需要响应于诸如双击或其他轻击手势之类的触摸事件或者响应于除了捏合手势之外的其他用户动作而放大。为了做到这一点,UIScrollView提供了两种方法的实现:setZoomScale:animated:zoomToRect:animated:

1. setZoomScale:animated:

  1. setZoomScale:animated:通过设置当前缩放比例为指定的值来缩放。该值必须是在minimumZoomScalemaximumZoomScale范围内。animated指定是否有动画。也可以直接设置scrollView的zoomScale属性,直接设置属性就相当于时animated为NO的setZoomScale:animated:方法;
  2. 通过该方法或者直接改变zoomScale属性缩放视图时,视图的位置(origin)保持不变
- (IBAction)zoom:(id)sender {
    if (self.mainView.zoomScale > self.mainView.minimumZoomScale) {// 已经放大 现在缩小
        [self.mainView setZoomScale:self.mainView.minimumZoomScale animated:YES];
    }
    else {
        
        [self.mainView setZoomScale:self.mainView.maximumZoomScale animated:YES];
    }
    
    NSLog(@"self.imageView.center: %@  imageView origin: %@",NSStringFromCGPoint(self.imageView.center), NSStringFromCGPoint(self.imageView.frame.origin));
}

效果为


ProgrammaticallyZoom.gif

控制台的打印信息为

self.imageView.center: {207, 368} imageView origin: {138, 245.33333333333331}

self.imageView.center: {276, 490.66666666666663} imageView origin: {138, 245.33333333333329}

可以看到,imageView的origin是没有改变的

2. zoomToRect:animated:

Zooms to a specific area of the content so that it is visible in the receiver.

放大到内容的特定区域,以便在receiver中可见。
参数解释:

  • rect:A rectangle defining an area of the content view. The rectangle should be in the coordinate space of the view returned by viewForZoomingInScrollView:.
    定义内容视图区域的矩形。 矩形应该位于viewForZoomingInScrollView:返回的视图的坐标空间中
  • animated:YES if the scrolling should be animated, NO if it should be immediate.
    animated参数决定了位置和缩放的变化是否会导致动画发生

苹果官方提供了一个示例,

/**
 该方法返回的矩形适合传递给zoomToRect:animated:方法。

 @param scrollView UIScrollView实例
 @param scale 新的缩放比例(通常zoomScale通过添加或乘以缩放量而从现有的缩放比例派生而来)
 @param center 放大缩小的中心点
 @return zoomRect 是以内容视图为坐标系
 */
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(float)scale withCenter:(CGPoint)center {
 
    CGRect zoomRect;
 
    // The zoom rect is in the content view's coordinates.
    // At a zoom scale of 1.0, it would be the size of the
    // imageScrollView's bounds.
    // As the zoom scale decreases, so more content is visible,
    // the size of the rect grows.
    zoomRect.size.height = scrollView.frame.size.height / scale;
    zoomRect.size.width  = scrollView.frame.size.width  / scale;
 
    // choose an origin so as to get the right center.
    zoomRect.origin.x = center.x - (zoomRect.size.width  / 2.0);
    zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
 
    return zoomRect;
}
  • 当用户完成缩放手势或通过代码完成缩放时,会触发scrollViewDidEndZooming:withView:atScale:代理事件。

通过点击进行缩放

通过点击进行缩放的时候, 我们可以通过tap手势和touchesBegan:等方式, 在苹果给出的示例中以touch的方式实现了点击缩放image

In order for your application to support tap to zoom functionality, you do not need to subclass theUIScrollViewclass. Instead you implement the required touch handling in the class for which theUIScrollViewdelegate methodviewForZoomingInScrollView:returns. That class will be responsible for tracking the number of fingers on the screen and the tap count. When it detects a single tap, a double tap, or a two-finger touch, it will respond accordingly. In the case of the double tap and two-finger touch, it should programmatically zoom the scroll view by the appropriate factor.

大致翻译:
为了让您的应用程序支持点击缩放功能,您不需要继承UIScrollView该类。而是在UIScrollView委托方法viewForZoomingInScrollView:返回的类中实现所需的触摸处理 。该类将负责跟踪屏幕上的手指数量和点击次数。当它检测到单击,双击或双指触摸时,它将作出相应的响应。在双击和双指触摸的情况下,应该以适当的因子以编程方式缩放scrollView

实现一个简单的图片放大缩小查看功能

浏览.gif

主要有两部分:

  1. show和dismiss时的过渡动画
  2. 图片的放大缩小

以上面的效果图为例,我这里是通过一个view来实现,在view中添加一个scrollView,scrollView上添加一个imageView来显示图片,并作为viewForZoomingInScrollView :的返回view。
.h文件

- (instancetype)initWithOriginImage:(UIImage *)originImage highlightedImage:(UIImage *)highlightedImage fromRect:(CGRect)fromRect;

- (void)show;

- (void)dismiss;

在初始化的时候传入一个普通图片和一个高清图片,以及图片所在视图(UIImageViewUIButton等)的frame;

动画显示图片

- (void)showOriginImageWithAnimation
{
    self.imageView.frame = self.fromRect;
    if (self.originImage) {
        CGRect finalRect = [self getScaledFinalFrame];
        if (finalRect.size.height > getViewHeight(self.scrollView)) {
            self.scrollView.contentSize = CGSizeMake(getViewWidth(self.scrollView), finalRect.size.height);
        }
        self.alpha = 0.f;
        self.imageView.image = self.originImage;
        [UIView animateWithDuration:_animationDuration animations:^{
            self.imageView.frame = finalRect;
            self.alpha = 1.f;
        } completion:^(BOOL finished) {
            if (self.highlightedImage) {
                self.imageView.image = self.highlightedImage;
            }
        }];
    }
    else {
        self.imageView.frame = self.bounds;
        self.alpha = 0;
        if (self.highlightedImage) {
            self.imageView.image = self.highlightedImage;
        }
        [UIView animateWithDuration:_animationDuration animations:^{
            self.alpha = 1.f;
        } completion:nil];
    }
}

双击手势,实现图片的缩小和放大

- (void)doubleTapGesture:(UITapGestureRecognizer *)tap
{
    if (self.scrollView.zoomScale > _minimumZoomScale) {// 已经放大 现在缩小
        [self.scrollView setZoomScale:_minimumZoomScale animated:YES];
    }
    else {
        // 已经缩小 现在放大
        CGPoint point = [tap locationInView:self.scrollView];
//        [self zoomScrollView:self.scrollView toPoint:point withScale:_maximumZoomScale animated:YES];
        // 方法一 以point为中心点进行放大
        CGRect zoomRect = [self zoomRectForScrollView:self.scrollView withScale:_maximumZoomScale withCenter:point];
        [self.scrollView zoomToRect:zoomRect animated:YES];
        // 方法二 也可以通过这种方法 来放大 这种是直接放大 以scrollView的中心点
//        [self.scrollView setZoomScale:_maximumZoomScale animated:YES];
    }
}

消失动画 也是单击时调用的方法

- (void)dismiss
{
    CGRect frame = self.fromRect;
    CGFloat originX = self.scrollView.contentOffset.x + frame.origin.x;
    CGFloat originY = self.scrollView.contentOffset.y + frame.origin.y;
    frame.origin = CGPointMake(originX, originY);
    
    [UIView animateWithDuration:_animationDuration animations:^{
        self.imageView.frame = frame;
        self.alpha = 0.f;
    } completion:^(BOOL finished) {
        [self removeFromSuperview];
    }];
}

当然别忘了实现scrollView的代理方法

  • 返回imageView作为缩放视图
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

缩放view的时候调整imageView的位置

// any zoom scale changes
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    CGFloat scrollW = CGRectGetWidth(scrollView.frame);
    CGFloat scrollH = CGRectGetHeight(scrollView.frame);

    CGSize contentSize = scrollView.contentSize;
    CGFloat offsetX = scrollW > contentSize.width ? (scrollW - contentSize.width) * 0.5 : 0;
    CGFloat offsetY = scrollH > contentSize.height ? (scrollH - contentSize.height) * 0.5 : 0;

    CGFloat centerX = contentSize.width * 0.5 + offsetX;
    CGFloat centerY = contentSize.height * 0.5 + offsetY;

    self.imageView.center = CGPointMake(centerX, centerY);
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容

  • *7月8日上午 N:Block :跟一个函数块差不多,会对里面所有的内容的引用计数+1,想要解决就用__block...
    炙冰阅读 2,492评论 1 14
  • { 11、核心动画 需要签协议,但是系统帮签好 一、CABasicAnimation 1、创建基础动画对象 CAB...
    CYC666阅读 1,557评论 2 4
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,791评论 22 665
  • 《世说新语》是我国魏晋南北朝时期「志人小说」的代表作,为言谈、轶事的笔记体短篇小说,由南朝宋刘义庆编撰。从这部书的...
    WTF真麻烦阅读 1,323评论 0 4
  • 情感太平衡会陌生 情感太失衡会难守 我半抱你的肩头 你推拒我的笑容 朋友,你似乎不知我的感受 我黯然了脸色 你皱了...
    殷殷囷囷阅读 128评论 4 4