Круговой индикатор прогресса iPhone. CGContextRef. Ничья по требованию - PullRequest
1 голос
/ 25 февраля 2010

Я хочу нарисовать изображение, которое фактически было бы круглым индикатором прогресса на кнопке UIB. Поскольку предполагается, что изображение представляет ход выполнения задачи, я не думаю, что мне следует обрабатывать код рисования в методе drawrect вида.

У меня есть поток, который выполняет некоторые задачи. После каждой задачи он вызывает метод в главном потоке. Вызываемый метод должен обновить изображение на кнопке.

В методе обновления кнопки я создаю CGContextRef с помощью CGBitmapContextCreate. Затем я использую рамку кнопки, чтобы создать CGRect. Затем я пытаюсь использовать созданный мной контекст. Наконец я установил NeedsDisplay и очистил.

Но ничего из этого не находится внутри метода drawrect.

Я хотел бы знать, использовал ли кто-либо CGContext для рисования в / в представлении по требованию в представлении во время отображения представления.

Я хотел бы получить некоторые идеи относительно подхода к этому.

Вот инкапсулированная версия того, что я делаю сейчас: CGContextRef xContext = nil; CGColorSpaceRef xColorSpace; CGRect xRect; void * xBitmapData; int iBMPByteCount; int iBMPBytesPerRow; float fBMPWidth = 20.0f; float fBMPHeight = 20.0f; ФПИ плавания = 3,14159; float 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];

Вышеприведенное немного шатко, потому что я пробовал разные настройки. Тем не менее, я думаю, что это говорит о том, что я пытаюсь сделать.

Ответы [ 2 ]

0 голосов
/ 02 марта 2010

Это действительно беспокоило меня, поэтому после того, как я столкнулся с серией глупых, но необходимых вопросов, касающихся проекта, для которого я хочу эту функциональность, я поиграл с этим. Конечный результат заключается в том, что теперь я могу произвольно нарисовать для кнопки дугу, отображающую ход выполнения определенной фоновой задачи.

Цель состояла в том, чтобы нарисовать что-то вроде маленького индикатора в правом нижнем углу окон 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;
}


0 голосов
/ 26 февраля 2010

Альтернативный подход:

Вы можете создать серию изображений, представляющих обновления прогресса, а затем заменить свойство currentImage UIButton на setImage: forState: метод на каждом этапе процесса. Это не требует рисования в существующем виде, и этот подход хорошо сработал для меня, чтобы показать простую «анимацию» изображений (кнопок или других).

Подойдет ли вам этот подход? Если нет, то почему?

Bart

...