在 UIView 中为选择指示绘制三角形切口

Posted

技术标签:

【中文标题】在 UIView 中为选择指示绘制三角形切口【英文标题】:Draw triangle cutout for selection indication in UIView 【发布时间】:2011-10-02 12:39:02 【问题描述】:

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

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

我意识到子视图会使动画更容易,但会使绘图复杂化;直接绘图可能会使动画更难一些。而且我对Quartz不太熟悉,所以不知道怎么走直接画法。

提前致谢!

更新:我查看了 Matt Gallagher 在 drawing shapes with holes 上的帖子,但它并没有真正回答我的问题。有没有办法让我“看到”我的形状中某个路径下方的内容并复制它? …然后支持动画?

更新 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];

【问题讨论】:

【参考方案1】:

你看过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

【讨论】:

我可以创建一个只占用部分视图的 CAShapeLayer 吗?当我执行[self.layer addSublayer:shapeLayer] 时,整个视图都会变黑。我希望能够将图层几乎作为一个独立的“视图”使用,不会干扰视图的其余部分。 我不确定你在做什么。您可以将填充颜色设置为您想要的任何颜色。您还可以通过在要遮罩的图层上调用 setMask 将形状图层用作遮罩,并将其传递给您的形状图层。请记住,面具允许通过任何“被掩盖”的东西。我一直觉得它倒退了,但这就是它的工作方式。 您能否提供一些可以复制上述示例的示例代码?我刚刚为这个项目进入 CALayers,非常感谢您的帮助。 我用代码更新了我的答案。如果您需要更多说明,请告诉我。 好的,谢谢;它正确绘制(对此投票)。但我无法改变动画(用我试图做的事情更新了我的问题)。此外,它还涵盖了我已经在绘制的内容以及 UILabel — 有没有办法将图层放在现有内容的后面?

以上是关于在 UIView 中为选择指示绘制三角形切口的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIView 中绘制具有特定角度的开放式三角形?

访问在 UIView 上绘制的 UIBezierPath

你可以在我的情节中为三角形的尖端着色吗?

如何在 UIView 上制作一个三角形

如何使用quartz2d/coregraphics绘制圆角三角形

聊天小三角