異步繪製專題
1 圖片處理
1.1 編輯圖片的幾個方法
第一種
先用UIImage對象加載一張圖片
然後轉化成CGImageRef放到CGContext中去編輯
第二種
用CGImageCreate函數創建CGImageRef
然後把CGImageRef放到CGContext中去編輯
第三種
用CGImageCreateCopy或者CGImageCreateCopyWithColorSpace
函數拷貝
CGImageRef CGImageCreate (
size_t width, //圖片的寬度
size_t height, //圖片的高度
size_t bitsPerComponent, //圖片每個顏色的bits,比如rgb顏色空間,有可能是5或者 8 ==
size_t bitsPerPixel, //每一個像素佔用的buts,15位24位 32位等等
size_t bytesPerRow, //每一行佔用多少bytes注意是bytes不是bits 1byte= 8bit
CGColorSpaceRef colorspace, //顏色空間,比如rgb
CGBitmapInfo bitmapInfo, //layout,像素中bit的佈局,是rgba還是 argb,==
CGDataProviderRefprovider, //數據源提供者,url或者內存==
const CGFloat decode[], //一個解碼數組
bool shouldInterpolate, //抗鋸齒參數
CGColorRenderingIntent intent
//圖片渲染相關參數
);
1.2 示例代碼
CGImageRef CGImageCreate(size_t width,size_t height,
size_tbitsPerComponent,size_t bitsPerPixel,size_t bytesPerRow,
CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRefprovider,
constCGFloat decode[],bool shouldInterpolate,
CGColorRenderingIntent intent);
通過這個方法,我們可以創建出一個CGImageRef類型的對象,下面分別對參數進行解釋:
sizt_t是定義的一個可移植性的單位,在64位機器中爲8字節,32位位4字節。
width:圖片寬度像素
height:圖片高度像素
bitsPerComponent:每個顏色的比特數,例如在rgba-32模式下爲8
bitsPerPixel:每個像素的總比特數
bytesPerRow:每一行佔用的字節數,注意這裏的單位是字節
space:顏色空間模式,例如constCFStringRef kCGColorSpaceGenericRGB這個函數可以返回一個顏色空間對象。
bitmapInfo:位圖像素佈局,枚舉如下:
?
1 2 3 4 5 6 7 8 9 10 11 |
typedef CF_OPTIONS(uint32_t, CGBitmapInfo) { kCGBitmapAlphaInfoMask = 0x1F, kCGBitmapFloatComponents = (1 << 8),
kCGBitmapByteOrderMask = 0x7000, kCGBitmapByteOrderDefault = (0 << 12), kCGBitmapByteOrder16Little = (1 << 12), kCGBitmapByteOrder32Little = (2 << 12), kCGBitmapByteOrder16Big = (3 << 12), kCGBitmapByteOrder32Big = (4 << 12) } |
provider:數據源提供者
decode[]:解碼渲染數組
shouldInterpolate:是否抗鋸齒
intent:圖片相關參數
CGImageRef CGImageMaskCreate(size_t width,size_t height,
size_t bitsPerComponent,size_t bitsPerPixel,size_t bytesPerRow,
CGDataProviderRef provider,constCGFloat decode[],bool shouldInterpolate)
這個方法用於創建mask圖片圖層,可以設置其顯示部分與不顯示部分達到特殊的效果,參數意義同上。
CGImageRef CGImageCreateCopy(CGImageRef image)
這個方法可以複製一個CGImageRef對象
CGImageRef CGImageCreateWithJPEGDataProvider(CGDataProviderRef
source, constCGFloat decode[],bool shouldInterpolate,
CGColorRenderingIntent intent)
通過JPEG數據源獲取圖像
CGImageRef CGImageCreateWithPNGDataProvider(CGDataProviderRef source,
constCGFloat decode[],bool shouldInterpolate,
CGColorRenderingIntent intent)
通過PNG數據源獲取圖像
CGImageRef CGImageCreateWithImageInRect(CGImageRef image,
CGRect rect)
截取圖像的一個區域重繪圖像
CGImageRef CGImageCreateWithMask(CGImageRef image,CGImageRef mask)
截取mask圖像的某一區域重繪
CGImageRef CGImageCreateWithMaskingColors(CGImageRef image,
constCGFloat components[])
通過顏色分量數組創建位圖
CGImageRef CGImageCreateCopyWithColorSpace(CGImageRef image,
CGColorSpaceRef space)
通過顏色空間模式複製位圖
CGImageRef CGImageRetain(CGImageRef image)
引用+1
void CGImageRelease(CGImageRef image)
引用-1
bool CGImageIsMask(CGImageRef image)
返回是否爲Mask圖層
size_t CGImageGetWidth(CGImageRef image)
獲取寬度像素
size_t CGImageGetHeight(CGImageRef image)
獲取高度像素
下面這些方法分別獲取相應屬性
size_t CGImageGetBitsPerComponent(CGImageRef image)
size_t CGImageGetBitsPerPixel(CGImageRef image)
size_t CGImageGetBytesPerRow(CGImageRef image)
CGColorSpaceRef CGImageGetColorSpace(CGImageRef image)CG_EXTERNCGImageAlphaInfo CGImageGetAlphaInfo(CGImageRef image)
CGDataProviderRef CGImageGetDataProvider(CGImageRef image)
constCGFloat *CGImageGetDecode(CGImageRef image)
bool CGImageGetShouldInterpolate(CGImageRef image)
CGColorRenderingIntent CGImageGetRenderingIntent(CGImageRef image)
CGBitmapInfo CGImageGetBitmapInfo(CGImageRef image)
1.3 PNG與JPEG優劣比較
存儲速度:JPG更快
壓縮比:JPG更大;
圖片質量:JPG更好
JPG不支持透明效果;
UIImageJPEGRepresentation方法在耗時上比較少 而UIImagePNGRepresentation耗時操作時間比較長
UIImageJPEGRepresentation函數需要兩個參數:圖片的引用和壓縮係數.而UIImagePNGRepresentation只需要圖片引用作爲參數.通過在實際使用過程中,比較發現:UIImagePNGRepresentation(UIImage* image) 要比UIImageJPEGRepresentation(UIImage*image, 1.0) 返回的圖片數據量大很多.譬如,同樣是讀取攝像頭拍攝的同樣景色的照片, UIImagePNGRepresentation()返回的數據量大小爲199K ,而 UIImageJPEGRepresentation(UIImage* image, 1.0)返回的數據量大小隻爲140KB,比前者少了50多KB.如果對圖片的清晰度要求不高,還可以通過設置 UIImageJPEGRepresentation函數的第二個參數,大幅度降低圖片數據量.譬如,剛纔拍攝的圖片, 通過調用UIImageJPEGRepresentation(UIImage*image, 1.0)讀取數據時,返回的數據大小爲140KB,但更改壓縮係數後,通過調用UIImageJPEGRepresentation(UIImage* image, 0.5)讀取數據時,返回的數據大小隻有11KB多,大大壓縮了圖片的數據量 ,而且從視角角度看,圖片的質量並沒有明顯的降低.因此,在讀取圖片數據內容時,建議優先使用UIImageJPEGRepresentation,並可根據自己的實際使用場景,設置壓縮係數,進一步降低圖片數據量大小.
1.4 圖片縮放
圖片縮放的三個函數
http://www.cnblogs.com/pengyingh/articles/2355052.html
程序中一個界面用到了好多張大圖,內存報警告了,所以做了一下圖片縮放,在網上找了別人寫的代碼
//把圖片做等比縮放,生成一個新圖片
1 - (UIImage*) imageByScalingProportionallyToSize:(CGSize)targetSize sourceImage:(UIImage*)sourceImage {
2
3 UIGraphicsBeginImageContext(targetSize);
4 [sourceImage drawInRect:CGRectMake(0,0,targetSize.width, targetSize.height)];
5 UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
6 UIGraphicsEndImageContext();
7 returnscaledImage;
8
9 UIImage *newImage = nil;
10 CGSize imageSize = sourceImage.size;
11 CGFloat width = imageSize.width;
12 CGFloat height = imageSize.height;
13 CGFloat targetWidth = targetSize.width;
14 CGFloat targetHeight = targetSize.height;
15 CGFloat scaleFactor = 0.0;
16 CGFloat scaledWidth = targetWidth;
17 CGFloat scaledHeight = targetHeight;
18 CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
19
20 UIGraphicsBeginImageContext(targetSize);// this will crop
21
22 CGRect thumbnailRect = CGRectZero;
23 thumbnailRect.origin = thumbnailPoint;
24 thumbnailRect.size.width = scaledWidth;
25 thumbnailRect.size.height = scaledHeight;
26
27 [sourceImage drawInRect:thumbnailRect];
28
29 newImage =UIGraphicsGetImageFromCurrentImageContext();
30 if(newImage== nil)
31 NSLog(@"couldnot scale image");
32
33 //pop thecontext to get back to the default
34 UIGraphicsEndImageContext();
35 returnnewImage;
36 }
//把圖片按照新大小進行裁剪,生成一個新圖片
1 - (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize image:(UIImage *)sourceImage
2 {
3 // UIImage *sourceImage = self;
4 UIImage *newImage = nil;
5 CGSize imageSize = sourceImage.size;
6 CGFloat width = imageSize.width;
7 CGFloat height = imageSize.height;
8 CGFloat targetWidth = targetSize.width;
9 CGFloat targetHeight = targetSize.height;
10 CGFloat scaleFactor = 0.0;
11 CGFloat scaledWidth = targetWidth;
12 CGFloat scaledHeight = targetHeight;
13 CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
14
15 if(CGSizeEqualToSize(imageSize, targetSize) == NO)
16 {
17 CGFloat widthFactor = targetWidth /width;
18 CGFloat heightFactor = targetHeight / height;
19
20 if(widthFactor > heightFactor)
21 scaleFactor = widthFactor;// scale to fit height
22 else
23 scaleFactor = heightFactor;// scale to fit width
24 scaledWidth = width * scaleFactor;
25 scaledHeight = height * scaleFactor;
26
27 // centerthe image
28 if(widthFactor > heightFactor)
29 {
30 thumbnailPoint.y = (targetHeight -scaledHeight) *0.5;
31 }
32 else
33 if(widthFactor < heightFactor)
34 {
35 thumbnailPoint.x =(targetWidth - scaledWidth) *0.5;
36 }
37 }
38
39 UIGraphicsBeginImageContext(targetSize);// this will crop
40
41 CGRect thumbnailRect = CGRectZero;
42 thumbnailRect.origin = thumbnailPoint;
43 thumbnailRect.size.width = scaledWidth;
44 thumbnailRect.size.height = scaledHeight;
45
46 [sourceImage drawInRect:thumbnailRect];
47
48 newImage =UIGraphicsGetImageFromCurrentImageContext();
49 if(newImage== nil)
50 NSLog(@"couldnot scale image");
51
52 //pop thecontext to get back to the default
53 UIGraphicsEndImageContext();
54 returnnewImage;
55 }
1 - (UIImage*)generatePhotoThumbnail:(UIImage *)image
2 {
3 // Create a thumbnail version of the imagefor the event object.
4 CGSize size = image.size;
5 CGSize croppedSize;
6 CGFloat ratio = 64.0;//這個是設置轉換後圖片的尺寸大小
7 CGFloat offsetX = 0.0;
8 CGFloat offsetY = 0.0;
9
10 // check thesize of the image, we want to make it
11 // a square with sides the size of the smallest dimension
12 if(size.width > size.height) {
13 offsetX = (size.height - size.width) /2;
14 croppedSize = CGSizeMake(size.height,size.height);
15 } else {
16 offsetY = (size.width - size.height) /2;
17 croppedSize = CGSizeMake(size.width,size.width);
18 }
19
20 // Crop theimage before resize
21 CGRect clippedRect = CGRectMake(offsetX *-1, offsetY * -1,croppedSize.width, croppedSize.height);
22 //裁剪圖片
CGImageRef imageRef =CGImageCreateWithImageInRect([image CGImage], clippedRect);
23 // Donecropping
24
25 // Resize the image
26 CGRect rect = CGRectMake(0.0,0.0,ratio, ratio);
27
28 UIGraphicsBeginImageContext(rect.size);
29 [[UIImage imageWithCGImage:imageRef]drawInRect:rect];
30 UIImage *thumbnail =UIGraphicsGetImageFromCurrentImageContext();
31 UIGraphicsEndImageContext();
32 // DoneResizing
33
34 returnthumbnail;
35 }
實際應用簡化
- (UIImage *)generatePhotoThumbnail:(UIImage*)image
{
CGRect rect=CGRectMake(0, 0, 60, 78);
//裁剪圖片
CGImageRef imageRef=CGImageCreateWithImageInRect([image CGImage],CGRectMake(0,0,140,182));
UIGraphicsBeginImageContext(rect.size);
[[UIImage imageWithCGImage:imageRef]drawInRect:rect];
//如果不裁剪圖片可以直接畫
//[image drawInRect:CGRectMake(0, 0, theSize.width, theSize.height)];
UIImage *thumbnail=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return thumbnail;
}
附:
UIImage類並沒有提供縮放圖片需要用到的API,是不是覺得很吃驚?沒關係,我們自己來添加一個。
定義縮放圖片的Category
// UIImage+Scale.h
@interface UIImage (scale)
-(UIImage*)scaleToSize:(CGSize)size;
@end
實現這個Category的定義
// UIImage+Scale.m
#import "UIImage+Scale.h"
@implementation UIImage (scale)
-(UIImage*)scaleToSize:(CGSize)size
{// 創建一個bitmap的context//並把它設置成爲當前正在使用的context
UIGraphicsBeginImageContext(size);
// 繪製改變大小的圖片
[self drawInRect:CGRectMake(0,0,size.width, size.height)];
// 從當前context中創建一個改變大小後的圖片
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// 使當前的context出堆棧
UIGraphicsEndImageContext();
// 返回新的改變大小後的圖片
return scaledImage;
}
@end
如何使用
// 創建圖片
UIImage *image =[UIImage imageNamed:@"myImage.png"];
// 更改圖片大小
UIImage *scaledImage =[image scaleToSize:CGSizeMake(25.0f,35.0f)]
1.5 參考鏈接
IOS-圖片操作集合
http://blog.csdn.net/ch_soft/article/details/7685753
UIImagePNGRepresentation 存在緩慢問題
http://blog.sina.com.cn/s/blog_95a3991f010162ws.html
<iOS>UIImage變爲NSData並進行壓縮
http://www.cnblogs.com/robinkey/archive/2013/01/21/2869930.html
UIImageJPEGRepresentation和UIImagePNGRepresentation
http://blog.csdn.net/mideveloper/article/details/11473627
png有透明通道,JPEG無
http://blog.163.com/chentong1115@126/blog/static/45314732200972323921819/
透明PNG圖片有黑邊的解決方法
用UIImage和UIButton畫出的按鈕,使用透明的png圖片,爲什麼會出現白邊
http://segmentfault.com/q/1010000000095447
JPG、PNG和GIF圖片的基本原理及優化方法
http://www.mahaixiang.cn/Photoshop/400.html
JPEG 原理詳細
http://blog.chinaunix.net/uid-27002868-id-3220554.html
IOS開發中圖片資源使用png還是jpg格式
http://www.cnblogs.com/wengzilin/p/3485298.html
(good)ios開發圖片格式的選擇:png和jpg
http://m.blog.csdn.net/blog/awaylin113/22712317
IOS開發之保存圖片到Documents目錄及PNG,JPEG格式相互轉換
http://blog.csdn.net/sanpintian/article/details/7418755
iOS過濾png圖片透明部分點擊事件
http://www.cocoachina.com/industry/20121127/5192.html
JPEG壓縮原理
http://blog.csdn.net/xfortius/article/details/8904012
png壓縮原理
http://blog.csdn.net/zykun/article/details/1825086
iOS開發,圖片使用png好還是jpg好?
http://www.cocoachina.com/bbs/read.php?tid=110115
2 繪製文本
2.1 NSMutableAttributedString繪製
CGRect textViewRect = CGRectMake(ICON_SPACE*1-2,_imageHeight +ICON_SPACE,_postContentTextView.frame.size.width,_labelSize);//ceilf(_labelSize)
// [_postContentTextViewdrawRect:textViewRect];
NSMutableAttributedString *str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentText];
[str addAttribute:NSForegroundColorAttributeNamevalue:kContentTextColorrange:NSMakeRange(0,[_vm.contentTextlength])];
[str addAttribute:NSFontAttributeNamevalue:kContentTextFontrange:NSMakeRange(0,[_vm.contentTextlength])];
[str addAttribute:NSBackgroundColorDocumentAttributevalue:[UIColorwhiteColor]range:NSMakeRange(0,[_vm.contentTextlength])];
// NSLog(@"ContentText:%@, Frame:%@",_vm.contentText, NSStringFromCGRect(_postContentTextView.frame));
// NSLog(@"TextViewRect:%@",NSStringFromCGRect(textViewRect));
[str drawInRect:textViewRect];
2.2 參考資料
IOS開發(78)之繪製文本
http://www.2cto.com/kf/201305/212045.html
iOS 界面上繪製不同字體 顏色 大小的字符串
http://blog.csdn.net/wsk_123_123/article/details/23277457
初探NSAttributedString和NSMutableAttributedString的使用 -LiuWJ
http://www.tuicool.com/articles/Fvqia2
iOS 字符屬性NSAttributedString描述
http://my.oschina.net/lanrenbar/blog/395909
NSAttributedString 詳解
http://www.cnblogs.com/zhw511006/archive/2012/09/21/2696700.html
3 異步繪製
3.1 異步繪製示例
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
CGRect drawRect = _bgImageView.frame;
UIGraphicsBeginImageContextWithOptions(drawRect.size,YES,0);
CGContextRef context = UIGraphicsGetCurrentContext();
if (!context) {
return;
}
[[UIColor whiteColor] set];
CGContextFillRect(context, drawRect);
CGRect imgRect = CGRectZero;
if ([_vm.contentImgPathlength] >0) {
imgRect = CGRectMake(0,0,BODY_HEIGTH,_imageHeight);//_postContentImageView.frame;
[_vm.contentImagedrawInRect:_postContentImageView.frameblendMode:kCGBlendModeNormalalpha:1];
}
CGRect textViewRect = CGRectZero;
if ([_vm.contentTextlength] >0) {
NSMutableAttributedString*str;
if (!_isContentDisplayCompletly) {
if (_vm.digestText) {
str = [[NSMutableAttributedStringalloc]initWithString:_vm.digestTextattributes:_postContentTextView.typingAttributes];
} else
str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentTextattributes:_postContentTextView.typingAttributes];
} else
str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentTextattributes:_postContentTextView.typingAttributes];
[str drawInRect:_postContentTextView.frame];
}
if (_subjectTitleHeight>0) {
CGRectsubjectIconFrame =CGRectMake(_subjectButton.frame.origin.x,_subjectButton.frame.origin.y,10,12);//kContentToSubjectSpace
UIImage*iconImg = [UIImageimageNamed:@"PostChannelFlageIcon"];
subjectIconFrame.size =iconImg.size;
[iconImg drawInRect:subjectIconFrameblendMode:kCGBlendModeNormalalpha:1];
CGRectsubjectTitleFrame =CGRectMake(subjectIconFrame.origin.x +subjectIconFrame.size.width +2,subjectIconFrame.origin.y-1,200,_subjectTitleHeight);
[_subjectButton.titleLabel.attributedTextdrawInRect:subjectTitleFrame];
// [_subjectButtondrawRect:_subjectButton.frame];
}
UIImage*temp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(),^{
_bgImageView.image =nil;
_bgImageView.image =temp;
[self setHidden:NO];
});
});
3.2 DrawRect之後注意用hitTest:withEvent:方法處理事件接收
//用戶觸摸時第一時間加載內容
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event{
UIView*result = [superhitTest:pointwithEvent:event];
CGPointbuttonPoint = [_subjectButtonconvertPoint:pointfromView:self];
if ([_subjectButtonpointInside:buttonPointwithEvent:event]){
return _subjectButton;
}
returnresult;
}
3.3 參考鏈接
[iOS Animation]-CALayer 繪圖效率-異步繪製
http://my.oschina.net/u/2438875/blog/507545?fromerr=R4LnEaJ5
CGDataProviderCreateWithData對內存數據的釋放
http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VnJQ6jaitZF
IOS中使用像素位圖(CGImageRef)對圖片進行處理
http://my.oschina.net/u/2340880/blog/406437?p={{currentPage-1}}
4 Asyncdisplaykit
4.1 參考鏈接
Asyncdisplaykit 指南(一)
http://www.th7.cn/Program/IOS/201410/302413.shtml
AsyncDisplayKit 教程:達到 60FPS 的滾動幀率
http://www.cocoachina.com/swift/20141124/10298.html
http://asyncdisplaykit.org/guide/
AsyncDisplayKit入門指南
http://www.cocoachina.com/ios/20141020/9975.html
5 開發技巧
5.1 常見問題
5.1.1 CGBitmapContextCreateImage繪製後內存泄露導致內存告警
1、 CGBitmapContextCreateImage繪製的圖片會造成內存無法釋放,應該換用CGDataProviderCreateWithCFData。
5.1.1.1 方案一:修改源代碼,入緩存前壓縮
http://my.oschina.net/u/1244672/blog/510379
SDWebImage有一個SDWebImageDownloaderOperation類來執行下載操作的。裏面有個下載完成的方法:
- (void)connectionDidFinishLoading:(NSURLConnection*)aConnection {
SDWebImageDownloaderCompletedBlockcompletionBlock = self.completedBlock;
@synchronized(self) {
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread= nil;
self.connection= nil;
[[NSNotificationCenterdefaultCenter] postNotificationName:SDWebImageDownloadStopNotificationobject:nil];
}
if(![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {
responseFromCached= NO;
}
if(completionBlock)
{
if(self.options & SDWebImageDownloaderIgnoreCachedResponse &&responseFromCached) {
completionBlock(nil,nil, nil, YES);
}
else {
UIImage *image= [UIImage sd_imageWithData:self.imageData];
NSString *key= [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
image = [selfscaledImageForKey:key image:image];
// Do notforce decoding animated GIFs
if(!image.images) {
image = [UIImagedecodedImageWithImage:image];
}
if(CGSizeEqualToSize(image.size, CGSizeZero)) {
completionBlock(nil,nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0pixels"}], YES);
}
else {
completionBlock(image,self.imageData, nil, YES);
}
}
}
self.completionBlock= nil;
[self done];
}
其中,UIImage*image = [UIImage sd_imageWithData:self.imageData];就是將data轉換成image。
再看看sd_imageWithData:這個方法:
+ (UIImage*)sd_imageWithData:(NSData *)data {
UIImage*image;
NSString*imageContentType = [NSData sd_contentTypeForImageData:data];
if([imageContentType isEqualToString:@"image/gif"]) {
image =[UIImage sd_animatedGIFWithData:data];
}
#ifdef SD_WEBP
else if([imageContentType isEqualToString:@"image/webp"])
{
image =[UIImage sd_imageWithWebPData:data];
}
#endif
else {
image =[[UIImage alloc] initWithData:data];
UIImageOrientationorientation = [self sd_imageOrientationFromImageData:data];
if(orientation != UIImageOrientationUp) {
image =[UIImage imageWithCGImage:image.CGImage
scale:image.scale
orientation:orientation];
}
}
return image;
}
這個方法在UIImage+MultiFormat裏面,是UIImage的一個類別處理。這句話很重要image =[[UIImage alloc] initWithData:data]; SDWebImage把下載下來的data直接轉成image,然後沒做等比縮放直接存起來使用。所以,我們只需要在這邊做處理即可:
UIImage+MultiFormat添加一個方法:
+(UIImage*)compressImageWith:(UIImage *)image
{
float imageWidth= image.size.width;
float imageHeight = image.size.height;
float width =640;
float height =image.size.height/(image.size.width/width);
float widthScale = imageWidth /width;
float heightScale = imageHeight /height;
// 創建一個bitmap的context
// 並把它設置成爲當前正在使用的context
UIGraphicsBeginImageContext(CGSizeMake(width,height));
if (widthScale> heightScale) {
[imagedrawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];
}
else {
[imagedrawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];
}
// 從當前context中創建一個改變大小後的圖片
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
// 使當前的context出堆棧
UIGraphicsEndImageContext();
returnnewImage;
}
然後在:image =[[UIImage alloc] initWithData:data];下面調用以下:
if(data.length/1024 > 1024) {
image = [selfcompressImageWith:image];
}
當data大於1M的時候做壓縮處理。革命尚未成功,還需要一步處理。在SDWebImageDownloaderOperation的connectionDidFinishLoading方法裏面的:
UIImage *image= [UIImage sd_imageWithData:self.imageData];
//將等比壓縮過的image在賦在轉成data賦給self.imageData
NSData *data =UIImageJPEGRepresentation(image, 1);
self.imageData = [NSMutableDatadataWithData:data];
5.1.1.2 方案二:設置全局緩存大小
http://www.myexception.cn/swift/2033029.html
1、首先在appdelegate方法 didFinishLaunchingWithOptions
SDImageCache.sharedImageCache().maxCacheSize=1024*1024*8設置一下最大的緩存大小。
2、在appdelegate applicationDidReceiveMemoryWarning里加入
SDImageCache.sharedImageCache().clearMemory()
SDWebImageManager.sharedManager().cancelAll()
5.1.1.3 方案三:定時清理內存緩存
http://www.bubuko.com/infodetail-956863.html
經過嘗試,發現了一個最簡單的完美解決該問題的方法
在使用SDWebImage加載較多圖片造成內存警告時,定期調用
[[SDImageCache sharedImageCache] setValue:nilforKey:@"memCache"];
5.1.1.4 方案四(不推薦):修復SD庫代碼,不做解壓,直接返回壓縮的原圖
5.1.1.5 方案五(推薦):使用CGDataProviderRef進行圖形解壓重繪
iOS開發中界面展示大圖片時UIImage的性能有關問題
http://www.myexception.cn/operating-system/578931.html
#import "SDWebImageDecoder.h"
@implementation UIImage (ForceDecode)
+ (UIImage*)decodedImageWithImage:(UIImage *)image {
if (image.images) {
// Do notdecode animated images
return image;
}
//僅僅作爲臨時應付方案
// return image;
UIImage*decompressedImage;
@autoreleasepool {
//核心代碼,可以解決內存未釋放問題
NSData *data=UIImageJPEGRepresentation(image,1);
CGDataProviderRef dataProvider =CGDataProviderCreateWithCFData((__bridgeCFDataRef)data);
CGImageRefimageRef =CGImageCreateWithPNGDataProvider(dataProvider,
NULL,NO,
kCGRenderingIntentDefault);
// CGImageRef imageRef = image.CGImage;
CGSizeimageSize = CGSizeMake(CGImageGetWidth(imageRef),CGImageGetHeight(imageRef));
CGRectimageRect = (CGRect){.origin =CGPointZero, .size =imageSize};
CGColorSpaceRefcolorSpace =CGColorSpaceCreateDeviceRGB();
CGBitmapInfobitmapInfo = CGImageGetBitmapInfo(imageRef);
intinfoMask = (bitmapInfo &kCGBitmapAlphaInfoMask);
BOOLanyNonAlpha = (infoMask ==kCGImageAlphaNone ||
infoMask == kCGImageAlphaNoneSkipFirst ||
infoMask == kCGImageAlphaNoneSkipLast);
// CGBitmapContextCreatedoesn't support kCGImageAlphaNone with RGB.
// https://developer.apple.com/library/mac/#qa/qa1037/_index.html
if(infoMask == kCGImageAlphaNone&& CGColorSpaceGetNumberOfComponents(colorSpace)>1) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
// Set noneSkipFirst.
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
}
// Some PNGs tell us theyhave alpha but only 3 components. Odd.
else if(!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace)==3) {
// Unset the old alphainfo.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
// It calculates thebytes-per-row based on the bitsPerComponent and width arguments.
CGContextRefcontext = CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
// If failed, returnundecompressed image
if(!context) return image;
CGContextDrawImage(context,imageRect, imageRef);
CGImageRefdecompressedImageRef =CGBitmapContextCreateImage(context);
CGContextRelease(context);
decompressedImage = [UIImageimageWithCGImage:decompressedImageRefscale:image.scaleorientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
}
// CVPixelBufferRef pixelBuffer;
//
// CreateCGImageFromCVPixelBuffer(pixelBuffer,&decompressedImageRef);
// CGImage *cgImage =CGBitmapContextCreateImage(context);
// CFDataRef dataRef =CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
// CGImageRelease(cgImage);
// image->imageRef = dataRef;
// image->image =CFDataGetBytePtr(dataRef);
returndecompressedImage;
}
5.1.2 繪製時底部出現高度不定的細微黑線
問題原因:
將Text做寬高計算時,高度值容易得出小數數值,而頁面繪製均是基於整數像素點繪製,對於小數點部分,系統會做捨去處理(即便有縮放),固留下高度不定的未繪製區域(爲黑色)。
解決方案:
將計算出來的高度值做向下取整處理即可。
CGRect labelFrame = [content boundingRectWithSize:size options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeadingattributes:_postContentTextView.typingAttributescontext:nil];
labelSize = labelFrame.size;
labelSize.height = ceilf(labelSize.height);
6 參考鏈接
(GOOD)iOS開發中界面展示大圖片時UIImage的性能有關問題
http://www.myexception.cn/operating-system/578931.html
(Good)iPhone - UIImage Leak, CGBitmapContextCreateImage Leak
http://stackoverflow.com/questions/1427478/iphone-uiimage-leak-cgbitmapcontextcreateimage-leak
Another iPhone - CGBitmapContextCreateImage Leak
http://stackoverflow.com/questions/1434714/another-iphone-cgbitmapcontextcreateimage-leak
UIGraphicsBeginImageContext vs CGBitmapContextCreate
http://stackoverflow.com/questions/4683448/uigraphicsbeginimagecontext-vs-cgbitmapcontextcreate
iPhone - CGBitmapContextCreateImage Leak, Anyone else withthis problem?
Build and Analyze false positive on leak detection?
http://stackoverflow.com/questions/8438249/build-and-analyze-false-positive-on-leak-detection
iPhone - Multiple CGBitmapContextCreateImage Calls -ObjectAlloc climbing
(Good)ios開發圖片處理,內存泄露
http://www.oschina.net/question/736524_69802
主題 : CGBitmapContextCreateImage(bitmap)內存泄露問題處理
http://www.cocoachina.com/bbs/read.php?tid=31835
iOS異步圖片加載優化與常用開源庫分析
http://luoyibu.com/2015/05/12/iOS異步圖片加載優化與常用開源庫分析/
主題 : 圖片處理開源函數ImageProcessing CGDataProviderCreateWithData Bug修復
http://www.cocoachina.com/bbs/read.php?tid=116149
CGDataProviderCreateWithData對內存數據的釋放
http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VmpqgoSitZE
IOS7.x下UIGraphicsGetImageFromCurrentImageContext引發內存暴漲,導致應用被結束掉
http://blog.163.com/l1_jun/blog/static/1438638820155593641529/
在 iOS 中與CGContextRef 的內存泄漏
http://www.itstrike.cn/Question/55b86ce7-dfba-4548-a103-22dc5317420a.html
Quartz 2D (ProgrammingWithQuartz) note
http://renxiangzyq.iteye.com/blog/1188025
使用AFNetworking, SDWebimage和OHHTTPStubs
http://blog.shiqichan.com/using-afnetworking-sdwebimage-and-ohhttpstubs/
SDWebImage緩存圖片的機制(轉)
http://blog.csdn.net/zhun36/article/details/8900327
近來一個swift項目用uicollectionview 用sdwebimage 加載圖片,發生內存猛增,直接閃退的情況,簡單說一下解決方案
http://www.myexception.cn/swift/2033029.html
關於SDWebImage加載高清圖片導致app崩潰的問題
http://www.bubuko.com/infodetail-956863.html
SDWebImage加載大圖導致的內存警告問題
http://blog.csdn.net/richer1997/article/details/43481959
解決MWPhotoBrowser中的SDWebImage加載大圖導致的內存警告問題
http://my.oschina.net/u/1244672/blog/510379
使用SDWebImage加載大量圖片後造成內存泄露的解決辦法
http://www.bubuko.com/infodetail-985746.html
UIGraphicsBeginImageContext系列知識
http://blog.sina.com.cn/s/blog_5fb39f9101017n1v.html
iOS繪圖教程
http://blog.csdn.net/nogodoss/article/details/18660153
Quartz 2D 參考-文本
http://blog.csdn.net/kmyhy/article/details/7258338
CGBitmapContextCreate函數
http://blog.csdn.net/thanklife/article/details/25790433
UIGraphicsBeginImageContext創建的映像停留在內存中永恆
多次在cell中加載網絡圖片後,內存增長,以前資源未釋放
http://bbs.csdn.net/topics/390891681
請問下面的代碼有潛在的內存泄漏?
[ios]UIGraphicsGetImageFromCurrentImageContext()-內存泄漏
http://www.itstrike.cn/Question/88ada9bd-911c-44a7-874b-e04c1a1c2bca.html
[轉載]ios開發之View屬性hidden, opaque, alpha的區別
http://blog.sina.com.cn/s/blog_7da2c9030101ev8n.html
利用預渲染加速iOS設備的圖像顯示
http://www.keakon.net/2011/07/26/利用預渲染加速iOS設備的圖像顯示
iOS使用CGContextRef繪製各種圖形
http://www.devstore.cn/essay/essayInfo/116.html
iOS CGContextRef畫圖小結
http://blog.sina.com.cn/s/blog_9693f61a0101deko.html
IOS用CGContextRef畫各種圖形(文字、圓、直線、弧線、矩形、扇形、橢圓、三角形、圓角矩形、貝塞爾曲線、圖片)
http://blog.csdn.net/rhljiayou/article/details/9919713
iOS 畫圖 以及清空
http://blog.csdn.net/woshidaniu/article/details/46683409
iOS通過Quartz畫矩形、文字、線
http://blog.csdn.net/onlyou930/article/details/7726399