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
更多相關文章
  • 可以使用--ignore-table=dbname.tablename 忽略一張表 ...
  • ```sql CREATE TABLE IF NOT EXISTS ( INT NOT NULL AUTO_INCREMENT, VARCHAR(45) NOT NULL, VARCHAR(2048) NULL, VARCHAR(45) NOT NULL, TIMESTAMP NOT NULL DE ...
  • MySQL資料庫語法 資料庫管理系統(DBMS)的概述 1. 什麼是DBMS:數據的倉庫 方便查詢 可存儲的數據量大 保證數據的完整、一致 安全可靠 2. DBMS的發展:今天主流資料庫為關係型資料庫管理系統(RDBMS 使用表格存儲數據) 3. 常見DBMS:Orcale、MySQL、SQL Se ...
  • --上一月,上一年 select add_months(sysdate,-1) last_month,add_months(sysdate,-12) last_year from dual; --下一月,下一年 select add_months(sysdate,1) last_month,add_... ...
  • ---https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_8003.htm drop user geovin; drop user geovindu; create user geovindu identified by... ...
  • 一、簡介: ListView,列表視圖,直接繼承了AbsListView,是一個以垂直方式在項目中顯示View視圖的列表。ListView的數據項,來自一個繼承了ListAdapter介面的適配器。 二、新建一個包listview並新建ListViewActivity.java活動: 三、在Andr ...
  • The Android ION memory allocator "英文原文" ION heaps ION設計的目標 為了避免記憶體碎片化,或者為一些有著特殊記憶體需求的硬體,比如GPUs、display controller以及camera等,在系統啟動的時候,會為他們預留一些memory pools ...
  • 今天項目因為元數據被拒,再次提交去編輯APP時,發現進不了我的APP界面,出現瞭如下情況,大概有10多分鐘 ,一直進不去 ,公司網路一直不穩定,於是打開了我的VPN,然後就可以了。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...