我正在为我的某个应用呈现带有文字的图像,并且对UI性能有显着影响(可能会大约1秒冻结),所以我在后台线程上进行操作。由于图片包含文字,因此使用UILabel
和其他UIView
可以轻松地将所有内容都展开,并将包含所有内容的视图渲染为图像。
但是,我收到来自Xcode的警告,说它在后台线程上是不允许的,因为它使用UIKit。为什么我不允许在后台线程上调用UIKit,即使我的用例是完全自包含的并且与屏幕上的任何渲染隔离?
为了帮助下面的代码更有意义,它绘制了一个图像,该图像是几个项目的列表,每个项目由两个小方块图像和一行中的项目名称组成。该列表可以有多个列。代码已稍微调整(主要是变量名称),以避免显示专有代码,但执行相同的工作。
我的代码:
NSArray<MyItem*>* items; // These are the items that I'm drawing. They
// get set before the following code is called.
// Processing code:
const CGFloat TITLE_FONT_SIZE = 50; // font size of the title
const CGFloat ITEM_FONT_SIZE = 25; // font size of the item names
const int OUTER_PADDING = 60; // padding from the edge of the image to the main content
const int ROW_PADDING = 13; // padding between rows
const int COL_PADDING = 100; // padding between columns
const int PADDING = 20; // padding between content items in a row
const int BOX_SIZE = 25; // how high/wide each image is
const int ROW_HEIGHT = BOX_SIZE; // pixel height of a line
const int COL_WIDTH = 500; // pixel width of a column (image1, image2, and name)
// compute the dimensions of the image
UILabel* titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont systemFontOfSize:TITLE_FONT_SIZE];
titleLabel.text = @"My image";
[titleLabel sizeToFit];
titleLabel.frame = CGRectMake(OUTER_PADDING, OUTER_PADDING / 2, titleLabel.frame.size.width, titleLabel.frame.size.height);
const int MIN_NUM_COLS = 1 + ((titleLabel.frame.size.width - COL_WIDTH) / (COL_WIDTH + COL_PADDING));
const int NORMAL_NUM_COLS = (int)ceil(sqrt([items count] / (COL_WIDTH / (ROW_HEIGHT))));
const int NUM_COLS = (MIN_NUM_COLS > NORMAL_NUM_COLS ? MIN_NUM_COLS : NORMAL_NUM_COLS);
const int NUM_ROWS = (int)ceil([items count] / (float)NUM_COLS);
const int NUM_OVERFLOW_ROWS = [items count] % NUM_ROWS;
const int titleWidth = titleLabel.frame.size.width;
const int defaultWidth = (NUM_COLS * (COL_WIDTH + COL_PADDING)) - COL_PADDING;
const int pixelWidth = (2 * OUTER_PADDING) + (titleWidth > defaultWidth ? titleWidth : defaultWidth);
const int pixelHeight = (2 * OUTER_PADDING) + (TITLE_FONT_SIZE + PADDING) + (NUM_ROWS * (ROW_HEIGHT + ROW_PADDING)) - ROW_PADDING;
const int nbytes = 4 * pixelHeight * pixelWidth;
byte* data = (byte*)malloc(sizeof(byte) * nbytes);
memset(data, 255, nbytes);
CGContextRef context = CGBitmapContextCreate(data, pixelWidth, pixelHeight, 8, 4 * pixelWidth, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast);
// --------------------------------------------------
// create a view heirarchy and then draw to our context
UIView* mainView = [[UIView alloc] init];
[mainView addSubview:titleLabel];
// setup all the views
int keyIndex = 0;
CGFloat x = OUTER_PADDING;
CGFloat starty = titleLabel.frame.origin.y + titleLabel.frame.size.height + PADDING;
for (int col = 0; col < NUM_COLS; col++)
{
int nrows = (col == NUM_COLS + 1 ? NUM_OVERFLOW_ROWS : NUM_ROWS);
CGFloat y = starty;
for (int row = 0; (row < nrows) && (keyIndex < [items count]); row++)
{
CGFloat tempx = x;
MyItem* item = [items objectAtIndex:keyIndex];
UIImageView* imageview1 = [[UIImageView alloc] initWithImage:item.image1];
imageview1.frame = CGRectMake(tempx, y, BOX_SIZE, BOX_SIZE);
[mainView addSubview:imageview1];
tempx += BOX_SIZE + PADDING;
UIImageView* imageview2 = [[UIImageView alloc] initWithImage:item.imageview2];
imageview2.frame = CGRectMake(tempx, y, BOX_SIZE, BOX_SIZE);
[mainView addSubview:imageview2];
tempx += BOX_SIZE + PADDING;
UILabel* label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:ITEM_FONT_SIZE];
label.text = item.name;
[label sizeToFit];
label.center = CGPointMake(tempx + (label.frame.size.width / 2), imageview2.center.y);
[mainView addSubview:label];
y += ROW_HEIGHT + ROW_PADDING;
keyIndex++;
}
x += COL_WIDTH + COL_PADDING;
}
// --------------------------------------------------
// draw everything to actually generate the image
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, pixelHeight));
[mainView.layer renderInContext:context];
CGImageRef cgimage = CGBitmapContextCreateImage(context);
myCoolImage = [UIImage imageWithCGImage:cgimage];
CGImageRelease(cgimage);
CGContextRelease(context);
free(data);
答案 0 :(得分:1)
正如我们在评论中所建立的那样,你所做的是非法和缓慢的。
安排和调整UILabel和UIImageView对象的速度很慢,并且调用
CALayer renderInContext
真的慢。
并不是你画的方式。
你所做的一切都在实际绘图世界(Quartz 2D)中具有类似性,如果你这样做,不仅在背景中是合法的,它可能不会甚至< em>需要在后台,因为它会快得多。所以:
使用UILabel的每个地方,都可以使用NSAttributedString draw...
命令实现完全相同的效果。
使用UIImageView的每个地方,都可以使用UIImage draw...
命令实现完全相同的效果。
我们任何做过任何大量绘图的人都学会了使用实际绘图代码创建您正在制作的类型的结构化布局,现在您也有机会学会这样做。