zoukankan      html  css  js  c++  java
  • [IOS] 详解图片局部拉伸 + 实现图片局部收缩

    (图为微信首页右上角『+』效果)

    当初还在开发WP7的时候,从IOS同事那边了解到类似微信以上功能的实现。

    Item条数不同,总高度也不同,这就需要将背景图片进行局部拉伸到响应的高度,并且保持上方的三角形不变型。

    然而回想WP,没找到有API能对图片做此处理,只要图片显示比例与源图比例不一样,就会导致图片拉伸变形。

    (因此我只能让设计给一个右上角三角形,之后一个纯色长方形,纯色长方形拉伸后不会有问题。想要图片局部改变也行,得自己处理像素)

     一. 局部拉伸

    现在我们就来看看如何进行图片局部拉伸,相关API如下:

    - (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode;

     capInsets定义图片的不拉伸范围(这个范围是相对于源图片大小而言),resizingMode定义了图片以拉伸/平铺的方式变换。

     

    在设置了四周的不拉伸范围后,中间的蓝色部分将会以 拉伸/平铺的方式 被拉伸。

     

    1. 我们先讨论讨论基于某一点进行拉伸。

    原图片大小为 200 * 78,需把它拉伸成200 * 250 ,宽度保持不变。

    ①.在不进行局部拉伸的情况下,我们得到的效果:

    ②. 可以知道,为了达到效果,图片周围都不能够拉伸,现在我们来拉伸正中间的一个点。

    - (void)viewDidLoad {
    
      [super viewDidLoad];   
      
      //源图片大小 可以通过对其触摸查看拉伸点改变后相应的效果
     UIImage *originImage = [UIImage imageNamed:@"wechat"]; //200 * 78
     originButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 200, 200, 78)];
     originButton.userInteractionEnabled = NO;
     [originButton setBackgroundImage:originImage forState:UIControlStateNormal];
     [self.view addSubview:originButton];
    
     //拉伸后的图片
     CGFloat width = originImage.size.width / 2.0;
     CGFloat height = originImage.size.height / 2.0;
     UIImage *newImage = [originImage resizableImageWithCapInsets:UIEdgeInsetsMake(height,width,height,width) resizingMode:UIImageResizingModeStretch];//取正中间一个点,拉伸方式
     resizableButton = [[UIButton alloc] initWithFrame:CGRectMake(205, 200, 200, 250)];//高度由78变为250
     [resizableButton setBackgroundImage:originImage forState:UIControlStateNormal];
     [self.view addSubview:resizableButton];
    
    }
    
    //实现触摸  如果在左边的原图内部 则根据触摸点所在位置去拉伸图片
    -(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self.view];
        CGPoint actualPoint = [self.view convertPoint:point toView:originButton];//坐标转换
        if(actualPoint.x >= 0 && actualPoint.x <= originButton.frame.size.width &&
           actualPoint.y >= 0 && actualPoint.y <= originButton.frame.size.height){
            NSLog(@"--------%@---------",NSStringFromCGPoint(actualPoint));
            UIImage *image1 = [UIImage imageNamed:@"wechat"];
            CGFloat top = actualPoint.y;
            CGFloat left = actualPoint.x;
            CGFloat bottom = image1.size.height - actualPoint.y;
            CGFloat right = image1.size.width - actualPoint.x; 
         UIImage *newImage = [image1 resizableImageWithCapInsets:UIEdgeInsetsMake(top,left,bottom,right) resizingMode:UIImageResizingModeStretch]; [resizableButton setBackgroundImage:newImage forState:UIControlStateNormal]; } }

     

    ③.宽度大于图片实际宽度时:

     ④.长和宽都大于图片时,横向纵向都会被拉伸。

    相当于在上面的纵向拉伸结束的基础上(同时拉伸点也被拉伸的)继续由拉伸点横向拉伸。

    总结:以上我们都是基于一个点进行拉伸。

         纵向拉伸时,会以拉伸点横向延伸形成的线,拉伸至新的高度。

         横向拉伸时,会以拉伸点纵向延伸形成的线,拉伸新的宽度。

      

    2. 现在我们看看基于某一块区域进行拉伸

    ①.宽度不变,高度变大,使用拉伸Stretch的方式,其他亦然。 

        

     .高和宽都增加,使用平铺Tile的方式。

    也更好的解释了上述的Stretch拉伸

       

    二. 图片局部收缩

    但是如果控件的大小比图片的小的话,就会导致图片压缩。三角形处特别明显

     

     既然能够将图片进行局部拉伸,那是否能够将图片进行局部压缩呢?

    想了很久,用拉伸resizableImageWithCapInsets的方式没找到解决办法,那只有自己写了。

    1.不管是横向还是纵向,都只能收缩要收缩的部分,因此也跟拉伸一样,需要一个UIEdgeInsets。

    2.要把收缩的部分裁剪下来,变得更小。 因此需要知道变化后的宽和高,即图片最终的宽高CGRect。

    3.这个局部收缩的过程就是将图片裁剪、收缩、拼接的过程。

    为了方便,写了一个分类,和拉伸的方法类似//capInsets 相对图片来说,不需要拉伸的部分

    //actualSize 需要显示的大小
    - (UIImage *)shrinkImageWithCapInsets:(UIEdgeInsets)capInsets actualSize:(CGSize)actualSize{
        UIImage newImage = self;  
        if(actualSize.width < self.size.width){
            //宽度变小了 
            newImage = 裁剪-中间收缩-拼接(newImage);//最多分为三列  详细代码见文末demo
    if(actualSize.height >= self.size.height){ return newAllImage; }//否则继续纵向处理 } if(actualSize.height < self.size.height){ //高度变小了 newImage = 裁剪-中间收缩-拼接(newImage);//最多分为三行 详情见文末demo
    return newAllImage; } return nil; }

     图片裁剪:

    //裁剪图片
    -(UIImage *)clipImageWithClipRect:(CGRect)clipRect{
        CGImageRef clipImageRef = CGImageCreateWithImageInRect(self.CGImage, clipRect);
        UIGraphicsBeginImageContext(clipRect.size);//设置图片大小
        
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextDrawImage(context, clipRect, clipImageRef);
        UIImage *clipImage = [UIImage imageWithCGImage :clipImageRef];
        
        UIGraphicsEndImageContext();
        
        return clipImage;
    }

    图片收缩:

    //按照一定大小缩放图片
    -(UIImage *)scaleImageToSize:(CGSize)size{
        UIGraphicsBeginImageContext(size);//设定新的大小
        [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
        UIImage *scaleImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return scaleImage;
    }

    多个图片拼接:

    +(UIImage *)combineWithImages:(NSArray *)images orientation:(YFImageCombineType)orientation{
        NSMutableArray *sizeArray = [[NSMutableArray alloc] init];
        CGFloat maxHeight = 0, maxWidth = 0;
        for (id image in images) {
    //        if([image isKindOfClass:[UIImage class]]){
                CGSize size = ((UIImage *)image).size;
                if(orientation == YFImageCombineHorizental){//横向
                    maxWidth += size.width;
                    maxHeight = (size.height > maxHeight) ? size.height : maxHeight;
                }
                else{
                    maxHeight += size.height;
                    maxWidth = (size.width > maxWidth) ? size.width : maxWidth;
                }
                [sizeArray addObject:[NSValue valueWithCGSize:size]];
    //        }
        }
        
        CGFloat lastLength = 0;//记录上一次的最右或者最下边值
        UIGraphicsBeginImageContext(CGSizeMake(maxWidth, maxHeight));
        for (int i = 0; i < sizeArray.count; i++){
            CGSize size = [[sizeArray objectAtIndex:i] CGSizeValue];
            CGRect currentRect;
            if(orientation == YFImageCombineHorizental){//横向
                currentRect = CGRectMake(lastLength, (maxHeight - size.height) / 2.0, size.width, size.height);
                [[images objectAtIndex:i] drawInRect:currentRect];
                lastLength = CGRectGetMaxX(currentRect);
            }
            else{
                currentRect = CGRectMake((maxWidth - size.width) / 2.0, lastLength, size.width, size.height);
                [[images objectAtIndex:i] drawInRect:currentRect];
                lastLength = CGRectGetMaxY(currentRect);
            }
        }
        UIImage* combinedImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        return combinedImage;
    }

    使用:引入头文件   #import "UIImage+YFShrink.h"

        //收缩后的图片  150 * 70   需要传入一个显示的实际大小
        //实际显示宽度须actualWidth <= left + right;
        //实际显示高度须actualHeight <= top + bottom;
        UIImage *originImage = [UIImage imageNamed:@"wechat"]; //200 * 78
        UIImage *shrinkImage = [originImage shrinkImageWithCapInsets:UIEdgeInsetsMake(30, 40, 30, 60) actualSize:CGSizeMake(150, 60)];
        shrinkButton = [[UIButton alloc] initWithFrame:CGRectMake(20, 320, 150, 60)];
        [shrinkButton setBackgroundImage:shrinkImage forState:UIControlStateNormal];
        [self.view addSubview:shrinkButton];

    效果:                                         标注:

    Github链接

  • 相关阅读:
    Constraint.constant动画效果
    poj3469 Dual Core CPU --- 最小割
    开发Blog整理
    Android 四大组件学习之BroadcastReceiver四
    在光标处插入指定文本(支持文本域和文本框)
    图片显示插件
    Extjs4 自定义组件
    Windows英文版GitHub客户端使用操作流程图文攻略教程现没中文版
    innerHTML和innerText怎么区分
    button和input type=button的区别及注意事项
  • 原文地址:https://www.cnblogs.com/yffswyf/p/6841254.html
Copyright © 2011-2022 走看看