在 UIView 中绘制三角形切口以用于选择指示
我正在创建一个用作我的应用程序的类别选择器的视图。我希望它有一个切口三角形作为选择指示,如下图所示:
我是不知道如何绘制三角形,使其成为一个切口,露出下面的主视图。下面的主视图很可能有一个自定义的、可能不重复的(我还没有决定)图像作为背景。此外,我希望当选择更改时三角形能够动画到新位置,这使事情变得更加复杂。
我意识到子视图会使动画更容易,但会使绘图复杂化;直接绘制可能会使动画变得更困难。而且我对Quartz不太熟悉,所以我不知道如何走直接绘制路线。
提前致谢!
更新:我看过 Matt Gallagher 在 绘制带孔的形状,但它并没有真正回答我的问题。有没有办法让我“看到”我的形状中特定路径下方的内容并复制它? ......然后支持动画?
更新2:我通过简单地绘制一条附加路径完成了部分工作。结果如下: http://dl.dropbox.com/u/7828009/Category%20Selector。 mov
代码:
CGRect cellRect = [self rectForCategoryNumber:(selectedCategoryIndex + 1)];
[[UIColor scrollViewTexturedBackgroundColor] setFill];
CGContextMoveToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.15));
CGContextAddLineToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.65));
CGContextAddLineToPoint(currentContext, self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextClosePath(currentContext);
CGContextFillPath(currentContext);
[[UIColor darkGrayColor] setStroke];
CGContextSetLineCap(currentContext, kCGLineCapRound);
CGContextMoveToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.15));
CGContextAddLineToPoint(currentContext, self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextSetLineWidth(currentContext, 2.5);
CGContextStrokePath(currentContext);
[[UIColor lightGrayColor] setStroke];
CGContextMoveToPoint(currentContext,self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextAddLineToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.65));
CGContextSetLineWidth(currentContext, 1.0);
CGContextStrokePath(currentContext);
显然,在这种情况下它才有效,因为我在两种情况下都使用相同的填充颜色;如果可能的话,我想消除这种依赖性。另外,我当然想为该三角形的位置设置动画。
更新3:我尝试做的动画:
static CALayer *previousLayer = nil;
static CGMutablePathRef previousPath = nil;
// … Get context, etc.
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = shapePath;
[shapeLayer setFillColor:[[UIColor redColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setBounds:self.bounds];
[shapeLayer setAnchorPoint:self.bounds.origin];
[shapeLayer setPosition:self.bounds.origin];
if (previousPath) { // Animate change
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"changePath"];
animation.duration = 0.5;
animation.fromValue = (id)previousPath;
animation.toValue = (id)shapePath;
[shapeLayer addAnimation:animation forKey:@"animatePath"];
previousPath = shapePath;
}
if (previousLayer)
[previousLayer removeFromSuperlayer];
previousLayer = shapeLayer;
[self.layer addSublayer:shapeLayer];
I'm creating a view that serves as a category selector for my app. I'd like it to have a cutout triangle as the selection indication, as in this image:
I'm not sure how to draw the triangle so that it is a cutout, revealing the main view underneath. The main view underneath will most likely have a custom, possibly non-repeating (I haven't decided yet) image as its background. In addition, I would like the triangle to animate to a new location when the selection changes, which further complicates things a bit.
I realize a subview would make the animation easier, but would complicate the drawing; direct drawing would probably make the animation a bit harder. And I'm not too familiar with Quartz, so I'm not sure how to go with the direct drawing route.
Thanks in advance!
Update: I've looked at Matt Gallagher's post on drawing shapes with holes, but it doesn't really answer my question. Is there a way for me to "see" what's underneath a certain path within my shape, and copy that? …And then support animating it?
Update 2: I've done a partial job by simply drawing an additional path. The result looks like this:
http://dl.dropbox.com/u/7828009/Category%20Selector.mov
The code:
CGRect cellRect = [self rectForCategoryNumber:(selectedCategoryIndex + 1)];
[[UIColor scrollViewTexturedBackgroundColor] setFill];
CGContextMoveToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.15));
CGContextAddLineToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.65));
CGContextAddLineToPoint(currentContext, self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextClosePath(currentContext);
CGContextFillPath(currentContext);
[[UIColor darkGrayColor] setStroke];
CGContextSetLineCap(currentContext, kCGLineCapRound);
CGContextMoveToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.15));
CGContextAddLineToPoint(currentContext, self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextSetLineWidth(currentContext, 2.5);
CGContextStrokePath(currentContext);
[[UIColor lightGrayColor] setStroke];
CGContextMoveToPoint(currentContext,self.frame.size.width * 0.8, (cellRect.origin.y + cellRect.size.height * 0.4));
CGContextAddLineToPoint(currentContext, self.frame.size.width, (cellRect.origin.y + cellRect.size.height * 0.65));
CGContextSetLineWidth(currentContext, 1.0);
CGContextStrokePath(currentContext);
Obviously, in this case it only works because I'm using the same fill color in both cases; I'd like to eliminate this dependency if possible. Also, of course I'd like to animate the position of that triangle.
Update 3: What I tried to do to animate:
static CALayer *previousLayer = nil;
static CGMutablePathRef previousPath = nil;
// … Get context, etc.
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = shapePath;
[shapeLayer setFillColor:[[UIColor redColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setBounds:self.bounds];
[shapeLayer setAnchorPoint:self.bounds.origin];
[shapeLayer setPosition:self.bounds.origin];
if (previousPath) { // Animate change
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"changePath"];
animation.duration = 0.5;
animation.fromValue = (id)previousPath;
animation.toValue = (id)shapePath;
[shapeLayer addAnimation:animation forKey:@"animatePath"];
previousPath = shapePath;
}
if (previousLayer)
[previousLayer removeFromSuperlayer];
previousLayer = shapeLayer;
[self.layer addSublayer:shapeLayer];
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您看过 CAShapeLayer 吗?您可以为选择窗格创建一条路径,该路径定义整个轮廓,包括排除需要屏蔽的每个状态的三角形。如果您希望在图像中显示轮廓,则可以通过设置
lineWidth
和StrokeColor
属性来描边图层。那应该能够给你你需要的东西。CAShapeLayer
中的path
属性是可动画的,这意味着您所要做的就是设置path
属性,它就会动画(假设您的图层是视图的子层而不是根层)。使用代码更新
此代码:
显示如下:
您可以下载示例项目在这里:
https://dzwonsemrish7.cloudfront.net/items/180s2a200N2i3b0y1g37/ArrowIndicator.zip
Have you looked at the CAShapeLayer? You can create a path for your selection pane that defines the whole outline including the exclusion of the triangle for each state you need to mask. You can then stroke the layer if you want the outline you're showing in your image by setting the
lineWidth
andstrokeColor
properties. That should be able to give you what you need. Thepath
property in theCAShapeLayer
is animatable which means that all you have to do is set thepath
property and it will animate (assuming your layers are sublayers of the view and not the root layer).Update With Code
This code:
results in this display:
And you can download the sample project here:
https://dzwonsemrish7.cloudfront.net/items/180s2a200N2i3b0y1g37/ArrowIndicator.zip