1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問(wèn)題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      這篇文章主要介紹iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

      創(chuàng)新互聯(lián)專注于網(wǎng)站建設(shè),為客戶提供成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)開(kāi)發(fā)服務(wù),多年建網(wǎng)站服務(wù)經(jīng)驗(yàn),各類網(wǎng)站都可以開(kāi)發(fā),成都品牌網(wǎng)站建設(shè),公司官網(wǎng),公司展示網(wǎng)站,網(wǎng)站設(shè)計(jì),建網(wǎng)站費(fèi)用,建網(wǎng)站多少錢,價(jià)格優(yōu)惠,收費(fèi)合理。

      前言

      相信大家應(yīng)該都有所體會(huì),裁剪圖片功能在很多上傳圖片的場(chǎng)景里都需要用到,一方面應(yīng)用服務(wù)器可能對(duì)圖片的尺寸大小有限制,因而希望上傳的圖片都是符合規(guī)定的,另一方面,用戶可能希望只上傳圖片中的部分內(nèi)容,突出圖片中關(guān)鍵的信息。而為了滿足用戶多種多樣的裁剪需求,就需要裁剪圖片時(shí)能支持由用戶動(dòng)態(tài)地改變裁剪范圍、裁剪尺寸等。

      動(dòng)態(tài)裁剪圖片的基本過(guò)程大致可以分為以下幾步

      • 顯示圖片與裁剪區(qū)域

      • 支持移動(dòng)和縮放圖片

      • 支持手勢(shì)改變裁剪區(qū)域

      • 進(jìn)行圖片裁剪并獲得裁剪后的圖片

      顯示圖片與裁剪區(qū)域

      顯示圖片

      在裁剪圖片之前,首先我們要在頁(yè)面上顯示待裁剪的圖片,如下圖所示

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      這一步比較簡(jiǎn)單,配置一個(gè) UIImageView 來(lái)放置圖片即可。但是要注意一點(diǎn),UIImageView 有多種 contentMode,最常見(jiàn)有三種

      • UIViewContentModeScaleToFill

      • UIViewContentModeScaleAspectFit

      • UIViewContentModeScaleAspectFill

      三者區(qū)別可以看下面的比較

      UIViewContentModeScaleToFill

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      UIViewContentModeScaleAspectFit

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      UIViewContentModeScaleAspectFill

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      可以看出,ScaleToFill 會(huì)改變圖片的長(zhǎng)寬比例來(lái)鋪滿整個(gè) UIImageView,ScaleAspectFill 則會(huì)保持圖片比例來(lái)鋪滿,從而會(huì)有部分圖片內(nèi)容超出 UIImageView 區(qū)域的情況,而 ScaleAspectFit 則會(huì)保證圖片比例不變,同時(shí)圖片內(nèi)容都顯示在 UIImageView 中,即使無(wú)法鋪滿 UIImageView。

      因此不同顯示模式會(huì)影響到我們最終顯示到屏幕上的圖片的樣子,而在裁剪過(guò)程中最理想的放置圖片的模式則是,圖片的短邊剛好鋪滿裁剪區(qū)域的短邊,而長(zhǎng)邊至少不會(huì)小于裁剪區(qū)域的長(zhǎng)邊,這就要求我們要考慮裁剪區(qū)域的長(zhǎng)寬來(lái)放置我們的圖片。

      裁剪區(qū)域

      接下來(lái)我們要放置我們的裁剪區(qū)域,它的樣子如下所示

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      裁剪區(qū)域本身就是在 UIImageView 上放上一層 UIView,再在 UIView 上繪制出一個(gè)白邊框的方格 Layer。

      首先自定義一個(gè) CAShapeLayer

      #import 
      
      @interface YasicClipAreaLayer : CAShapeLayer
      
      @property(assign, nonatomic) NSInteger cropAreaLeft;
      @property(assign, nonatomic) NSInteger cropAreaTop;
      @property(assign, nonatomic) NSInteger cropAreaRight;
      @property(assign, nonatomic) NSInteger cropAreaBottom;
      
      - (void)setCropAreaLeft:(NSInteger)cropAreaLeft CropAreaTop:(NSInteger)cropAreaTop CropAreaRight:(NSInteger)cropAreaRight CropAreaBottom:(NSInteger)cropAreaBottom;
      
      
      @end
      
      @implementation YasicClipAreaLayer
      
      - (instancetype)init
      {
       self = [super init];
       if (self) {
       _cropAreaLeft = 50;
       _cropAreaTop = 50;
       _cropAreaRight = SCREEN_WIDTH - self.cropAreaLeft;
       _cropAreaBottom = 400;
       }
       return self;
      }
      
      - (void)drawInContext:(CGContextRef)ctx
      {
       UIGraphicsPushContext(ctx);
       
       CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
       CGContextSetLineWidth(ctx, lineWidth);
       CGContextMoveToPoint(ctx, self.cropAreaLeft, self.cropAreaTop);
       CGContextAddLineToPoint(ctx, self.cropAreaLeft, self.cropAreaBottom);
       CGContextSetShadow(ctx, CGSizeMake(2, 0), 2.0);
       CGContextStrokePath(ctx);
       
       CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
       CGContextSetLineWidth(ctx, lineWidth);
       CGContextMoveToPoint(ctx, self.cropAreaLeft, self.cropAreaTop);
       CGContextAddLineToPoint(ctx, self.cropAreaRight, self.cropAreaTop);
       CGContextSetShadow(ctx, CGSizeMake(0, 2), 2.0);
       CGContextStrokePath(ctx);
       
       CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
       CGContextSetLineWidth(ctx, lineWidth);
       CGContextMoveToPoint(ctx, self.cropAreaRight, self.cropAreaTop);
       CGContextAddLineToPoint(ctx, self.cropAreaRight, self.cropAreaBottom);
       CGContextSetShadow(ctx, CGSizeMake(-2, 0), 2.0);
       CGContextStrokePath(ctx);
       
       CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
       CGContextSetLineWidth(ctx, lineWidth);
       CGContextMoveToPoint(ctx, self.cropAreaLeft, self.cropAreaBottom);
       CGContextAddLineToPoint(ctx, self.cropAreaRight, self.cropAreaBottom);
       CGContextSetShadow(ctx, CGSizeMake(0, -2), 2.0);
       CGContextStrokePath(ctx);
       
       UIGraphicsPopContext();
      }
      
      - (void)setCropAreaLeft:(NSInteger)cropAreaLeft
      {
       _cropAreaLeft = cropAreaLeft;
       [self setNeedsDisplay];
      }
      
      - (void)setCropAreaTop:(NSInteger)cropAreaTop
      {
       _cropAreaTop = cropAreaTop;
       [self setNeedsDisplay];
      }
      
      - (void)setCropAreaRight:(NSInteger)cropAreaRight
      {
       _cropAreaRight = cropAreaRight;
       [self setNeedsDisplay];
      }
      
      - (void)setCropAreaBottom:(NSInteger)cropAreaBottom
      {
       _cropAreaBottom = cropAreaBottom;
       [self setNeedsDisplay];
      }
      
      - (void)setCropAreaLeft:(NSInteger)cropAreaLeft CropAreaTop:(NSInteger)cropAreaTop CropAreaRight:(NSInteger)cropAreaRight CropAreaBottom:(NSInteger)cropAreaBottom
      {
       _cropAreaLeft = cropAreaLeft;
       _cropAreaRight = cropAreaRight;
       _cropAreaTop = cropAreaTop;
       _cropAreaBottom = cropAreaBottom;
       
       [self setNeedsDisplay];
      }
      
      @end

      這里 layer 有幾個(gè)屬性 cropAreaLeft、cropAreaRight、cropAreaTop、cropAreaBottom,從命名上可以看出這幾個(gè)屬性定義了這個(gè) layer 上繪制的白邊框裁剪區(qū)域的坐標(biāo)信息。還暴露了一個(gè)方法用于配置這四個(gè)屬性。

      然后在 CAShapeLayer 內(nèi)部,重點(diǎn)在于復(fù)寫 drawInContext 方法,這個(gè)方法負(fù)責(zé)直接在圖層上繪圖,復(fù)寫的方法主要做的事情是根據(jù)上面四個(gè)屬性 cropAreaLeft、cropAreaRight、cropAreaTop、cropAreaBottom 繪制出封閉的四條線,這樣就能表示裁剪區(qū)域的邊界了。

      要注意的是 drawInContext 方法不能手動(dòng)顯示調(diào)用,必須通過(guò)調(diào)用 setNeedsDisplay 或者 setNeedsDisplayInRect 讓系統(tǒng)自動(dòng)調(diào)該方法。

      在裁剪頁(yè)面里,我們放置了一個(gè) cropView,然后將自定義的 CAShaplayer 加入到這個(gè) view 上

       self.cropView.layer.sublayers = nil;
       YasicClipAreaLayer * layer = [[YasicClipAreaLayer alloc] init];
       
       CGRect cropframe = CGRectMake(self.cropAreaX, self.cropAreaY, self.cropAreaWidth, self.cropAreaHeight);
       UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:self.cropView.frame cornerRadius:0];
       UIBezierPath * cropPath = [UIBezierPath bezierPathWithRect:cropframe];
       [path appendPath:cropPath];
       layer.path = path.CGPath;
       
       layer.fillRule = kCAFillRuleEvenOdd;
       layer.fillColor = [[UIColor blackColor] CGColor];
       layer.opacity = 0.5;
       
       layer.frame = self.cropView.bounds;
       [layer setCropAreaLeft:self.cropAreaX CropAreaTop:self.cropAreaY CropAreaRight:self.cropAreaX + self.cropAreaWidth CropAreaBottom:self.cropAreaY + self.cropAreaHeight];
       [self.cropView.layer addSublayer:layer];
       [self.view bringSubviewToFront:self.cropView];

      這里主要是為了用自定義的 CAShapelayer 產(chǎn)生出空心遮罩的效果,從而出現(xiàn)中心的裁剪區(qū)域高亮而四周非裁剪區(qū)域有蒙層的效果,示意圖如下

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      所以首先確定了 cashapelayer 的大小為 cropview 的大小,生成一個(gè)對(duì)應(yīng)的 UIBezierPath,然后根據(jù)裁剪區(qū)域的大?。ㄓ?self.cropAreaX, self.cropAreaY, self.cropAreaWidth, self.cropAreaHeight 確定)生成空心遮罩的內(nèi)圈 UIBezierPath,

      CGRect cropframe = CGRectMake(self.cropAreaX, self.cropAreaY, self.cropAreaWidth, self.cropAreaHeight);
       UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:self.cropView.frame cornerRadius:0];
       UIBezierPath * cropPath = [UIBezierPath bezierPathWithRect:cropframe];
       [path appendPath:cropPath];
       layer.path = path.CGPath;

      然后將這個(gè) path 配置給 CAShapeLayer,并將 CAShapeLayer 的 fillRule 配置為 kCAFillRuleEvenOdd

       layer.fillRule = kCAFillRuleEvenOdd;
       layer.fillColor = [[UIColor blackColor] CGColor];
       layer.opacity = 0.5;
       layer.frame = self.cropView.bounds;

      其中 fillRule 屬性表示使用哪一種算法去判斷畫布上的某區(qū)域是否屬于該圖形“內(nèi)部”,內(nèi)部區(qū)域?qū)⒈惶畛漕伾?,主要有兩種方式

      kCAFillRuleNonZero,這種算法判斷規(guī)則是,如果從某一點(diǎn)射出任意方向射線,與對(duì)應(yīng) Layer 交點(diǎn)為 0 則不在 Layer 內(nèi),大于 0 則在 畫布內(nèi)

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      kCAFillRuleEvenOdd 如果從某一點(diǎn)射出任意射線,與對(duì)應(yīng) Layer 交點(diǎn)為偶數(shù)則在畫布內(nèi),否則不在畫布內(nèi)

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      再給 CAShapeLayer 設(shè)置蒙層顏色為透明度 0.5 的黑色,就可以實(shí)現(xiàn)空心蒙層效果了。

      最后就是設(shè)置 layer 的四個(gè)屬性并繪制內(nèi)邊框的白色邊線。

       [layer setCropAreaLeft:self.cropAreaX CropAreaTop:self.cropAreaY CropAreaRight:self.cropAreaX + self.cropAreaWidth CropAreaBottom:self.cropAreaY + self.cropAreaHeight];
       [self.cropView.layer addSublayer:layer];
       [self.view bringSubviewToFront:self.cropView];

      合理放置圖片

      到這一步我們正確顯示了圖片,也正確顯示出了裁剪區(qū)域,但是我們沒(méi)有將二者的約束關(guān)系建立起來(lái),因此可能會(huì)出現(xiàn)下面這樣的情況

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      可以看到這里由于這張圖片的 width 遠(yuǎn)大于 height,因此會(huì)在裁剪區(qū)域內(nèi)出現(xiàn)黑色區(qū)域,這對(duì)用戶來(lái)說(shuō)是一種不好的體驗(yàn),同時(shí)也會(huì)影響到我們后面的裁剪步驟,究其原因是因?yàn)槲覀儧](méi)有針對(duì)裁剪區(qū)域的寬高來(lái)放置 UIImageView,我們希望最理想的效果是,能在裁剪區(qū)域內(nèi)實(shí)現(xiàn)類似 UIViewContentModeScaleAspectFill 的效果,也就是圖片保持比例地鋪滿裁剪區(qū)域,并允許部分內(nèi)容超出裁剪區(qū)域,這就要求

      • 當(dāng)圖片寬與裁剪區(qū)域?qū)捴却笥趫D片高與裁剪區(qū)域高之比時(shí),將圖片高鋪滿裁剪區(qū)域高,圖片寬成比例放大

      • 當(dāng)圖片高與裁剪區(qū)域高之比大于圖片寬與裁剪區(qū)域?qū)捴葧r(shí),將圖片寬鋪滿裁剪區(qū)域?qū)?,圖片高成比例方法

      這里我們用到 Masonry 來(lái)做這些布局操作

       CGFloat tempWidth = 0.0;
       CGFloat tempHeight = 0.0;
       
       if (self.targetImage.size.width/self.cropAreaWidth <= self.targetImage.size.height/self.cropAreaHeight) {
       tempWidth = self.cropAreaWidth;
       tempHeight = (tempWidth/self.targetImage.size.width) * self.targetImage.size.height;
       } else if (self.targetImage.size.width/self.cropAreaWidth > self.targetImage.size.height/self.cropAreaHeight) {
       tempHeight = self.cropAreaHeight;
       tempWidth = (tempHeight/self.targetImage.size.height) * self.targetImage.size.width;
       }
       
       [self.bigImageView mas_updateConstraints:^(MASConstraintMaker *make) {
       make.left.mas_equalTo(self.cropAreaX - (tempWidth - self.cropAreaWidth)/2);
       make.top.mas_equalTo(self.cropAreaY - (tempHeight - self.cropAreaHeight)/2);
       make.width.mas_equalTo(tempWidth);
       make.height.mas_equalTo(tempHeight);
       }];

      可以看到,我們進(jìn)行了兩步判斷,從而獲得合適的寬高值,然后將圖片進(jìn)行布局,在自動(dòng)布局時(shí)將圖片中心與裁剪區(qū)域中心重合,最后我們會(huì)得到下面的效果圖

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      支持移動(dòng)和縮放圖片

      正如上面所講,由于圖片在裁剪區(qū)域內(nèi)是以類似 UIViewContentModeScaleAspectFill 的方式放置的,很可能出現(xiàn)部分內(nèi)容溢出裁剪區(qū)域,因此我們要讓圖片能支持動(dòng)態(tài)移動(dòng)和縮放,從而使用戶能靈活地裁剪圖片的內(nèi)容。

      具體實(shí)現(xiàn)上,我們其實(shí)是在 cropview 上加上手勢(shì),間接操作圖片的尺寸和位置,這樣有助于后面我們實(shí)現(xiàn)動(dòng)態(tài)改變裁剪區(qū)域的實(shí)現(xiàn)。

      縮放功能

      這里實(shí)現(xiàn)縮放的原理實(shí)際是對(duì)放置圖片的 UIImageView 的 frame 進(jìn)行修改,首先我們要記錄下最初的 UIImageView 的 frame

      self.originalFrame = CGRectMake(self.cropAreaX - (tempWidth - self.cropAreaWidth)/2, self.cropAreaY - (tempHeight - self.cropAreaHeight)/2, tempWidth, tempHeight);

      然后為 cropView 添加手勢(shì)

       // 捏合手勢(shì)
       UIPinchGestureRecognizer *pinGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleCenterPinGesture:)];
       [self.view addGestureRecognizer:pinGesture];

      然后是手勢(shì)處理函數(shù)

      -(void)handleCenterPinGesture:(UIPinchGestureRecognizer *)pinGesture
      {
       CGFloat scaleRation = 3;
       UIView * view = self.bigImageView;
       
       // 縮放開(kāi)始與縮放中
       if (pinGesture.state == UIGestureRecognizerStateBegan || pinGesture.state == UIGestureRecognizerStateChanged) {
       // 移動(dòng)縮放中心到手指中心
       CGPoint pinchCenter = [pinGesture locationInView:view.superview];
       CGFloat distanceX = view.frame.origin.x - pinchCenter.x;
       CGFloat distanceY = view.frame.origin.y - pinchCenter.y;
       CGFloat scaledDistanceX = distanceX * pinGesture.scale;
       CGFloat scaledDistanceY = distanceY * pinGesture.scale;
       CGRect newFrame = CGRectMake(view.frame.origin.x + scaledDistanceX - distanceX, view.frame.origin.y + scaledDistanceY - distanceY, view.frame.size.width * pinGesture.scale, view.frame.size.height * pinGesture.scale);
       view.frame = newFrame;
       pinGesture.scale = 1;
       }
       
       // 縮放結(jié)束
       if (pinGesture.state == UIGestureRecognizerStateEnded) {
       CGFloat ration = view.frame.size.width / self.originalFrame.size.width;
       
       // 縮放過(guò)大
       if (ration > 5) {
       CGRect newFrame = CGRectMake(0, 0, self.originalFrame.size.width * scaleRation, self.originalFrame.size.height * scaleRation);
       view.frame = newFrame;
       }
       
       // 縮放過(guò)小
       if (ration < 0.25) {
       view.frame = self.originalFrame;
       }
       // 對(duì)圖片進(jìn)行位置修正
       CGRect resetPosition = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
       
       if (resetPosition.origin.x >= self.cropAreaX) {
       resetPosition.origin.x = self.cropAreaX;
       }
       if (resetPosition.origin.y >= self.cropAreaY) {
       resetPosition.origin.y = self.cropAreaY;
       }
       if (resetPosition.size.width + resetPosition.origin.x < self.cropAreaX + self.cropAreaWidth) {
       CGFloat movedLeftX = fabs(resetPosition.size.width + resetPosition.origin.x - (self.cropAreaX + self.cropAreaWidth));
       resetPosition.origin.x += movedLeftX;
       }
       if (resetPosition.size.height + resetPosition.origin.y < self.cropAreaY + self.cropAreaHeight) {
       CGFloat moveUpY = fabs(resetPosition.size.height + resetPosition.origin.y - (self.cropAreaY + self.cropAreaHeight));
       resetPosition.origin.y += moveUpY;
       }
       view.frame = resetPosition;
       
       // 對(duì)圖片縮放進(jìn)行比例修正,防止過(guò)小
       if (self.cropAreaX < self.bigImageView.frame.origin.x
       || ((self.cropAreaX + self.cropAreaWidth) > self.bigImageView.frame.origin.x + self.bigImageView.frame.size.width)
       || self.cropAreaY < self.bigImageView.frame.origin.y
       || ((self.cropAreaY + self.cropAreaHeight) > self.bigImageView.frame.origin.y + self.bigImageView.frame.size.height)) {
       view.frame = self.originalFrame;
       }
       }
      }

      在手勢(shì)處理時(shí),要注意,為了能跟隨用戶捏合手勢(shì)的中心進(jìn)行縮放,我們要在手勢(shì)過(guò)程中移動(dòng)縮放中心到手指中心,這里我們判斷了 pinGesture 的 state 來(lái)確定手勢(shì)開(kāi)始、進(jìn)行中和結(jié)束階段。

       if (pinGesture.state == UIGestureRecognizerStateBegan || pinGesture.state == UIGestureRecognizerStateChanged) {
       // 移動(dòng)縮放中心到手指中心
       CGPoint pinchCenter = [pinGesture locationInView:view.superview];
       CGFloat distanceX = view.frame.origin.x - pinchCenter.x;
       CGFloat distanceY = view.frame.origin.y - pinchCenter.y;
       CGFloat scaledDistanceX = distanceX * pinGesture.scale;
       CGFloat scaledDistanceY = distanceY * pinGesture.scale;
       CGRect newFrame = CGRectMake(view.frame.origin.x + scaledDistanceX - distanceX, view.frame.origin.y + scaledDistanceY - distanceY, view.frame.size.width * pinGesture.scale, view.frame.size.height * pinGesture.scale);
       view.frame = newFrame;
       pinGesture.scale = 1;
       }

      pinchCenter 就是捏合手勢(shì)的中心,我們獲取到當(dāng)前圖片 view 的 frame,然后計(jì)算當(dāng)前 view 與手勢(shì)中心的 x、y 坐標(biāo)差,再根據(jù)手勢(shì)縮放值 scale,創(chuàng)建出新的 frame

       CGRect newFrame = CGRectMake(view.frame.origin.x + scaledDistanceX - distanceX, view.frame.origin.y + scaledDistanceY - distanceY, view.frame.size.width * pinGesture.scale, view.frame.size.height * pinGesture.scale);

      這個(gè) frame 的中心坐標(biāo)就在縮放手勢(shì)的中心,將新的 frame 賦值給圖片 view,從而實(shí)現(xiàn)依據(jù)手勢(shì)中心進(jìn)行縮放的效果。

      而在手勢(shì)結(jié)束階段,我們要對(duì)圖片縮放進(jìn)行邊界保護(hù),既不能放大過(guò)大,也不能縮小過(guò)小。

      CGFloat ration = view.frame.size.width / self.originalFrame.size.width;
       
       // 縮放過(guò)大
       if (ration > 5) {
       CGRect newFrame = CGRectMake(0, 0, self.originalFrame.size.width * scaleRation, self.originalFrame.size.height * scaleRation);
       view.frame = newFrame;
       }
       
       // 縮放過(guò)小
       if (ration < 0.25) {
       view.frame = self.originalFrame;
       }

      同時(shí)縮放后如果圖片與裁剪區(qū)域出現(xiàn)了空白區(qū)域,還要對(duì)圖片的位置進(jìn)行修正以保證圖片始終是覆蓋全裁剪區(qū)域的。

      // 對(duì)圖片進(jìn)行位置修正
       CGRect resetPosition = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
       
       if (resetPosition.origin.x >= self.cropAreaX) {
        resetPosition.origin.x = self.cropAreaX;
       }
       if (resetPosition.origin.y >= self.cropAreaY) {
        resetPosition.origin.y = self.cropAreaY;
       }
       if (resetPosition.size.width + resetPosition.origin.x < self.cropAreaX + self.cropAreaWidth) {
        CGFloat movedLeftX = fabs(resetPosition.size.width + resetPosition.origin.x - (self.cropAreaX + self.cropAreaWidth));
        resetPosition.origin.x += movedLeftX;
       }
       if (resetPosition.size.height + resetPosition.origin.y < self.cropAreaY + self.cropAreaHeight) {
        CGFloat moveUpY = fabs(resetPosition.size.height + resetPosition.origin.y - (self.cropAreaY + self.cropAreaHeight));
        resetPosition.origin.y += moveUpY;
       }
       view.frame = resetPosition;

      這里我們通過(guò)生成當(dāng)前圖片的 CGRect,與裁剪區(qū)域的邊界進(jìn)行如下比較

      • 圖片左邊線大于裁剪區(qū)域左邊線時(shí)圖片移動(dòng)到裁剪區(qū)域 x 值

      • 圖片上邊線大于裁剪區(qū)域上邊線時(shí)圖片移動(dòng)到裁剪區(qū)域 y 值

      • 圖片右邊線小于裁剪區(qū)域右邊線時(shí)圖片右貼裁剪區(qū)域右邊線

      • 圖片下邊線小于裁剪區(qū)域右邊線時(shí)圖片下貼裁剪區(qū)域下邊線

      進(jìn)行這番操作后,可能會(huì)出現(xiàn)由于圖片過(guò)小無(wú)法鋪滿裁剪區(qū)域的情況,如下圖所示

      iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能

      因此還需要再次對(duì)圖片尺寸進(jìn)行修正

       // 對(duì)圖片縮放進(jìn)行比例修正,防止過(guò)小
       if (self.cropAreaX < self.bigImageView.frame.origin.x
        || ((self.cropAreaX + self.cropAreaWidth) > self.bigImageView.frame.origin.x + self.bigImageView.frame.size.width)
        || self.cropAreaY < self.bigImageView.frame.origin.y
        || ((self.cropAreaY + self.cropAreaHeight) > self.bigImageView.frame.origin.y + self.bigImageView.frame.size.height)) {
        view.frame = self.originalFrame;
       }

      這樣就實(shí)現(xiàn)了縮放功能。

      移動(dòng)功能

      相比于縮放,移動(dòng)功能實(shí)現(xiàn)就簡(jiǎn)單了,只需要在 cropview 上添加 UIPanGestureRecognizer,然后在回調(diào)方法里拿到需要移動(dòng)的距離,修改 UIImageView 的 center 就可以了。

       CGPoint translation = [panGesture translationInView:view.superview];
       [view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
        [panGesture setTranslation:CGPointZero inView:view.superview];

      但是同樣為了保證移動(dòng)后的圖片不會(huì)與裁剪區(qū)域出現(xiàn)空白甚至是超出裁剪區(qū)域,這里更新了圖片位置后,在手勢(shì)結(jié)束時(shí)還要對(duì)圖片進(jìn)行位置修正

        CGRect currentFrame = view.frame;
        
        if (currentFrame.origin.x >= self.cropAreaX) {
         currentFrame.origin.x = self.cropAreaX;
         
        }
        if (currentFrame.origin.y >= self.cropAreaY) {
         currentFrame.origin.y = self.cropAreaY;
        }
        if (currentFrame.size.width + currentFrame.origin.x < self.cropAreaX + self.cropAreaWidth) {
         CGFloat movedLeftX = fabs(currentFrame.size.width + currentFrame.origin.x - (self.cropAreaX + self.cropAreaWidth));
         currentFrame.origin.x += movedLeftX;
        }
        if (currentFrame.size.height + currentFrame.origin.y < self.cropAreaY + self.cropAreaHeight) {
         CGFloat moveUpY = fabs(currentFrame.size.height + currentFrame.origin.y - (self.cropAreaY + self.cropAreaHeight));
         currentFrame.origin.y += moveUpY;
        }
        [UIView animateWithDuration:0.3 animations:^{
         
         [view setFrame:currentFrame];
        }];

      可以看到,這里做的位置檢查與縮放時(shí)做的檢查是一樣的,只是由于不會(huì)改變圖片尺寸所以這里不需要進(jìn)行尺寸修正。

      支持手勢(shì)改變裁剪區(qū)域

      接下來(lái)就是動(dòng)態(tài)裁剪圖片的核心內(nèi)容了,其實(shí)原理也很簡(jiǎn)單,只要在上面的移動(dòng)手勢(shì)處理函數(shù)中,進(jìn)行一些判斷,決定是移動(dòng)圖片位置還是改變裁剪區(qū)域,也就是自定義的 CAShapeLayer 的繪制方框的尺寸就可以了。

      首先我們定義一個(gè)枚舉,用來(lái)表示當(dāng)前應(yīng)當(dāng)操作的是圖片還是裁剪區(qū)域的邊線

      typedef NS_ENUM(NSInteger, ACTIVEGESTUREVIEW) {
       CROPVIEWLEFT,
       CROPVIEWRIGHT,
       CROPVIEWTOP,
       CROPVIEWBOTTOM,
       BIGIMAGEVIEW
      };

      它們分別表示觸發(fā)對(duì)象為裁剪區(qū)域左邊線、右邊線、上邊線、下邊線以及 UIImageView

      然后我們定義一個(gè)枚舉屬性

      @property(assign, nonatomic) ACTIVEGESTUREVIEW activeGestureView;

      判斷操作對(duì)象的標(biāo)準(zhǔn)是當(dāng)前的手勢(shì)所觸發(fā)的位置是在邊線上還是在非邊線上,因此我們需要知道手勢(shì)觸發(fā)時(shí)的坐標(biāo),要知道這一點(diǎn)就需要我們繼承一個(gè) UIPanGestureRecognizer 并覆寫一些方法

      @interface YasicPanGestureRecognizer : UIPanGestureRecognizer
      
      @property(assign, nonatomic) CGPoint beginPoint;
      @property(assign, nonatomic) CGPoint movePoint;
      
      -(instancetype)initWithTarget:(id)target action:(SEL)action inview:(UIView*)view;
      
      @end
      
      @interface YasicPanGestureRecognizer()
      
      @property(strong, nonatomic) UIView *targetView;
      
      @end
      
      @implementation YasicPanGestureRecognizer
      
      -(instancetype)initWithTarget:(id)target action:(SEL)action inview:(UIView*)view{
       
       self = [super initWithTarget:target action:action];
       if(self) {
       self.targetView = view;
       }
       return self;
      }
      
      - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{
       
       [super touchesBegan:touches withEvent:event];
       UITouch *touch = [touches anyObject];
       self.beginPoint = [touch locationInView:self.targetView];
      }
      
      - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
      {
       [super touchesMoved:touches withEvent:event];
       UITouch *touch = [touches anyObject];
       self.movePoint = [touch locationInView:self.targetView];
      }
      
      @end

      可以看到,我們首先傳入了一個(gè) view,用于將手勢(shì)觸發(fā)的位置轉(zhuǎn)換為 view 中的坐標(biāo)值。在 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{ 方法中我們得到了手勢(shì)開(kāi)始時(shí)的觸發(fā)點(diǎn) beginPoint,在 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 方法中我們獲得了手勢(shì)進(jìn)行時(shí)的觸發(fā)點(diǎn) movePoint。

      自定義完 UIPanGestureRecognizer 后我們將其加到 cropview 上并把 cropview 作為參數(shù)傳給 UIPanGestureRecognizer

       // 拖動(dòng)手勢(shì)
       YasicPanGestureRecognizer *panGesture = [[YasicPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleDynamicPanGesture:) inview:self.cropView];
       [self.cropView addGestureRecognizer:panGesture];

      接下來(lái)就是處理手勢(shì)的函數(shù),這里我們可以將整個(gè)過(guò)程分為三個(gè)步驟,開(kāi)始時(shí) -> 進(jìn)行時(shí) -> 結(jié)束時(shí)。

      手勢(shì)開(kāi)始時(shí)

      在這里我們要根據(jù)手勢(shì)的 beginPoint 判斷觸發(fā)對(duì)象是邊線還是 UIImageView

      // 開(kāi)始滑動(dòng)時(shí)判斷滑動(dòng)對(duì)象是 ImageView 還是 Layer 上的 Line
       if (panGesture.state == UIGestureRecognizerStateBegan) {
       if (beginPoint.x >= self.cropAreaX - judgeWidth && beginPoint.x <= self.cropAreaX + judgeWidth && beginPoint.y >= self.cropAreaY && beginPoint.y <= self.cropAreaY + self.cropAreaHeight && self.cropAreaWidth >= 50) {
        self.activeGestureView = CROPVIEWLEFT;
       } else if (beginPoint.x >= self.cropAreaX + self.cropAreaWidth - judgeWidth && beginPoint.x <= self.cropAreaX + self.cropAreaWidth + judgeWidth && beginPoint.y >= self.cropAreaY && beginPoint.y <= self.cropAreaY + self.cropAreaHeight && self.cropAreaWidth >= 50) {
        self.activeGestureView = CROPVIEWRIGHT;
       } else if (beginPoint.y >= self.cropAreaY - judgeWidth && beginPoint.y <= self.cropAreaY + judgeWidth && beginPoint.x >= self.cropAreaX && beginPoint.x <= self.cropAreaX + self.cropAreaWidth && self.cropAreaHeight >= 50) {
        self.activeGestureView = CROPVIEWTOP;
       } else if (beginPoint.y >= self.cropAreaY + self.cropAreaHeight - judgeWidth && beginPoint.y <= self.cropAreaY + self.cropAreaHeight + judgeWidth && beginPoint.x >= self.cropAreaX && beginPoint.x <= self.cropAreaX + self.cropAreaWidth && self.cropAreaHeight >= 50) {
        self.activeGestureView = CROPVIEWBOTTOM;
       } else {
        self.activeGestureView = BIGIMAGEVIEW;
        [view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
        [panGesture setTranslation:CGPointZero inView:view.superview];
       }
       }

      手勢(shì)進(jìn)行時(shí)

      在這里,如果觸發(fā)對(duì)象是邊線,則計(jì)算邊線需要移動(dòng)的距離和方向,以及對(duì)于邊界條件的限制以防止邊線之間交叉錯(cuò)位的情況,具體來(lái)說(shuō)就是獲得坐標(biāo)差值,更新 cropAreaX、cropAreaWidth 等值,然后更新 CAShapeLayer 上的空心蒙層。

      如果觸發(fā)對(duì)象是 UIImageView 則只需要將其位置進(jìn)行改變即可。

      // 滑動(dòng)過(guò)程中進(jìn)行位置改變
       if (panGesture.state == UIGestureRecognizerStateChanged) {
       CGFloat diff = 0;
       switch (self.activeGestureView) {
        case CROPVIEWLEFT: {
        diff = movePoint.x - self.cropAreaX;
        if (diff >= 0 && self.cropAreaWidth > 50) {
         self.cropAreaWidth -= diff;
         self.cropAreaX += diff;
        } else if (diff < 0 && self.cropAreaX > self.bigImageView.frame.origin.x && self.cropAreaX >= 15) {
         self.cropAreaWidth -= diff;
         self.cropAreaX += diff;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWRIGHT: {
        diff = movePoint.x - self.cropAreaX - self.cropAreaWidth;
        if (diff >= 0 && (self.cropAreaX + self.cropAreaWidth) < MIN(self.bigImageView.frame.origin.x + self.bigImageView.frame.size.width, self.cropView.frame.origin.x + self.cropView.frame.size.width - 15)){
         self.cropAreaWidth += diff;
        } else if (diff < 0 && self.cropAreaWidth >= 50) {
         self.cropAreaWidth += diff;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWTOP: {
        diff = movePoint.y - self.cropAreaY;
        if (diff >= 0 && self.cropAreaHeight > 50) {
         self.cropAreaHeight -= diff;
         self.cropAreaY += diff;
        } else if (diff < 0 && self.cropAreaY > self.bigImageView.frame.origin.y && self.cropAreaY >= 15) {
         self.cropAreaHeight -= diff;
         self.cropAreaY += diff;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWBOTTOM: {
        diff = movePoint.y - self.cropAreaY - self.cropAreaHeight;
        if (diff >= 0 && (self.cropAreaY + self.cropAreaHeight) < MIN(self.bigImageView.frame.origin.y + self.bigImageView.frame.size.height, self.cropView.frame.origin.y + self.cropView.frame.size.height - 15)){
         self.cropAreaHeight += diff;
        } else if (diff < 0 && self.cropAreaHeight >= 50) {
         self.cropAreaHeight += diff;
        }
        [self setUpCropLayer];
        break;
        }
        case BIGIMAGEVIEW: {
        [view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
        [panGesture setTranslation:CGPointZero inView:view.superview];
        break;
        }
        default:
        break;
       }
       }

      手勢(shì)結(jié)束時(shí)

      手勢(shì)結(jié)束時(shí),我們需要對(duì)位置進(jìn)行修正。如果是裁剪區(qū)域邊線,則要判斷左右、上下邊線之間的距離是否過(guò)短,邊線是否超出 UIImageView 的范圍等。如果左右邊線距離過(guò)短則設(shè)置最小裁剪寬度,如果上線邊線距離過(guò)短則設(shè)置最小裁剪高度,如果左邊線超出了 UIImageView 左邊線則需要緊貼 UIImageView 的左邊線,并更新裁剪區(qū)域?qū)挾?,以此類推。然后更?CAShapeLayer 上的空心蒙層即可。

      如果是 UIImageView 則跟上一節(jié)一樣要保證圖片不會(huì)與裁剪區(qū)域出現(xiàn)空白。

       // 滑動(dòng)結(jié)束后進(jìn)行位置修正
       if (panGesture.state == UIGestureRecognizerStateEnded) {
       switch (self.activeGestureView) {
        case CROPVIEWLEFT: {
        if (self.cropAreaWidth < 50) {
         self.cropAreaX -= 50 - self.cropAreaWidth;
         self.cropAreaWidth = 50;
        }
        if (self.cropAreaX < MAX(self.bigImageView.frame.origin.x, 15)) {
         CGFloat temp = self.cropAreaX + self.cropAreaWidth;
         self.cropAreaX = MAX(self.bigImageView.frame.origin.x, 15);
         self.cropAreaWidth = temp - self.cropAreaX;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWRIGHT: {
        if (self.cropAreaWidth < 50) {
         self.cropAreaWidth = 50;
        }
        if (self.cropAreaX + self.cropAreaWidth > MIN(self.bigImageView.frame.origin.x + self.bigImageView.frame.size.width, self.cropView.frame.origin.x + self.cropView.frame.size.width - 15)) {
         self.cropAreaWidth = MIN(self.bigImageView.frame.origin.x + self.bigImageView.frame.size.width, self.cropView.frame.origin.x + self.cropView.frame.size.width - 15) - self.cropAreaX;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWTOP: {
        if (self.cropAreaHeight < 50) {
         self.cropAreaY -= 50 - self.cropAreaHeight;
         self.cropAreaHeight = 50;
        }
        if (self.cropAreaY < MAX(self.bigImageView.frame.origin.y, 15)) {
         CGFloat temp = self.cropAreaY + self.cropAreaHeight;
         self.cropAreaY = MAX(self.bigImageView.frame.origin.y, 15);
         self.cropAreaHeight = temp - self.cropAreaY;
        }
        [self setUpCropLayer];
        break;
        }
        case CROPVIEWBOTTOM: {
        if (self.cropAreaHeight < 50) {
         self.cropAreaHeight = 50;
        }
        if (self.cropAreaY + self.cropAreaHeight > MIN(self.bigImageView.frame.origin.y + self.bigImageView.frame.size.height, self.cropView.frame.origin.y + self.cropView.frame.size.height - 15)) {
         self.cropAreaHeight = MIN(self.bigImageView.frame.origin.y + self.bigImageView.frame.size.height, self.cropView.frame.origin.y + self.cropView.frame.size.height - 15) - self.cropAreaY;
        }
        [self setUpCropLayer];
        break;
        }
        case BIGIMAGEVIEW: {
        CGRect currentFrame = view.frame;
        
        if (currentFrame.origin.x >= self.cropAreaX) {
         currentFrame.origin.x = self.cropAreaX;
         
        }
        if (currentFrame.origin.y >= self.cropAreaY) {
         currentFrame.origin.y = self.cropAreaY;
        }
        if (currentFrame.size.width + currentFrame.origin.x < self.cropAreaX + self.cropAreaWidth) {
         CGFloat movedLeftX = fabs(currentFrame.size.width + currentFrame.origin.x - (self.cropAreaX + self.cropAreaWidth));
         currentFrame.origin.x += movedLeftX;
        }
        if (currentFrame.size.height + currentFrame.origin.y < self.cropAreaY + self.cropAreaHeight) {
         CGFloat moveUpY = fabs(currentFrame.size.height + currentFrame.origin.y - (self.cropAreaY + self.cropAreaHeight));
         currentFrame.origin.y += moveUpY;
        }
        [UIView animateWithDuration:0.3 animations:^{
         
         [view setFrame:currentFrame];
        }];
        break;
        }
        default:
        break;
       }
       }

      進(jìn)行圖片裁剪并獲得裁剪后的圖片

      最后一步就是對(duì)圖片進(jìn)行裁剪了。首先確定對(duì)圖片的縮放尺寸 imageScale

       CGFloat imageScale = MIN(self.bigImageView.frame.size.width/self.targetImage.size.width, self.bigImageView.frame.size.height/self.targetImage.size.height);

      然后將 cropView 的裁剪區(qū)域?qū)?yīng)到 UIImageView 上,再除以縮放值,即可得到對(duì)應(yīng) UIImage 上需要裁剪的區(qū)域

       CGFloat cropX = (self.cropAreaX - self.bigImageView.frame.origin.x)/imageScale;
       CGFloat cropY = (self.cropAreaY - self.bigImageView.frame.origin.y)/imageScale;
       CGFloat cropWidth = self.cropAreaWidth/imageScale;
       CGFloat cropHeight = self.cropAreaHeight/imageScale;
       CGRect cropRect = CGRectMake(cropX, cropY, cropWidth, cropHeight);

      最后調(diào)用 CoreGraphic 的方法,將圖片對(duì)應(yīng)區(qū)域的數(shù)據(jù)取出來(lái)生成新的圖片,就是我們需要的裁剪后的圖片了。

       CGImageRef sourceImageRef = [self.targetImage CGImage];
       CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, cropRect);
       UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

      以上是“iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


      文章標(biāo)題:iOS中如何實(shí)現(xiàn)動(dòng)態(tài)區(qū)域裁剪圖片功能
      URL網(wǎng)址:http://ef60e0e.cn/article/pogiie.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        扬州市| 泰州市| 名山县| 逊克县| 鲁山县| 华蓥市| 厦门市| 柘荣县| 茂名市| 磴口县| 吉林省| 光泽县| 定南县| 荥阳市| 临泽县| 淮北市| 临洮县| 小金县| 叶城县| 凌云县| 蒙阴县| 义马市| 皮山县| 邹平县| 长汀县| 剑阁县| 余庆县| 翁源县| 旬邑县| 赣州市| 毕节市| 横山县| 东乡族自治县| 鄂伦春自治旗| 鱼台县| 通州市| 凤庆县| 滦平县| 湛江市| 庆城县| 定兴县|