resizableImageWithCapInsets实现登录按钮、胶囊tab按钮和聊天气泡贴图效果

1.关于UIImage的resizable capInsets属性

This technique is often used to create variable-width buttons, which retain the same rounded corners but whose center region grows or shrinks as needed. 

For example, you can use this method to create a background image for a button with borders and corners: when the button is resized, the corners of the image remain unchanged, but the borders and center of the image expand to cover the new size.


当我们在创建变宽按钮时,往往保持按钮的圆角,只是让中心部分进行必要地伸缩。

例如,你可以使用 stretchable 或 resizable 来设置圆角按钮的背景贴图,当按钮尺寸发生改变时,背景贴图的圆角保持不变,中心部分进行必要伸缩来适应新的尺寸。


调用 stretchableImage 或 resizableImage 后,返回一个带有缩放封盖属性的UIImage对象(Return a new image object with the specified cap insets)。
当我们对超出图片size的UIImageView/UIButton进行setImage或setBackgroundImage时,就会按照预设CapInsets属性进行填充、拉伸适配

2.-[UIImage(UIImageDeprecated) stretchableImageWithLeftCapWidth:topCapHeight:]

NS_DEPRECATED_IOS(2_0, 5_0, "Use -resizableImageWithCapInsets:")
【Summary】
Creates and returns a new image object with the specified cap values.
【Discussion】

specifying cap insets such that the interior is a1x1 area.

During scaling or resizing of the image, areas covered by a cap are not scaled or resized
Instead, the 1-pixel wide area not covered by the cap in each direction is what is scaled or resized

【解说】
    它的功能是创建一个内容可拉伸,而边角不拉伸的图片,需要两个参数:

