UIView坐标系对圆形UIProgressView的影响
Posted
技术标签:
【中文标题】UIView坐标系对圆形UIProgressView的影响【英文标题】:Effect of UIView coordinate system on circular version of UIProgressView 【发布时间】:2014-06-04 20:42:54 【问题描述】:我需要一个派生自UIView
的类,它通过填充越来越大的圆扇区来表示进度(随着进度从0 变为1,它的角度应该增加2 PI)。这就像UIProgressView
的循环版本。
下面的代码还不能很好地完成这项工作:它会生成越来越多的小扇区,以闪烁的斑马图案堆叠。据推测,代码将其坐标与 UIView flipping 其坐标系、CGContextAddArc
的(显然)“顺时针”的反直觉定义等混淆了。
不当行为的原因是什么?如何解决?
#define PERCENTAGE_TO_RADIANS(PERCENTAGE) ((PERCENTAGE) * 2 * M_PI - M_PI_2)
- (void)drawRect:(CGRect)rect
NSAssert(!self.opaque, nil);
NSAssert(!self.clearsContextBeforeDrawing, nil);
CGSize size = self.bounds.size;
CGPoint center = [self.superview convertPoint:self.center toView:self];
NSAssert(size.width == size.height, nil);
NSAssert((self.progress >= 0) && (self.progress <= 1), nil);
NSAssert(PERCENTAGE_TO_RADIANS(0 ) == -M_PI_2 , nil); // 0% progress corresponds to north
NSAssert(PERCENTAGE_TO_RADIANS(0.25) == 0 , nil); // 25% progress corresponds to east
NSAssert(PERCENTAGE_TO_RADIANS(0.5 ) == M_PI_2 , nil); // 50% progress corresponds to south
NSAssert(PERCENTAGE_TO_RADIANS(0.75) == M_PI , nil); // 75% progress corresponds to west
NSAssert(PERCENTAGE_TO_RADIANS(1 ) == 3 * M_PI_2, nil); // 100% progress corresponds to north
CGFloat x = center.x;
CGFloat y = center.y;
CGFloat radius = size.width / 2.0;
CGFloat startAngle = self.lastAngle;
CGFloat endAngle = PERCENTAGE_TO_RADIANS(self.progress);
int clockwise = 0;
if (self.progress > 0)
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetFillColorWithColor(context, self.sectorTintColor.CGColor);
CGContextSetLineWidth(context, 1);
NSAssert(startAngle <= endAngle, nil);
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, radius, startAngle, endAngle, clockwise);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
self.lastAngle = endAngle;
【问题讨论】:
【参考方案1】:您需要知道的最重要的事情是正 Y 向下,这使得角度和顺时针方向不直观。您有两个选择,要么直接使用它,要么在绘制任何东西之前翻转 Y 轴。在下面的示例代码中,我采用了后一种方法。
drawRect
方法不是累积的。换句话说,您需要在每次调用drawRect
时绘制整个扇区。因此没有理由跟踪起始角度,您应该始终从角度 PI/2 开始并绘制整个扇区。
无需保存和恢复图形状态。每次调用 drawRect
时都会创建新的图形上下文。
UIColor 类具有 set
、setFill
和 setStroke
方法,允许您在不使用 CGColors 的情况下更改填充和描边颜色。
考虑到所有这些,假设self.progress
是介于 0.0 和 1.0 之间的数字,代码如下所示
- (void)drawRect:(CGRect)rect
if ( self.progress <= 0 || self.progress > 1.0 )
return;
CGFloat x = self.bounds.size.width / 2.0;
CGFloat y = self.bounds.size.height / 2.0;
CGFloat r = (x <= y) ? x : y;
CGFloat angle = M_PI_2 - self.progress * 2.0 * M_PI;
CGContextRef context = UIGraphicsGetCurrentContext();
// fix the y axis, so that positive y is up
CGContextScaleCTM( context, 1.0, -1.0 );
CGContextTranslateCTM( context, 0, -self.bounds.size.height );
// draw the pie shape
[self.sectorTintColor setFill];
CGContextMoveToPoint( context, x, y );
CGContextAddArc( context, x, y, r, M_PI_2, angle, YES );
CGContextClosePath( context );
CGContextFillPath( context );
【讨论】:
非常好,谢谢。你能再澄清一点吗?如果视图的clearsContextBeforeDrawing
设置为NO
,我认为drawRect:
是累积的。但是您的建议是,如果是这样,则不会清除背景视图的上下文,但是无论如何都会在调用drawRect:
之前清除视图自己的视图(在rect
内),对吗?
This 对clearsContextBeforeDrawing
很有帮助:“您无法通过执行以下操作来防止内容被擦除:[self setClearsContextBeforeDrawing: NO];
这只是对图形引擎的提示,没有意义让它为您预先清除视图,因为无论如何您都可能需要重新绘制整个区域。它可能会阻止您的视图被自动擦除,但您不能依赖它。”
啊,是的,这很好地解释了它。现在我们只需要preservesContextFromPreviousDrawing
属性:)以上是关于UIView坐标系对圆形UIProgressView的影响的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Swift 在 UIView 中将圆形进度条居中?