iOS- XKZoomingView 簡單的圖片縮放預覽,支持橫屏、長圖【手勢:單擊、雙擊、放大縮小】

来源:https://www.cnblogs.com/wangkejia/archive/2018/11/27/10025041.html
-Advertisement-
Play Games

XKZoomingView.h XKZoomingView.m USE ELSE ...


XKZoomingView.h

#import <UIKit/UIKit.h>

@interface XKZoomingView : UIScrollView
/**
 本地圖片
 */
@property (nonatomic, strong) UIImage *mainImage;

/**
 圖片顯示
 */
@property (nonatomic, strong) UIImageView *mainImageView;
@end

XKZoomingView.m

#import "XKZoomingView.h"
@interface XKZoomingView()<UIScrollViewDelegate>
/**
 當前圖片偏移量
 */
@property (nonatomic,assign) CGPoint currPont;
@end
@implementation XKZoomingView

- (instancetype)init{
    self = [super init];
    if (self) {
        [self setup];
    }
    return self;
}
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}
- (void)setup{
    self.backgroundColor = [[UIColor grayColor]colorWithAlphaComponent:0.2];
    self.delegate = self;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    self.decelerationRate = UIScrollViewDecelerationRateFast;
    self.maximumZoomScale = 3;
    self.minimumZoomScale = 1;
    self.alwaysBounceHorizontal = NO;
    self.alwaysBounceVertical = NO;
    self.layer.masksToBounds = YES;
    if (@available(iOS 11.0, *)) {
        self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
    [self addSubview:self.mainImageView];

    _currPont = CGPointZero;

    ///監聽屏幕旋轉
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
    ///單擊
    UITapGestureRecognizer *tapSingle = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapSingleSponse:)];
    //設置手勢屬性
    tapSingle.numberOfTapsRequired = 1;
    tapSingle.delaysTouchesEnded = NO;
    [self.mainImageView addGestureRecognizer:tapSingle];
    ///雙擊
    UITapGestureRecognizer *tapDouble = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapDoubleSponse:)];
    //設置手勢屬性
    tapDouble.numberOfTapsRequired = 2;
    [self.mainImageView addGestureRecognizer:tapDouble];
    ///避免手勢衝突
    [tapSingle requireGestureRecognizerToFail:tapDouble];
}

#pragma mark - layoutSubviews
- (void)layoutSubviews{
    [super layoutSubviews];
    ///放大或縮小中
    if (self.zooming || self.zoomScale != 1.0 || self.zoomBouncing) {
        return;
    }
    
    ///設置圖片尺寸
    if (_mainImage) {
        CGRect imgRect = [self getImageViewFrame];
        self.mainImageView.frame = imgRect;
        ///設置content size
        if (CGRectGetHeight(imgRect) > CGRectGetHeight(self.frame)) {
            [self setContentSize:CGSizeMake(CGRectGetWidth(self.frame), CGRectGetHeight(imgRect))];
        }
        else{
            [self setContentSize:CGSizeMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];
        }
    }
    
}
- (void)setMainImage:(UIImage *)mainImage{
    _mainImage = mainImage;
    self.mainImageView.image = _mainImage;
    [self setContentOffset:CGPointMake(0, 0)];
    [self setNeedsLayout];
}

/**
 根據圖片原始大小,獲取圖片顯示大小
 @return CGRect
 */
- (CGRect)getImageViewFrame{
    if (_mainImage) {
        CGRect imageRect;
        CGFloat scWidth = self.frame.size.width;
        CGFloat scHeight = self.frame.size.height;
        ///width
        if (_mainImage.size.width > scWidth) {
            imageRect.size.width = scWidth;
            CGFloat ratioHW = _mainImage.size.height/_mainImage.size.width;
            imageRect.size.height = ratioHW * imageRect.size.width;
            imageRect.origin.x = 0;
        }
        else{
            imageRect.size.width = _mainImage.size.width;
            imageRect.size.height = _mainImage.size.height;
            imageRect.origin.x = (scWidth - imageRect.size.width)/2;
        }
        ///height
        if (imageRect.size.height > scHeight) {
            
            imageRect.origin.y = 0;
        }
        else{
            imageRect.origin.y = (scHeight - imageRect.size.height)/2;
        }
        return imageRect;
    }
    return CGRectZero;
}
/**
 獲取點擊位置後所需的偏移量【目的是呈現點擊位置在試圖上】
 
 @param location 點擊位置
 */
- (void)zoomingOffset:(CGPoint)location{
    CGFloat lo_x = location.x * self.zoomScale;
    CGFloat lo_y = location.y * self.zoomScale;
    
    CGFloat off_x;
    CGFloat off_y;
    ///off_x
    if (lo_x < CGRectGetWidth(self.frame)/2) {
        off_x = 0;
    }
    else if (lo_x > self.contentSize.width - CGRectGetWidth(self.frame)/2){
        off_x = self.contentSize.width - CGRectGetWidth(self.frame);
    }
    else{
        off_x = lo_x - CGRectGetWidth(self.frame)/2;
    }
    
    ///off_y
    if (lo_y < CGRectGetHeight(self.frame)/2) {
        off_y = 0;
    }
    else if (lo_y > self.contentSize.height - CGRectGetHeight(self.frame)/2){
        if (self.contentSize.height <= CGRectGetHeight(self.frame)) {
            off_y = 0;
        }
        else{
            off_y = self.contentSize.height - CGRectGetHeight(self.frame);
        }
        
    }
    else{
        off_y = lo_y - CGRectGetHeight(self.frame)/2;
    }
    [self setContentOffset:CGPointMake(off_x, off_y)];
}