@property(nonatomic,readonlyNSInteger leftCapWidth:horiz. stretchable. 左边不拉伸区域的宽度。

@property(nonatomic,readonlyNSInteger topCapHeight:vert. stretchable. 上面不拉伸区域的高度。

    根据UIKit/UIImage.h对该属性的描述

  •     rightCapWidth=width-leftCapWidth-1
  •     bottomCapHeight=height-topCapWidth-1

    UIEdgeInsets capInsets =  UIEdgeInsetsMake(topCapHeight,leftCapWidth,bottomCapHeight,rightCapWidth)

    因此,实际stretchable的interior area是capInsets封盖圈起来的部分——一个1x1点阵。也即坐标为{left+1,top+1}的这个点(one point)会像贴瓷砖那样进行复制填充(UIImageResizingModeTile:the interior is tiled when drawn),capInsets封盖的周边外围保持不变。

    为视效均匀起见,一般选取图片的中心点(leftCapWidth=width/2,topCapHeight=height/2)进行复制填充。

// create a resizable version of this image. the interior is tiled when drawn.

3.-[UIImage resizableImageWithCapInsets:]

@property(nonatomic,readonlyUIEdgeInsets capInsets;

NS_AVAILABLE_IOS(5_0)

【Summary】
create a resizable version of this image. the interior is tiled when drawn.
【Discussion】

For best performance, use a tiled area that is a 1x1 pixel area in size.

iOS uses different rendering techniques, with different performance characteristics, depending on the size of each resizable area in the image:

  • If resizable areas have a width or height of 1 pixel—that is, a horizontally resizable area is 1 pixel wide, a vertically resizable area is 1 pixel tall, or the center region of the image is 1 x 1 pixel—iOS draws the image by stretching the 1-pixel region. This mode provides the fastest performance (for nonzero cap insets).
  • If resizable areas have a width or height greater than 1 pixel, iOS draws the image by tiling the region. This mode provides reduced performance, but can be useful for images with textured (rather than solid-color) content in their resizable areas.
  • If the entire image is resizable—that is, the capInsets parameter is UIEdgeInsetsZero—and its size is greater than 1 x 1 pixel, iOS draws the image by tiling the entire image. This mode is faster than the tiling mode for nonzero cap insets.
【解说】
    其中 UIEdgeInsets 参数的格式是(top,left,bottom,right),从上、左、下、右分别在图片上画了一道线,这样就给一个图片加了一个逆向包围圈,圈内部分会被拉伸圈外部分则保持不变

    (1)若选取图片的中心点(height/2,width/2,height/2+1,width/2+1)进行复制填充时,效果同stretchableImageWithLeftCapWidth:width/2 topCapHeight:height/2,对 1x1 pixel area 进行拉伸(stretching)适配。对于纯色图片,选取中心点进行拉伸时效能最优。

    (2)若capInsets围封区域大于1x1,则对尺寸超出部分,像贴瓷砖一样复制填充(tile/spread/extend)。对于纹理贴图(textured),通常采用此种方式。

    (3)若capInsets=UIEdgeInsetsZero,则围封区域为整个原始贴图(width*height),对于尺寸超出部分,同上也像贴瓷砖一样复制填充(tile/spread/extend)。但效能优于非零capInsets。


// the interior is resized according to the resizingMode

4.-[UIImage resizableImageWithCapInsets:resizingMode:]

NS_AVAILABLE_IOS(6_0)
================================================================================
【Discussion】

This method is exactly the same as its counterpart resizableImageWithCapInsets: except that the resizing mode of the new image object can be explicitly declared

You should only call this method in place of its counterpart if you specifically want your image to be resized with the UIImageResizingModeStretch resizing mode.

【解说】
    UIImageResizingModeTile:平铺模式,通过拉伸或铺排UIEdgeInsets指定的矩形区域来填充图片。

       resizableImageWithCapInsets: resizingMode:对于围封区域大于1x1,UIImageResizingModeTile相当于resizableImageWithCapInsets: ,一般直接调用resizableImageWithCapInsets:。对于围封区域等于1x1,若调用resizableImageWithCapInsets默认使用stretching方式,若指定resizingMode=UIImageResizingModeTile则可式指定使用tiling模式。

    UIImageResizingModeStretch:拉伸模式,通过拉伸UIEdgeInsets(围封区域大于1x1)指定的矩形区域来填充图片。

对于围封区域大于1x1,若调用resizableImageWithCapInsets默认使用tiling模式;若指定resizingMode=UIImageResizingModeStretch则可显式指定使用stretching模式。

        拉伸UIEdgeInsets排除外围指定的内部矩形区域(interior resizable area is a rectangle)。
  1. 横向拉伸:UIEdgeInsetsMake(0, coreRadius, 0, coreRadius),例如圆角/横向胶囊按钮贴图。
  2. 纵向拉伸:UIEdgeInsetsMake(coreRadius, 0, coreRadius, 0),例如类似温度计的圆角/纵向胶囊按钮贴图。
  3. 居中拉伸:UIEdgeInsetsMake(coreRadius, coreRadius,coreRadius, coreRadius),例如带corner的圆角登录按钮。


5.resizableImage & EdgeInsets 综合例程

    例如,我们要用贴图top_half_bg@2x.png(39x20)和贴图bot_half_bg@2x.png(39x20) 实现如下贴图效果:


    其中分割线上半部由top_half_bg@2x.png贴出顶部圆角效果,分割线下半部由bot_half_bg@2x.png贴出底部圆角效果。其拉伸属性设置代码如下:

// 圆角半径为6pixel=3point
#define CORNER_RADIUS 3
// top_half_bg@2x.png拉伸矩形区域
#define TOP_STRETCH_CAP_INSETS  UIEdgeInsetsMake(CORNER_RADIUS,CORNER_RADIUS,0,CORNER_RADIUS)
// bot_half_bg@2x.png拉伸矩形区域
#define BOT_STRETCH_CAP_INSETS  UIEdgeInsetsMake(0,CORNER_RADIUS,CORNER_RADIUS,CORNER_RADIUS)

UIImage* topBgImage = [UIImage imageNamed:@"top_half_bg.png"];
topBgImage = [topBgImage resizableImageWithCapInsets:TOP_STRETCH_CAP_INSETS];

UIImage* botBgImage = [UIImage imageNamed:@"bot_half_bg.png"];
botBgImage = [botBgImage resizableImageWithCapInsets:BOT_STRETCH_CAP_INSETS];


    最后,给出一个关于UIImageView和UIButton贴图的综合示例(fan2/resizableImage):

    (1)第一组(testVerticalResizable):例程演示了纵向半椭圆封盖属性的应用。上边一个左上、右上带圆角贴图,下边一个左下、右下带圆角贴图。

    (2)第二组(testRoundedRectButton):例程演示了横向圆角按钮效果的实现。

    (3)第三组(testResizableBubble):例程演示了类微信聊天气泡拉伸效果的实现。

    (4)第四组(testCapsuleRoundedButton):例程演示了横向半椭圆封盖属性的应用:

  • 初始胶囊按钮,背景为蓝圈白底,字体为蓝色。
  • 左右按钮初始图标分别为大拇指竖起和大拇指向下。 
  • 选择左胶囊按钮,背景变蓝,字体变白,图标由大拇指竖起变为一朵鲜花。 
  • 选择右胶囊按钮,背景变蓝,字体变白,图标由大拇指向下变为一朵枯萎。 


    以下为UIImageView和UIButton贴图的CapInsets标注:



testVerticalResizable

testVerticalResizable 例程演示了纵向半椭圆封盖属性的应用。

上半部 topBgImage 基于中心点拉伸贴图。

topBgImage = [topBgImage resizableImageWithCapInsets:CENTER_PIXEL_CAPINSETS_OF_IMAGE(topBgImage)

下半部 botBgImage 基于封盖 tiling 围封部分填充贴图。
// tiling底部椭圆上的围封矩形部分:
botBgImage = [botBgImage resizableImageWithCapInsets:BOT_IMGVIEW_CAPINSETS /*resizingMode:UIImageResizingModeStretch*/]; // 默认Tile,可测试Stretch


testRoundedRectButton

testRoundedRectButton 例程演示了横向圆角按钮效果的实现。
CapInsets 封盖剔除四周圆角部分,tiling 或 streching 围封矩形。

方式1:stretching 中心点
// stretching the is 1 x 1 pixel region, provides the fastest performance.
// loginBtnBgImg = [loginBtnBgImg resizableImageWithCapInsets:CENTER_PIXEL_CAPINSETS_OF_IMAGE(loginBtnBgImg) /*resizingMode:UIImageResizingModeTile*/]; // 默认Stretch,可测试Tile

RESIZABLE_IMAGE_BY_STRETCHING_CENTER_PIXEL(loginBtnBgImg);


方式2:stretching 椭圆中间封围的矩形部分:

// stretching the interior area not covered by the cap
loginBtnBgImg = [loginBtnBgImg resizableImageWithCapInsets:LOGIN_BTN_CAPINSETS resizingMode:UIImageResizingModeStretch]; // 默认Tile,测试Stretch


通过 UIView.CALayer 的 setCornerRadius 接口设置边框圆角半径。  
通过 `+[UIImage(UIColor) imageFromColor:]` 扩展接口可基于颜色绘制纯色背景,免用贴图资源

    [_loginButton.layer setMasksToBounds:YES];
    [_loginButton.layer setCornerRadius:LOGIN_BUTTON_CORNER_RADIUS];
    [_loginButton setBackgroundImage:[UIImage resizableImageFromColor:RGBCOLOR(54, 187, 72)] forState:UIControlStateNormal];


testResizableBubble

testResizableBubble 例程演示了类微信聊天气泡拉伸效果的实现。

方式1:stretching 中心点,箭头中心点会被纵向拉伸!
friendBubbleBgImg = [friendBubbleBgImg resizableImageWithCapInsets:CENTER_PIXEL_CAPINSETS_OF_IMAGE(friendBubbleBgImg) /*resizingMode:UIImageResizingModeTile*/]; // 默认Stretch,可测试Tile

方式2:tiling 围封的中部矩形部分(4,11,4,4):

默认 tiling 模式下左侧纵向填充出现3个箭头;指定 stretch 模式,则箭头会沿中心点纵向拉伸钝化。
friendBubbleBgImg = [friendBubbleBgImg resizableImageWithCapInsets:UIEdgeInsetsMake(4, 11, 4, 4) /*resizingMode:UIImageResizingModeStretch*/]; // 默认Tile,可测试Stretch


方式3:tiling 围封的右下矩形部分(21,11,4,4):  

保留箭头所在左上部分,右下方向填充/拉伸,实现预期拉伸效果!
friendBubbleBgImg = [friendBubbleBgImg resizableImageWithCapInsets:UIEdgeInsetsMake(21, BUBBLE_ANGLE_SIDE_MARGIN, BUBBLE_CORNER_RADIUS, BUBBLE_CORNER_RADIUS) /*resizingMode:UIImageResizingModeStretch*/]; // 默认Tile,可测试Stretch


testCapsuleRoundedButton

testCapsuleRoundedButton 例程演示了横向半椭圆封盖属性的应用。

胶囊按钮综合演示了:

+ 按钮背景贴图(setBackgroundImage):  
> -[UIImage resizableImageWithCapInsets:resizingMode:]  
+ 按钮图标及标题的间距控制(imageView/titleLabel EdgeInsets):  
  > -[UIButton setImageEdgeInsets:]   
> -[UIButton setTitleEdgeInsets:]   

点击按钮为选择态,背景变蓝,字体变白,图标改变。
左按钮为左图标右标题,右按钮为左标题右图标,图标和标题间距为 8pt。

resizable Background Image
方式1:stretching 中心点,纵向拉伸椭圆圆角部分至顶,导致右侧纵向饱满部分也向上鼓胀。
RESIZABLE_IMAGE_BY_STRETCHING_CENTER_PIXEL(leftNorBgImg);

方式2:stretching 右侧点,保留左侧椭圆,右侧正确拉伸。但是按下态不正常!
RESIZABLE_IMAGE_BY_INTERIOR_PIXEL(leftNorBgImg, BARBTN_CORNER_RADIUS, BARBTN_CORNER_RADIUS);

方式3:tiling 右侧围封的非椭圆区矩形部分,实现预期拉伸效果!
leftNorBgImg = [leftNorBgImg resizableImageWithCapInsets:LEFT_BARBTN_CAPINSETS /*resizingMode:UIImageResizingModeStretch*/]; // 默认Tile,可测试Stretch


setImageEdgeInsets & setTitleEdgeInsets

iOS 系统按钮默认只有一个 titleLabel 居中。当用户指定 imageView 作为按钮图标时,默认图标在左侧,标题在右侧,间隔0.5pt(1pixel),整体居中(UIControlContentVerticalAlignmentCenter/UIControlContentHorizontalAlignmentCenter)。
用户根据排版需求可设置 contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft/Center 使按钮控件(UIControl)中的元素(imageView+titleLabel)横向左/中对齐,然后通过 contentEdgeInsets 调整控件内元素组合的边缘来设置周边留白。
横向基于 contentHorizontalAlignmentcontentEdgeInsets 排版后,可进一步设置按钮控件子元素(imageView/titleLabel)的 UIEdgeInsets(titleEdgeInsets/imageEdgeInsets){top, left, bottom, right}。UIEdgeInsets的偏移效果是右移left/2,左移right/2;如为负数,则反向偏移。如此,可精确配置按钮控件子元素(imageView+titleLabel)之间的间隔。

关于 setImageEdgeInsets/setTitleEdgeInsets 的排版布局效果,参见下图:



左标题右图标

下面重点讲解“左标题右图标”效果的实现:

标题左移至图标位置

// test41:标题只设置左边距为负图标宽度(24pt),实际左移半图标
[_rightAgainstButton setTitleEdgeInsets:UIEdgeInsetsMake(0, -rightBtnImageWidth, 0, 0)];

// test42:标题只设置右边距为图标宽度(24pt),实际左移半图标
[_rightAgainstButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 0, rightBtnImageWidth)];

// test43:test41+test42,标题实际左移整个图标宽度
[_rightAgainstButton setTitleEdgeInsets:UIEdgeInsetsMake(0, -rightBtnImageWidth, 0, rightBtnImageWidth)];
图标右移至标题位置

// 图标设置左边距为按钮标题宽度,右边距为负按钮标题宽度,实际右移整个按钮标题宽度
[_rightAgainstButton setImageEdgeInsets:UIEdgeInsetsMake(0, rightBtnTitleWidth, 0, -rightBtnTitleWidth)];
标题再左移4pt,图标再右移4pt
// test44:基于test43,标题继续左移4pt
[_rightAgainstButton setTitleEdgeInsets:UIEdgeInsetsMake(0, -rightBtnImageWidth-4, 0, rightBtnImageWidth+4)];

// 图标继续右移4pt,最终左侧标题和右侧图标间距8pt
[_rightAgainstButton setImageEdgeInsets:UIEdgeInsetsMake(0, rightBtnTitleWidth+4, 0, -rightBtnTitleWidth-4)];


参考:

iOS图片拉伸技巧

[UIImage resizableImageWithCapInsets:]使用注意

iOS 不规则的ImageView》《iOS 实现聊天剪裁气泡

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值