返回一張受保護且被拉伸的圖片
應用場景:聊天窗口的氣泡
方法一(棄用):
iOS 5.0以前使用(棄用)這個方法會自動計算出偏向中間的一個1*1的方格也就是被拉伸的地方(默認使用拉伸),一般傳入的值為圖片大小的一半.
[image stretchableImageWithLeftCapWidth:imageHeight *0.5 topCapHeight:imageHeight *0.5 ];
方法二(常用):
將圖片沒有保護的部分進行拉伸腰耙。
上下左右的值定義了受保護區(qū)域榛丢,能被拉伸的地方是中間區(qū)域,一般我們都設成中心點為了安全挺庞。
// 1. 直接傳入保護的范圍晰赞,沒有設置圖拉伸的模式,默認為UIImageResizingModeTile(瓦片式)就是將圖片以原來的大小就行平鋪顯示
[image resizableImageWithCapInsets:UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)];
// 2. 設置圖片拉伸的模式
// UIImageResizingModeTile平鋪 UIImageResizingModeStretch拉伸
[image resizableImageWithCapInsets:UIEdgeInsetsMake(imageheight * 0.5, imagewidth * 0.5, imageheight * 0.5 -1, imagewidth * 0.5 - 1) resizingMode:UIImageResizingModeTile];
傳入的第一個參數(shù)capInsets
是UIEdgeInsets
類型的數(shù)據(jù): UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
這個參數(shù)是一個結構體,定義如下
typedef struct { CGFloat top, left , bottom, right ; } UIEdgeInsets;
該參數(shù)的意思是被保護的區(qū)域到原始圖像外輪廓的上部,左部,底部,右部的直線距離
比如 UIEdgeInsetsMake(42, 0, 0, 0) 指的是拉伸區(qū)选侨,詳細點解釋就是掖鱼,從頂部一直到y(tǒng)=42部分是保護區(qū),從y=42一直到底部是拉伸區(qū)
對于拉伸的方式援制,有以下兩種:
typedef NS_ENUM(NSInteger, UIImageResizingMode) {
UIImageResizingModeTile,(瓦片)
UIImageResizingModeStretch,(伸展)
};
-
IUIImageResizingModeStretch
:拉伸模式戏挡,通過拉伸UIEdgeInsets
指定的矩形區(qū)域來填充圖片 -
UIImageResizingModeTile
:平鋪模式(瓦片),通過重復顯示UIEdgeInsets
指定的矩形區(qū)域來填充圖片
來做四個測試晨仑,假如我們的原始圖像尺寸為60*128
在一個 180 * 384 的窗口進行測試圖像
測試的界面設計如圖
正上方為原始圖像窗口,用于顯示原始圖像的效果
左下方為測試圖像窗口,用于顯示測試狀況的效果
右下方為對比圖像窗口,用于顯示默認狀況的效果
當我們設置capInsets
為以下四種值的時候有什么樣子的效果: - 1:
capInsets
參數(shù)為UIEdgeInsetsMake(0, 0, 0, 0)
- 2:
capInsets
參數(shù)為UIEdgeInsetsMake(42, 0, 0, 0)
- 3:
capInsets
參數(shù)為UIEdgeInsetsMake(0, 20, 0, 0)
- 4:
capInsets
參數(shù)為UIEdgeInsetsMake(42, 20, 42, 20)
拉伸模式
resizingMode參數(shù)為UIImageResizingModeStretch
1.capInsets參數(shù)為UIEdgeInsetsMake(0, 0, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們未對原始圖像的任何區(qū)域進行保護.其拉伸效果如圖
在該種情況下,我們發(fā)現(xiàn)原始圖像按比例放大了3倍,因此我們將該情況當做拉伸模式下的默認狀況
在之后的實驗中,我們將該種狀況當做參考對象,顯示在界面的右下角
2.capInsets參數(shù)為UIEdgeInsetsMake(42, 0, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像上部的三分之一進行保護(即紅色方塊區(qū)域).其拉伸效果如圖
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 原始圖像中受保護的區(qū)域(即紅色方塊區(qū)域)在Y軸方向保持了原比例,但在X軸方向進行了拉伸
-
原始圖像中未受保護的區(qū)域,直接按比例進行了拉伸
3.capInsets參數(shù)為UIEdgeInsetsMake(0,20, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像左部的三分之一進行保護(即紅色方塊區(qū)域).其拉伸效果如圖
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 原始圖像中受保護的區(qū)域(即紅色方塊區(qū)域)在X軸方向保持了原比例,但在Y軸方向進行了拉伸
-
原始圖像中未受保護的區(qū)域,直接按比例進行了拉伸
4.capInsets參數(shù)為UIEdgeInsetsMake(42, 20, 42, 20)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像除數(shù)字5以外的區(qū)域進行保護(即兩個紅色方塊圍起來的區(qū)域).其拉伸效果如圖
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 在X軸上,由于1被左邊和上邊的設置保護,3被右邊和上邊的設置保護,所以只能用中間的2來拉伸,同理最底下的7,8,9
- 在Y軸上,由于1被左邊和上邊的設置保護,7被左邊和下邊的設置保護,所以只能用中間的4來拉伸,同理最底下的3,6,9
-
由于5沒有被保護,所以在整個剩余的空間中,用5進行拉伸填充
選擇平鋪模式
resizingMode參數(shù)為UIImageResizingModeTile
1.capInsets參數(shù)為UIEdgeInsetsMake(0, 0, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們未對原始圖像的任何區(qū)域進行保護.其平鋪效果如圖
在該種情況下,我們發(fā)現(xiàn)原始圖像按比例填充了相框,因此我們將該情況當做拉伸模式下的默認狀況
在之后的實驗中,我們將該種狀況當做參考對象,顯示在界面的右下角
2.capInsets參數(shù)為UIEdgeInsetsMake(42, 0, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像上部的三分之一進行保護(即紅色方塊區(qū)域).其平鋪效果如圖
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 原始圖像中受保護的區(qū)域(即紅色方塊區(qū)域)在Y軸方向保持了原比例,但在X軸方向進行了平鋪填充
-
原始圖像中未受保護的區(qū)域,直接按比例進行了平鋪,但不包含被保護的區(qū)域(注意觀察藍色箭頭所指的區(qū)域)
3.capInsets參數(shù)為UIEdgeInsetsMake(0,20, 0, 0)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像左部的三分之一進行保護(即紅色方塊區(qū)域).其平鋪效果如圖4.2.3
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 原始圖像中受保護的區(qū)域(即紅色方塊區(qū)域)在X軸方向保持了原比例,但在Y軸方向進行了平鋪填充
-
原始圖像中未受保護的區(qū)域,直接按比例進行了平鋪,但不包含被保護的區(qū)域(注意觀察藍色箭頭所指的區(qū)域)
4.capInsets參數(shù)為UIEdgeInsetsMake(42, 20, 42, 20)時
當我們向拉伸方法傳入該組參數(shù)時,代表我們對原始圖像除數(shù)字5以外的區(qū)域進行保護(即兩個紅色方塊圍起來的區(qū)域).其拉伸效果如圖
在該種情況下,我們可以發(fā)現(xiàn)拉伸后的圖像中:
- 在X軸上,由于1被左邊和上邊的設置保護,3被右邊和上邊的設置保護,所以只能用中間的2來平鋪,同理最底下的7,8,9
- 在Y軸上,由于1被左邊和上邊的設置保護,7被左邊和下邊的設置保護,所以只能用中間的4來平鋪,同理最底下的3,6,9
-
由于5沒有被保護,所以在整個剩余的空間中,用5進行平鋪填充
結論和建議
通過8組實驗數(shù)據(jù)可以觀察出拉伸方法在平鋪模式和拉伸模式下的變化過程和主要區(qū)別,由此我們可知:
- 對原始圖形使用拉伸方法且在四周增加保護區(qū)域后,能保證原始圖形的四個角不失真,但其余部分的變化細節(jié)則有不同
- 如果原始圖像的外輪廓不平整的話,使用拉伸方式會讓外輪廓的不平整度放大,使用平鋪方式應該能減小這種情況
測試代碼:
使用了Storyboard搭建軟件界面
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView1;
@property (weak, nonatomic) IBOutlet UIImageView *imageView2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView3;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//--imageView1的相關內容------------------------------------------------------------------------------------
//相框大小為60 * 128 圖片尺寸為60 * 128
//讀取圖片
UIImage *testImage1 = [UIImage imageNamed:@"123456789"];
testImage1 = [testImage1 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeStretch];
self.imageView1.image = testImage1;
//---imageView2的相關內容-----------------------------------------------------------------------------------
//相框為180 * 384 圖片尺寸為60 * 128 相框大小為原始圖片的3倍
//讀取圖片
UIImage *testImage2 = [UIImage imageNamed:@"123456789"];
/***********************************************/
//方法1 resizableImageWithCapInsets:默認是平鋪
//方法2 resizableImageWithCapInsets: resizingMode: 方法
// UIImageResizingModeTile, 平鋪
//平鋪的概念是保證原圖像大小不變,將新圖像填充滿
//testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeTile];
//將上部的三分之一"保護",然后進行顯示
//這代表新圖像中,上部的三分之一和原圖像一樣,而其余部分的填充不會使用原圖像上部的三分之一
// testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(42, 0, 0, 0) resizingMode:UIImageResizingModeTile];
//將左部的三分之一"保護",然后進行顯示
//這代表新圖像中,上部的三分之一和原圖像一樣,而其余部分的填充不會使用原圖像上部的三分之一
// testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(0,20, 0, 0) resizingMode:UIImageResizingModeTile];
//將四周進行保護后
//在X軸上,由于1被左邊的設置保護,3被右邊的設置保護,所以中間只能用2來平鋪,同理,7和9之間的8
//在Y軸上,由于1被上邊的設置保護,7被下邊的設置保護,所以中間只能用4來平鋪,同理,3和9之間的6
//由于5沒有被保護,所以在整個空間中,用5進行平鋪來填充剩余的區(qū)域
// testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(42, 20, 42, 20) resizingMode:UIImageResizingModeTile];
/***********************************************/
//resizableImageWithCapInsets: resizingMode: 方法
// UIImageResizingModeStretch, 拉伸
// 拉伸的概念是直接按比例將圖片放大到與相框尺寸相同的圖像,
//testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeStretch];
//將上部的三分之一"保護",然后進行顯示
//保證原圖像上部的三分之一在Y軸上不被拉伸,其余部分按剩余比例拉伸
//testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(42, 0, 0, 0) resizingMode:UIImageResizingModeStretch];
//將左部的三分之一"保護",然后進行顯示
//保證原圖像左部的三分之一在Y軸上不被拉伸,其余部分按剩余比例拉伸
//testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 20, 0, 0) resizingMode:UIImageResizingModeStretch];
//將四周進行保護后
//在X軸上,由于1被左邊的設置保護,3被右邊的設置保護,所以中間只能用2來拉伸,同理,7和9之間的8
//在Y軸上,由于1被上邊的設置保護,7被下邊的設置保護,所以中間只能用4來拉伸,同理,3和9之間的6
//由于5沒有被保護,所以在整個空間中,用5進行拉伸來填充剩余的區(qū)域
//testImage2 = [testImage2 resizableImageWithCapInsets:UIEdgeInsetsMake(42, 20, 42, 20) resizingMode:UIImageResizingModeStretch];
//將圖片添加到相框
self.imageView2.image = testImage2;
//-----imageView3的相關內容----------------------------------------------------------------------------------
//讀取圖片
UIImage *testImage3 = [UIImage imageNamed:@"123456789"];
/***********************************************/
//resizableImageWithCapInsets默認是平鋪
//resizableImageWithCapInsets默認情況對比圖
//testImage3 = [testImage3 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeTile];
/***********************************************/
//resizableImageWithCapInsets: resizingMode: 方法
// UIImageResizingModeTile, 平鋪 (已經測試過了,)
// UIImageResizingModeStretch, 拉伸
//testImage3 = [testImage3 resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeStretch];
/***********************************************/
//將圖片添加到相框
self.imageView3.image = testImage3;
// Do any additional setup after loading the view, typically from a nib.
}
@end
方法三:
Image Slicing 可視化縮放
相當于一個可視化的resizableImageWithCapInsets
增拥,可以用于指定在圖片縮放時用來填充的像素。我們可以在Xcode
的Assets.xcassets
目錄中選擇要slicing
的圖片寻歧,點擊圖片界面右下方的Show Slicing
按鈕掌栅,在想要設定切片的圖片上點擊Start Slicing
,將出現(xiàn)左中右(或者上中下)三條可以拖動的指示線码泛,通過拖動它們來設定實際的縮放范圍猾封。
他是可視化的resizableImageWithCapInsets,那么它的capInsets在哪里呢噪珊?打開圖片對應的.json文件晌缘,代碼如下:
{
"images" : [
{
"resizing" : {
"mode" : "9-part",
"center" : {
"mode" : "tile",
"width" : 42,
"height" : 92
},
"cap-insets" : {
"bottom" : 0,
"top" : 95,
"right" : 41,
"left" : 0
}
},
從文件可以看出來top
、left
痢站、bottom
磷箕、right
對應的就是上左下右的指示線,看到mode
為tile
阵难,就知道Image Slicing
默認為平鋪縮放岳枷,對于width
與Height
是做什么的呢?Width
代表的是左側線(或者上方線)和中間線之間的區(qū)域,Height
代表的是上側線和中間線之間的區(qū)域空繁。
做個測試:
這是一張2倍像素的圖片殿衰,我把它拖入
Assets.xcassets
中。選擇右下角Slices
的類型為Horizontal and Vertical
:左側ShowSlicing顯示如下:
在
storyboard
拖入一個ImageView
盛泡,上下左右距離邊框均為20闷祥。則顯示如下:簡單來記就是縱橫各三條線,線之外的四個角不會拉伸傲诵。六條線內部交匯處凯砍,亮色區(qū)域為拉伸區(qū)域,灰色蒙版區(qū)域被截取掉拴竹,不顯示果覆。
如果在Xib或者StoryBoard中可以通過View 的Stretching屬性來設置。
UIEdgeInsetsMake使用例子
在創(chuàng)建button的時候經常需要在button上添加圖片和按鈕殖熟,而且一般情況下我們需要的都是圖片在上局待、標題在下的效果(灰色的為button):
而當我們設置好title和image后發(fā)現(xiàn)是圖片居左,標題居右的:
當然這不是我們想要的效果菱属,于是通過設置
UIEdgeInsetsMake
屬性來達到我們想要的效果
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} UIEdgeInsets;
UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)要設置的就是四個邊距
圖中钳榨,藍色標識為可變區(qū)域, 綠色標識為不變區(qū)域纽门。
上面的圖片來自于一葉博客
可以這樣理解:
top
就是距離頂部的距離在默認的基礎上又加上設置的距離薛耻,拿開頭的button來說,在button僅設置image的時候(如果同時設置了image和title效果又是不一樣的赏陵,下面會詳解饼齿,稍安勿躁),image默認是居中的蝙搔,相當于進行了這樣的設置:
[button setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
如果我們在這個時候設置了:
[button setImageEdgeInsets:UIEdgeInsetsMake(20, 0, 0, 0)];
就相當于在圖片居中的基礎上缕溉,將圖片又下移了20,如果是-20則是在原有的基礎上向上移20吃型,其他幾個參數(shù)同理证鸥,正數(shù)就是距相應的邊的距離增加,負數(shù)就是距相應的距離減少勤晚。
為button同時設置圖片和標題
當我們給button設置圖片和title的時候默認是圖片居左枉层,標題距右并排排列的
當我們同時添加圖片和標題時,圖片默認會向左偏移button的titleLabel的寬度赐写,而標題會向右偏移圖片的寬度鸟蜡,既然如此,我們就可以設置偏移量來達到我們想要的任何效果
[button setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 0, -button.titleLabel.intrinsicContentSize.width)];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0, -button.currentImage.size.width, 0, 0)];
設置image偏移量的時候并不是設置的-button.titleLabel.bounds.size.width挺邀,而是-button.titleLabel.intrinsicContentSize.width揉忘,因為在iOS8之后-button.titleLabel.bounds.size.width的值為0跳座,經多方搜索才找到一個替代的方法,這樣設置的意義就是將image的偏移量距右邊的距離減少了titleLabel的寬度癌淮,由于默認是向左便宜了這么多躺坟,我們這樣設置之后相當于抵消了左移的偏移量沦补,所以圖片就居中了乳蓄,對于title道理是一樣的。這一點明白之后剩下的就沒什么難題了夕膀,繼續(xù)虚倒,title下移,image上移:
[button setImageEdgeInsets:UIEdgeInsetsMake(-button.titleLabel.intrinsicContentSize.height, 0, 0, -button.titleLabel.intrinsicContentSize.width)]; [button setTitleEdgeInsets:UIEdgeInsetsMake(button.currentImage.size.height, -button.currentImage.size.width, 0, 0)];
圖片和標題好像有點近产舞,好辦魂奥,再加一點:
[button setTitleEdgeInsets:UIEdgeInsetsMake(button.currentImage.size.height + 20, -button.currentImage.size.width, 0, 0)];
代碼:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 160, 160)];
[button setImage:[UIImage imageNamed:@"ffw_32"] forState:UIControlStateNormal];
[button setTitle:@"按鈕" forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.center = self.view.center;
button.backgroundColor = [UIColor grayColor];
[self.view addSubview:button];
NSLog(@"%f",-button.titleLabel.bounds.size.width);
[button setImageEdgeInsets:UIEdgeInsetsMake(-button.titleLabel.intrinsicContentSize.height, 0, 0, -button.titleLabel.intrinsicContentSize.width)];
[button setTitleEdgeInsets:UIEdgeInsetsMake(button.currentImage.size.height + 20, -button.currentImage.size.width, 0, 0)];
例子:在代碼中使用UIProgressView 設置了Progress Image和Track Image沒能正常的工作
Progress Image:
Track Image:
效果圖:
解決方法:
新建個子類繼承UIProgressView,并添加兩個屬性分別是:
@property (nonatomic,strong)UIImage *ggTrackImage;
@property (nonatomic,strong)UIImage *ggProgressImage;
分別在set方法里對UIProgressView
自帶的trackImage
,和ProgressImge
進行賦值,因為iOS的bug就不能直接拿到屬性進行賦值了易猫,取出UIProgressView
的SubViews
耻煤,并根據(jù)自己的需求對圖片進行拉伸,我這里選擇的是平滑拉伸准颓,代碼如下:
-(void)setGgTrackImage:(UIImage *)ggTrackImage
{
_ggTrackImage=ggTrackImage;
UIImageView *trackImageView=self.subviews.firstObject;
CGRect trackProgressFrame=trackImageView.frame;
trackProgressFrame.size.height=self.frame.size.height;
trackImageView.frame=trackProgressFrame;
CGFloat width = _ggTrackImage.size.width/2.0;
CGFloat height = _ggTrackImage.size.height/2.0;
UIImage *imgTrack = [_ggTrackImage resizableImageWithCapInsets:UIEdgeInsetsMake(height, width, height, width)];
trackImageView.image=imgTrack;
}
-(void)setGgProgressImage:(UIImage *)ggProgressImage
{
_ggProgressImage=ggProgressImage;
CGFloat width = _ggProgressImage.size.width/2.0;
CGFloat height = _ggProgressImage.size.height/2.0;
UIImageView *progressImageView=self.subviews.lastObject;
CGRect ProgressFrame=progressImageView.frame;
ProgressFrame.size.height=self.frame.size.height;
progressImageView.frame=ProgressFrame;
UIImage *imgProgress = [_ggProgressImage resizableImageWithCapInsets:UIEdgeInsetsMake(height, width, height哈蝇, width)];progressImageView.image=imgProgress;
}
搞定,將這個類導入其他使用直接進行圖片賦值即可
參考博客:
http://www.reibang.com/p/3907e6116f41
http://www.reibang.com/p/af2d471f7b9c
http://www.reibang.com/p/a577023677c1
http://www.reibang.com/p/4e4801cbda2b
http://www.reibang.com/p/901f9b98f7c2
http://www.reibang.com/p/0d3dbc30fad5