How to Get the reverse path of a UIBezierPath

后端 未结 4 1424
花落未央
花落未央 2021-02-07 14:56
self.myPath=[UIBezierPath bezierPathWithArcCenter:center 
                                           radius:200 
                                       startAngle:0 
            


        
相关标签:
4条回答
  • 2021-02-07 15:03

    You can put this into a single screen app into the view controller: It will make a yellow background view and a blue layer on top of it that has an oval region cut out by a mask.

    - (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];
    
    }
    

    this might answer this question as well: UIBezierPath Subtract Path

    0 讨论(0)
  • 2021-02-07 15:08

    Here's an alternative that doesn't require reversing the path at all.

    You have a portion of a view you essentially want to "clip out":

    clipped out

    Let's say you want the white area to be [UIColor whiteColor] with 75% alpha. Here's how you do it quickly:

    • You create a new UIView subclass.
    • This view has two properties:

      @property (retain) UIColor *fillColor;
      @property (retain) UIBezierPath *punchedOutPath;
      
    • You override its -drawRect: method to do this:

      - (void)drawRect:(CGRect)rect {
        [[self fillColor] set];
        UIRectFill(rect);
      
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
      
        [[self punchedOutPath] fill];
      
        CGContextSetBlendMode(ctx, kCGBlendModeNormal);
      }
      

    There's a caveat here: The fillColor of the view must not include the alpha component. So in your case, you'd want that to just be [UIColor whiteColor]. You then apply the alpha bit yourself by calling [myView setAlpha:0.75].

    What's going on here: This is using a blend mode called "Destination Out". Mathematically it's defined as R = D*(1 - Sa), but in layman's terms it means "Destination image wherever destination image is opaque but source image is transparent, and transparent elsewhere."

    So it's going to use the destination (i.e., what's already in the context) wherever the new stuff is transparent (i.e. outside of the bezier path), and then where the bezier path would be opaque, that stuff is going to become transparent. However, the destination stuff must already be opaque. If it's not opaque, the blending doesn't do what you want. This is why you have to provide an opaque UIColor and then do any transparency you want with the view directly.


    I ran this myself, with these circumstances:

    • the window has a [UIColor greenColor] background
    • the fillColor is white
    • the punchedOutPath is a oval that's inset 10 points from the edges of the view.
    • the view has an alpha of 0.75

    With the code above, I get this:

    enter image description here

    The interior is pure green, and the outside has the semi-transparent overlay.


    Update

    If your covering is an image, then you'll need to create a new image. But the principle is the same:

    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;
    }
    

    You would then take the result of this function and put it into a UIImageView.

    0 讨论(0)
  • 2021-02-07 15:11

    Use bezierPathByReversingPath. From the docs (iOS 6.0+ only):

    Creates and returns a new bezier path object with the reversed contents of the current path.

    so to reverse your path, you'd just:

    UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:center
                                                         radius:200
                                                     startAngle:0
                                                       endAngle:180
                                                      clockwise:YES];
    self.myPath = [aPath bezierPathByReversingPath];
    
    0 讨论(0)
  • 2021-02-07 15:29

    I have two solution for you.

    1. Draw this path on a CALayer. And use that CALayer as a mask layer for you actual CALayer.

    2. Draw a rectangle with the sizes of you frame before adding arc.

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

    I would use second solution. :)

    0 讨论(0)
提交回复
热议问题