在 UIView 中绘制三角形切口以用于选择指示

发布于 2024-12-07 22:27:49 字数 3174 浏览 1 评论 0原文

我正在创建一个用作我的应用程序的类别选择器的视图。我希望它有一个切口三角形作为选择指示,如下图所示:

Custom UI design

我是不知道如何绘制三角形,使其成为一个切口,露出下面的主视图。下面的主视图很可能有一个自定义的、可能不重复的(我还没有决定)图像作为背景。此外,我希望当选择更改时三角形能够动画到新位置,这使事情变得更加复杂。

我意识到子视图会使动画更容易,但会使绘图复杂化;直接绘制可能会使动画变得更困难。而且我对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:

Custom UI design

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

遇到 2024-12-14 22:27:49

您看过 CAShapeLayer 吗?您可以为选择窗格创建一条路径,该路径定义整个轮廓,包括排除需要屏蔽的每个状态的三角形。如果您希望在图像中显示轮廓,则可以通过设置 lineWidthStrokeColor 属性来描边图层。那应该能够给你你需要的东西。 CAShapeLayer 中的 path 属性是可动画的,这意味着您所要做的就是设置 path 属性,它就会动画(假设您的图层是视图的子层而不是根层)。

使用代码更新

此代码:

- (void)viewDidLoad
{
  [super viewDidLoad];

  [[self view] setBackgroundColor:[UIColor blueColor]];
  
  CGMutablePathRef path = CGPathCreateMutable();
  CGPathMoveToPoint(path,NULL,0.0,0.0);
  CGPathAddLineToPoint(path, NULL, 160.0f, 0.0f);  
  CGPathAddLineToPoint(path, NULL, 160.0f, 100.0f);
  CGPathAddLineToPoint(path, NULL, 110.0f, 150.0f);
  CGPathAddLineToPoint(path, NULL, 160.0f, 200.0f);
  CGPathAddLineToPoint(path, NULL, 160.0f, 480.0f);
  CGPathAddLineToPoint(path, NULL, 0.0f, 480.0f);
  CGPathAddLineToPoint(path, NULL, 0.0f, 0.0f);
  

  CAShapeLayer *shapeLayer = [CAShapeLayer layer];
  [shapeLayer setPath:path];
  [shapeLayer setFillColor:[[UIColor redColor] CGColor]];
  [shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
  [shapeLayer setBounds:CGRectMake(0.0f, 0.0f, 160.0f, 480)];
  [shapeLayer setAnchorPoint:CGPointMake(0.0f, 0.0f)];
  [shapeLayer setPosition:CGPointMake(0.0f, 0.0f)];
  [[[self view] layer] addSublayer:shapeLayer];

  CGPathRelease(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 and strokeColor properties. That should be able to give you what you need. The path property in the CAShapeLayer is animatable which means that all you have to do is set the path property and it will animate (assuming your layers are sublayers of the view and not the root layer).

Update With Code

This code:

- (void)viewDidLoad
{
  [super viewDidLoad];

  [[self view] setBackgroundColor:[UIColor blueColor]];
  
  CGMutablePathRef path = CGPathCreateMutable();
  CGPathMoveToPoint(path,NULL,0.0,0.0);
  CGPathAddLineToPoint(path, NULL, 160.0f, 0.0f);  
  CGPathAddLineToPoint(path, NULL, 160.0f, 100.0f);
  CGPathAddLineToPoint(path, NULL, 110.0f, 150.0f);
  CGPathAddLineToPoint(path, NULL, 160.0f, 200.0f);
  CGPathAddLineToPoint(path, NULL, 160.0f, 480.0f);
  CGPathAddLineToPoint(path, NULL, 0.0f, 480.0f);
  CGPathAddLineToPoint(path, NULL, 0.0f, 0.0f);
  

  CAShapeLayer *shapeLayer = [CAShapeLayer layer];
  [shapeLayer setPath:path];
  [shapeLayer setFillColor:[[UIColor redColor] CGColor]];
  [shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
  [shapeLayer setBounds:CGRectMake(0.0f, 0.0f, 160.0f, 480)];
  [shapeLayer setAnchorPoint:CGPointMake(0.0f, 0.0f)];
  [shapeLayer setPosition:CGPointMake(0.0f, 0.0f)];
  [[[self view] layer] addSublayer:shapeLayer];

  CGPathRelease(path);
  
}

results in this display:

enter image description here

And you can download the sample project here:

https://dzwonsemrish7.cloudfront.net/items/180s2a200N2i3b0y1g37/ArrowIndicator.zip

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文