如何获取 UIBezierPath 的反向路径

Posted

技术标签:

【中文标题】如何获取 UIBezierPath 的反向路径【英文标题】:How to Get the reverse path of a UIBezierPath 【发布时间】:2012-06-25 14:33:51 【问题描述】:
self.myPath=[UIBezierPath bezierPathWithArcCenter:center 
                                           radius:200 
                                       startAngle:0 
                                         endAngle:180 
                                        clockwise:YES];

(通过一些网络搜索,我就可以开始运行了)。

我有这条路。现在我想填充这条路径的反面,所以留下这部分并填充其他所有内容。我怎样才能完成编码?我没有太多这方面的信息。

问题

之前使用 Cemal Answer 后显示的区域只显示了一个带有红色笔划的圆圈。

编辑

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        self.backgroundColor = [UIColor whiteColor];
        self.punchedOutPath =  
         [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 400, 400)];
        self.fillColor = [UIColor redColor];
        self.alpha = 0.8;
    
    return self;


- (void)drawRect:(CGRect)rect

    [[self fillColor] set];
    UIRectFill(rect);

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);

    [[self punchedOutPath] fill];

    CGContextSetBlendMode(ctx, kCGBlendModeNormal);

【问题讨论】:

它是一个完整的圆形还是一个开放的圆形?另外,你能提供一张你所期待的图片吗?它不一定要漂亮,但它会极大地帮助人们了解您正在尝试做什么,以便他们可以帮助您。 done..一直在努力提供它,因为我做了赏金 【参考方案1】:

使用bezierPathByReversingPath。来自文档(仅限 ios 6.0+):

使用与当前路径相反的内容创建并返回一个新的贝塞尔路径对象。

所以要扭转你的道路,你只需:

UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:center
                                                     radius:200
                                                 startAngle:0
                                                   endAngle:180
                                                  clockwise:YES];
self.myPath = [aPath bezierPathByReversingPath];

【讨论】:

或者只是 reversing() 在 swift 中。【参考方案2】:

这是一个完全不需要反转路径的替代方案。

您实际上想要“剪掉”视图的一部分:

假设您希望白色区域为[UIColor whiteColor],alpha 为 75%。以下是您快速完成的方法:

您创建了一个新的UIView 子类。

这个视图有两个属性:

@property (retain) UIColor *fillColor;
@property (retain) UIBezierPath *punchedOutPath;

您覆盖其-drawRect: 方法来执行此操作:

- (void)drawRect:(CGRect)rect 
  [[self fillColor] set];
  UIRectFill(rect);

  CGContextRef ctx = UIGraphicsGetCurrentContext();
  CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);

  [[self punchedOutPath] fill];

  CGContextSetBlendMode(ctx, kCGBlendModeNormal);

这里有一个警告:视图的fillColor 必须包含 alpha 组件。所以在你的情况下,你希望它只是[UIColor whiteColor]。然后,您可以通过调用 [myView setAlpha:0.75] 自己应用 alpha 位。

这里发生了什么:这是使用一种名为“Destination Out”的混合模式。在数学上它被定义为R = D*(1 - Sa),但通俗地说,它的意思是“目标图像不透明但源图像透明的目标图像,并且在其他地方透明。”

所以它将使用目的地(即上下文中已经存在的内容),只要 new 东西是透明的(即在贝塞尔路径之外),然后贝塞尔路径将是不透明的,那东西会变得透明。但是,目标内容必须已经是不透明的。如果它不是不透明的,则混合不会达到您想要的效果。这就是为什么你必须提供一个不透明的UIColor,然后直接对视图进行任何你想要的透明度。


我自己运行了这个,在这些情况下:

窗口有一个[UIColor greenColor] 背景 fillColor 是白色的 punchedOutPath 是一个椭圆形,距离视图边缘 10 个点。 视图有一个alpha0.75

通过上面的代码,我明白了:

内部是纯绿色,外面有半透明的覆盖层。


更新

如果您的覆盖物是图像,那么您需要创建一个新图像。但原理是一样的:

UIImage* ImageByPunchingPathOutOfImage(UIImage *image, UIBezierPath *path) 
  UIGraphicsBeginImageContextWithOptions([image size], YES, [image scale]);

  [image drawAtPoint:CGPointZero];

  CGContextRef ctx = UIGraphicsGetCurrentContext();
  CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
  [path fill];


  UIImage *final = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return final;