#pragma mark - 重置圖片
- (void)resetImageViewState{
    self.zoomScale = 1;
    _mainImage = nil;;
    self.mainImageView.image = nil;
   
}
#pragma mark - 變數
- (UIImageView *)mainImageView {
    if (!_mainImageView) {
        _mainImageView = [UIImageView new];
        _mainImageView.image = nil;
        _mainImageView.contentMode = UIViewContentModeScaleAspectFit;
        _mainImageView.userInteractionEnabled = YES;
    }
    return _mainImageView;
}


#pragma mark - 單擊
- (void)tapSingleSponse:(UITapGestureRecognizer *)singleTap{
    if (!self.mainImageView.image) {
        return;
    }
    
    if (self.zoomScale != 1) {
        [UIView animateWithDuration:0.2 animations:^{
            self.zoomScale = 1;
        } completion:^(BOOL finished) {
            [self setContentOffset:_currPont animated:YES];
        }];
    }
}
#pragma mark - 雙擊
- (void)tapDoubleSponse:(UITapGestureRecognizer *)doubleTap{
    if (!self.mainImageView.image) {
        return;
    }
    CGPoint point = [doubleTap locationInView:self.mainImageView];
    if (self.zoomScale == 1) {
        [UIView animateWithDuration:0.2 animations:^{
            self.zoomScale = 2.0;
            [self zoomingOffset:point];
        }];
    }
    else{
        [UIView animateWithDuration:0.2 animations:^{
            self.zoomScale = 1;
        } completion:^(BOOL finished) {
            [self setContentOffset:_currPont animated:YES];
        }];
    }
}


#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    if (!self.mainImageView.image) {
        return;
    }
    CGRect imageViewFrame = self.mainImageView.frame;
    CGFloat width = imageViewFrame.size.width,
    height = imageViewFrame.size.height,
    sHeight = scrollView.bounds.size.height,
    sWidth = scrollView.bounds.size.width;
    if (height > sHeight) {
        imageViewFrame.origin.y = 0;
    } else {
        imageViewFrame.origin.y = (sHeight - height) / 2.0;
    }
    if (width > sWidth) {
        imageViewFrame.origin.x = 0;
    } else {
        imageViewFrame.origin.x = (sWidth - width) / 2.0;
    }
    self.mainImageView.frame = imageViewFrame;
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.mainImageView;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (self.isZooming || self.zoomScale != 1) {
        return;
    }
    _currPont = scrollView.contentOffset;
    
}
#pragma mark - 監聽屏幕旋轉通知
- (void)statusBarOrientationChange:(NSNotification *)notification{
    self.zoomScale = 1;
}
- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
}

@end

USE

XKZoomingView *zoomView = [[XKZoomingView alloc]init];
zoomView.frame = self.view.bounds;
zoomView.mainImage = [UIImage imageNamed:@""];
[self.view addSubview:zoomView];

 ELSE

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Hbase介紹 HBase 是一個 "開源" 的 "非關係型" "分散式資料庫" (NoSQL),它參考了 "谷歌" 的 "BigTable" 建模,實現的編程語言為 "Java" 。它是 "Apache軟體基金會" 的 "Hadoop" 項目的一部分,運行於 "HDFS" 文件系統之上,為 "Ha ...
  • linux架設oracle,用plsql連接資料庫時候報如下錯誤 :“資料庫字元集和客戶端字元集(2%)是不同的,字元集轉化可能會造成不可預期的後果”,問題解決 ...
  • 一、Apple 開發者網站 官方文檔 https://developer.apple.com/documentation 除了這兩個、還有 Core Foundation Core Animation Core Image 等、 趕快點擊網站去瞧瞧吧 https://developer.apple. ...
  • Android activity的生命周期這一張圖就夠了。 驗證結果: 值得註意的是從activity A >activity B是先執行A的onPause然後走B的生命周期最後才走A的onStop,同理從activity B按返回鍵時B進入onPause 然後走完A的生命周期在走B的onStop和 ...
  • 如何實現給分類“添加成員變數”? 預設情況下,因為分類底層結構的限制,不能添加成員變數到分類中。但可以通過關聯對象來間接實現 關聯對象提供了以下API 1> 添加關聯對象 void objc_setAssociatedObject(id object, const void * key, id va ...
  • 1. GCD 簡介 Grand Central Dispatch(GCD)是非同步執行任務的技術之一。一般將應用程式中記述的線程管理用的代碼在系統級中實現。開發者只需要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。由於線程管理是作為系統的一部分來 ...
  • 為了更好的用戶體驗,煎熬了許久,得到這麼個解決方案。在此記錄下來,以供後來者參考。 第一部分 清單文件中組件activity的android:windowSoftInputMode屬性值的含義: 【A】stateUnspecified:軟鍵盤的狀態並沒有指定,系統將選擇一個合適的狀態或依賴於主題的設 ...
  • 簡介: Application和Activity、Service一樣,都是Android框架的一個系統組件,每一個應用都有一個Application,Application的生命周期也就是整個app的生命周期。 特點: 實例的創建方式:單例模式 每一個app運行是會首先會創建Application類 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...