1

我想在 UIButton 上绘制一个有效地作为圆形进度指示器的图像。因为图像应该代表任务的进度,所以我认为我不应该在视图的 drawrect 方法中处理绘图代码。

我有一个正在执行某些任务的线程。在每个任务之后,它调用主线程上的一个方法。调用的方法应该更新按钮上的图像。

在按钮更新方法中,我使用 CGBitmapContextCreate 创建了一个 CGContextRef。然后我使用按钮的框架来创建一个 CGRect。然后我尝试使用我创建的上下文进行绘制。最后我设置 NeedsDisplay 并清理。

但是这些都不在视图的 drawrect 方法中。

我想知道是否有人在显示视图时使用 CGContext 在视图中按需绘制/在视图中绘制。

我想得到一些关于这样做的方法的想法。

这是我现在正在做的一个封装版本:CGContextRef xContext = nil; CGColorSpaceRef xColorSpace; CGRect xRect; 无效* xBitmapData;int iBMPByteCount; int iBMPBytesPerRow;浮动 fBMPWidth = 20.0f; 浮动 fBMPHeight = 20.0f; 浮动 fPI = 3.14159; 浮动 fRadius = 25.0f;

iBMPBytesPerRow = (fBMPWidth * 4);
iBMPByteCount = (iBMPBytesPerRow * fBMPHeight);
xColorSpace = CGColorSpaceCreateDeviceRGB();
xBitmapData = malloc(iBMPByteCount);
xContext = CGBitmapContextCreate(xBitmapData, fBMPWidth, fBMPHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(xColorSpace);
UIGraphicsPushContext(xContext);
xRect = CGRectMake(30.0f, 400.0f, 50.0f, 50.0f);

float fWidth = xRect.size.width;
float fHeight = xRect.size.height;

CGContextClearRect(xContext, xRect);
CGContextSetRGBStrokeColor(xContext, 0.5f, 0.6f, 0.7f, 1.0f);
CGContextSetLineWidth(xContext, 1.0f);

float fArcBegin = 45.0f * fPI / 180.0f;
float fArcEnd = 90.0f * fPI / 180.0f;

CGContextSetFillColor(xContext, CGColorGetComponents( [[UIColor greenColor] CGColor]));
CGContextMoveToPoint(xContext, fWidth, fHeight);
CGContextAddArc(xContext, fWidth, fHeight, fRadius, fArcBegin, fArcEnd, 0);

CGContextClosePath(xContext); 
CGContextFillPath(xContext); 

UIGraphicsPopContext;
CGContextRelease(xContext);

[self.view setNeedsDisplay];

// [self.view setNeedsDisplayInRect: xRect];

上面有点不稳定,因为我尝试了不同的调整。但是,我认为它传达了我想要做的事情。

4

2 回答 2

0

替代方法:

您可以创建一系列表示进度更新的图像,然后在流程的每个步骤中将 UIButton currentImage 属性替换为 setImage:forState: 方法。这不需要在现有视图中绘图,并且这种方法对我来说非常有效,可以显示图像(按钮或其他)的简单“动画”。

这种方法对你有用吗?如果不是,为什么不呢?

巴特

于 2010-02-26T16:36:03.303 回答
0

这真的让我很烦,所以在处理了一系列关于我想要这个功能的项目的愚蠢但必要的问题之后,我玩弄了它。最终结果是,我现在可以任意绘制一条弧线,表示特定后台任务的进度到按钮。

目标是在清理或编译项目时在 XCode 窗口的右下角绘制类似小指示器的东西。

我创建了一个函数,该函数将绘制和填充弧并将其作为 UIImage 返回。

工作线程使用当前值和按钮标识符调用方法 (PerformSelectorOnMainThread)。在被调用的方法中,我调用了填充百分比等的弧形图像函数。

示例调用:

oImg = [self ArcImageCreate:100.0f fWidth:100.0f 
    fPercentFilled: 0.45f fAngleStart: 0.0f xFillColor:[UIColor blueColor]];

然后设置按钮的背景图片:

[oBtn setBackgroundImage: oImg forState: UIControlStateNormal];

这是功能:
它还没有完成,但它工作得很好,足以说明我是如何做到这一点的。



/**
ArcImageCreate
@ingroup    UngroupedFunctions
@brief  Create a filled or unfilled solid arc and return it as a UIImage.

        Allows for dynamic / arbitrary update of an object that allows a UIImage to be drawn on it. \
    This can be used for some sort of pie chart or progress indicator by Image Flipping.

@param  fHeight     The height of the created UIImage.
@param  fWidth      The width of the created UIImage.
@param  fPercentFilled  A percentage of the circle to be filled by the arc. 0.0 to 1.0.
@param  AngleStart  The angle where the arc should start. 0 to 360. Clock Reference.
@param  xFillColor  The color of the filled area.

@return Pointer to a UIImage.

@todo [] Validate object creation at each step.

@todo [] Convert PercentFilled (0.0 to 1.0) to appropriate radian(?) (-3.15 to +3.15)

@todo [] Background Image Support. Allow for the arc to be drawn on top of an image \
    and the whole thing returned.

@todo [] Background Image Reduction. Background images will have to be resized to fit the specfied size. \
    Do not want to return a 65KB object because the background is 60K or whatever.

@todo [] UIColor RGBA Components. Determine a decent method of extracting RGVA values \
    from a UIColor*. Check out arstechnica.com/apple/guides/2009/02/iphone-development-accessing-uicolor-components.ars \
    for an idea.

*/
- (UIImage*) ArcImageCreate: (float)fHeight fWidth:(float)fWidth fPercentFilled:(float)fPercentFilled fAngleStart:(float)fAngleStart xFillColor:(UIColor*)xFillColor
{
    UIImage* fnRez = nil;
    float fArcBegin = 0.0f;
    float fArcEnd = 0.0f;
    float fArcPercent = 0.0f;
    UIColor* xArcColor = nil;
    float fArcImageWidth = 0.0f;
    float fArcImageHeight = 0.0f;
    CGRect xArcImageRect;

    CGContextRef xContext = nil;
    CGColorSpaceRef xColorSpace;
    void* xBitmapData;
    int iBMPByteCount;
    int iBMPBytesPerRow;
    float fPI = 3.14159;
    float fRadius = 25.0f;

// @todo Force default of 100x100 px if out of bounds. \
//  Check max image dimensions for iPhone. \
//  If negative, flip values *if* values are 'reasonable'. \
//  Determine minimum useable pixel dimensions. 10x10 px is too small. Or is it?
    fArcImageWidth = fHeight;
    fArcImageHeight = fWidth;

    // Get the passed target percentage and clip it between 0.0 and 1.0
    fArcPercent = (fPercentFilled  1.0f) ? 1.0f : fPercentFilled;
    fArcPercent = (fArcPercent > 1.0f) ? 1.0f : fArcPercent;

    // Get the passed start angle and clip it between 0.0 to 360.0
    fArcBegin = (fAngleStart  359.0f) ? 0.0f : fAngleStart;
    fArcBegin = (fArcBegin > 359.0f) ? 0.0f : fArcBegin;

    fArcBegin = (fArcBegin * fPI) / 180.0f;
    fArcEnd = ((360.0f * fArcPercent) * fPI) / 180.0f;

    // 
    if (xFillColor == nil) {
        // random color
    } else {
        xArcColor = xFillColor;
    }

    // Calculate memory required for image.
    iBMPBytesPerRow = (fArcImageWidth * 4);
    iBMPByteCount = (iBMPBytesPerRow * fArcImageHeight);
    xBitmapData = malloc(iBMPByteCount);

    // Create a color space. Behavior changes at OSXv10.4. Do not rely on it for consistency across devices.
    xColorSpace = CGColorSpaceCreateDeviceRGB();

    // Set the system to draw. Behavior changes at OSXv10.3.
    //  Both of these work. Not sure which is better.
//  xContext = CGBitmapContextCreate(xBitmapData, fArcImageWidth, fArcImageHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);
    xContext = CGBitmapContextCreate(NULL, fArcImageWidth, fArcImageHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);

    // Let the system know the colorspace reference is no longer required.
    CGColorSpaceRelease(xColorSpace);

    // Set the created context as the current context.
//  UIGraphicsPushContext(xContext);

    // Define the image's box.
    xArcImageRect = CGRectMake(0.0f, 0.0f, fArcImageWidth, fArcImageHeight);

    // Clear the image's box.
//  CGContextClearRect(xContext, xRect);

    // Draw the ArcImage's background image.
//  CGContextDrawImage(xContext, xArcImageRect, [oBackgroundImage CGImage]);

    // Set Us Up The Transparent Drawing Area.
    CGContextBeginTransparencyLayer(xContext, nil);

    // Set the fill and stroke colors
// @todo [] Determine why SetFilColor does not. Use alternative method.
//  CGContextSetFillColor(xContext, CGColorGetComponents([xArcColor CGColor]));
//  CGContextSetFillColorWithColor(xContext, CGColorGetComponents([xArcColor CGColor]));
// Test Colors
    CGContextSetRGBFillColor(xContext, 0.3f, 0.4f, 0.5f, 1.0f);
    CGContextSetRGBStrokeColor(xContext, 0.5f, 0.6f, 0.7f, 1.0f);
    CGContextSetLineWidth(xContext, 1.0f);

// Something like this to reverse drawing?
//  CGContextTranslateCTM(xContext, TranslateXValue, TranslateYValue);
//  CGContextScaleCTM(xContext, -1.0f, 1.0f); or CGContextScaleCTM(xContext, 1.0f, -1.0f);

    // Test Vals
//  fArcBegin = 45.0f * fPI / 180.0f;   // 0.785397
//  fArcEnd = 90.0f * fPI / 180.0f; // 1.570795

    // Move to the start point and draw the arc.
    CGContextMoveToPoint(xContext, fArcImageWidth/2.0f, fArcImageHeight/2.0f);
    CGContextAddArc(xContext, fArcImageWidth/2.0f, fArcImageHeight/2.0f, fRadius, fArcBegin, fArcEnd, 0);


    // Ask the OS to close the arc (current point to starting point). 
    CGContextClosePath(xContext);

    // Fill 'er up. Implicit path closure.
    CGContextFillPath(xContext);
//  CGContextEOFillPath(context);

    // Close Transparency drawing area.
    CGContextEndTransparencyLayer(xContext);

    // Create an ImageReference and create a UIImage from it.
    CGImageRef xCGImageTemp = CGBitmapContextCreateImage(xContext);
    CGContextRelease(xContext);
    fnRez = [UIImage imageWithCGImage: xCGImageTemp];
    CGImageRelease(xCGImageTemp);

//  UIGraphicsPopContext;

    return fnRez;
}


于 2010-03-02T17:40:20.593 回答