然后您将获取此函数的结果并将其放入UIImageView

【讨论】:

你能把你的项目提供给我吗??请..!这会更好,因为我会有一个工作代码..否则我将不得不与你澄清问题..这可能会让你很烦!. @shubhank 答案中的代码描述了您需要的一切 嗨..你能检查我对问题的新编辑吗..我尝试了你的方法..我将视图的背景设置为白色..但是椭圆形是黑色的..你能帮我解决一下吗……应该是白色的……谢谢 @Shubhank 这种方法不适用于backgroundColorUIView。您看到的黑色是视图打孔视图(可能是窗口)之后的颜色。 我打算将图像添加到视图中并在其上具有上述功能...如果它不能与UIView 一起使用...我将如何裁剪图像.. ?请给个建议..您帮了很多忙,如果您能帮我解决这个问题,那就太好了..非常感谢【参考方案3】:

您可以将其放入视图控制器中的单屏应用程序中:它将创建一个黄色背景视图和一个蓝色层,上面有一个被蒙版切掉的椭圆区域。

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // create a yellow background
    UIView *bg = [[UIView alloc] initWithFrame:self.view.bounds];
    bg.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:bg];    

    // create the mask that will be applied to the layer on top of the 
    // yellow background
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.fillRule = kCAFillRuleEvenOdd;
    maskLayer.frame = self.view.frame;

    // create the paths that define the mask
    UIBezierPath *maskLayerPath = [UIBezierPath bezierPath];
    [maskLayerPath appendPath:[UIBezierPath bezierPathWithRect:CGRectInset(self.view.bounds, 20, 20)]];
    // here you can play around with paths :)
//    [maskLayerPath appendPath:[UIBezierPath bezierPathWithOvalInRect:(CGRect)80, 80, 140, 190]];
    [maskLayerPath appendPath:[UIBezierPath bezierPathWithOvalInRect:(CGRect)100, 100, 100, 150]];
    maskLayer.path = maskLayerPath.CGPath;

    // create the layer on top of the yellow background
    CALayer *imageLayer = [CALayer layer];
    imageLayer.frame = self.view.layer.bounds;
    imageLayer.backgroundColor = [[UIColor blueColor] CGColor];

    // apply the mask to the layer
    imageLayer.mask = maskLayer;
    [self.view.layer addSublayer:imageLayer];


这也可以回答这个问题:UIBezierPath Subtract Path

【讨论】:

您能解释一下您在做什么以及为什么吗?让提问的人和寻找答案的其他人明白你的答案是什么。 奇怪,为什么 [UIBezierPath bezierPathWithOvalInRect:] 不画一个椭圆?您是否将该代码准确地复制并粘贴到了一个空的单屏项目中?【参考方案4】:

我有两个解决方案给你。

    CALayer 上绘制这条路径。并将 CALayer 用作实际 CALayer 的遮罩层。

    在添加圆弧之前用你的框架大小绘制一个矩形。

    UIBezierPath *path = [UIBezierPath bezierPathWithRect:view.frame];
    [path addArcWithCenter:center 
                    radius:200 
                startAngle:0 
                  endAngle:2*M_PI 
                 clockwise:YES];
    

我会使用第二种解决方案。 :)

【讨论】:

第二种方案更好,只是弧线需要逆时针添加,所以它所划定的区域被the nonzero winding number rule排除(矩形是顺时针方向添加的)。 这对我不起作用我得到了一个非常奇怪的结果..我在问题中添加了它..请看..谢谢 endAngle 必须是弧度(不是度数)。你想要一个 360° 的圆,所以是 2pi 或 6.283。你可以写成2*M_PI

以上是关于如何获取 UIBezierPath 的反向路径的主要内容,如果未能解决你的问题,请参考以下文章

如何以给定的顺序动画/填充 UIBezierPath?

UIBezierPath 路径形状可点击和可拖动

为 UIBezierPath 着色

Objective-C 如何避免在具有 UIBezierPath 笔触颜色的相同颜色图像上绘图

如何确定位置是不是在 UIbezierPath 内?

使用 Pan Gesture 将 UIView 拖到 UIBezierPath 上