Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reposition/resize UIBezierPath

I have a draggable view that has a mask layer on it. The mask layer has a UIBezierPath on it which makes an area on the view see-through/transparent(the effect that i want). My end goal is to change the position and size of the path(not the mask layer!) by passing a CGRect that is calculated based on the intersection of my view and another rectangle(Basically I want to hide the area that intersects).

1)How I create my mask and path(creates a see-through rectangle on my view):

self.maskLayer = [CAShapeLayer layer];
self.maskLayer.frame = self.contentView.bounds;

//default path rect
CGRect const rect = CGRectMake(CGRectGetMidX(self.contentView.bounds) ,
                                     CGRectGetMidY(self.contentView.bounds),
                                     50,50);


path = [UIBezierPath bezierPathWithRect:rect];

[path appendPath:[UIBezierPath bezierPathWithRect:self.contentView.frame]];

self.maskLayer.path = path.CGPath;

self.maskLayer.fillRule = kCAFillRuleEvenOdd;

self.contentView.layer.mask = self.maskLayer;

2) My problem is that I couldn't find a way to resize/reposition just the path. I searched online but couldn't find any solution.

This code is not working but is what I could think of:

//runs while the view is being dragged(using UIPanGestureRecognizer)
-(void)move{
 CGRect intersectedRect = CGRectIntersection(self.frame, self.productView.frame);
 path = [UIBezierPath bezierPathWithRect:intersectedRect];
[path appendPath:[UIBezierPath bezierPathWithRect:intersectedRect]];
[self.maskLayer setNeedsDisplay];
 }

How can I manipulate just the path of the masklayer in my move function? Thank you!

like image 725
Soc Avatar asked Dec 05 '13 12:12

Soc


5 Answers

// example path
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) cornerRadius:4];

// scale it
CGFloat scale = 0.9;
[path applyTransform:CGAffineTransformMakeScale(scale, scale)];

// move it
CGSize translation = CGSizeMake(10, 5);
[path applyTransform:CGAffineTransformMakeTranslation(translation.width,
                                                      translation.height)];

// apply it
self.myLayer.path = path;
like image 141
hfossli Avatar answered Nov 13 '22 09:11

hfossli


Swift version

Here is @hfossli's code for Swift:

// example path
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 100, height: 100), cornerRadius: 4)

// scale it
let scale = CGFloat(0.9)
path.applyTransform(CGAffineTransformMakeScale(scale, scale))

// move it
let translation = CGSize(width: 10, height: 5)
path.applyTransform(CGAffineTransformMakeTranslation(translation.width,
    translation.height))

// apply it
self.myLayer.path = path

Update

I've found in actual practice that I just just different initial values when creating a UIBezierPath rather than transforming it later. I guess it would depend on the purpose.

like image 40
Suragch Avatar answered Nov 13 '22 10:11

Suragch


Swift 3 extension (modify it with factorX, factorY if needed) :

extension UIBezierPath
{
    
    func scaleAroundCenter(factor: CGFloat)
    {
        let beforeCenter = self.bounds.getCenter()
        
        // SCALE path by factor
        let scaleTransform = CGAffineTransform(scaleX: factor, y: factor)
        self.apply(scaleTransform)
        
        let afterCenter = self.bounds.getCenter()
        let diff = CGPoint(
            x: beforeCenter.x - afterCenter.x,
            y: beforeCenter.y - afterCenter.y)
        
        let translateTransform = CGAffineTransform(translationX: diff.x, y: diff.y)
        self.apply(translateTransform)
    }

SWIFT 5 UPDATE

extension UIBezierPath {

    func scaleAroundCenter(factor: CGFloat) {
        let beforeCenter = CGPoint(x: self.bounds.midX, y: self.bounds.midY)

        // SCALE path by factor
        let scaleTransform = CGAffineTransform(scaleX: factor, y: factor)
        self.apply(scaleTransform)

        let afterCenter = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
        let diff = CGPoint( x: beforeCenter.x - afterCenter.x, y: beforeCenter.y - afterCenter.y)

        let translateTransform = CGAffineTransform(translationX: diff.x, y: diff.y)
        self.apply(translateTransform)
    }

}
like image 35
sabiland Avatar answered Nov 13 '22 09:11

sabiland


You need to assign the updated path to self.maskLayer.path in your move method.

like image 27
yurish Avatar answered Nov 13 '22 09:11

yurish


It's in swift 3 or higher

func move(path: UIBezierPath, fromPoint: CGPoint, toPoint: CGPoint) {
    let moveX = toPoint.x - fromPoint.x
    let moveY = toPoint.y - fromPoint.y
    path.apply(CGAffineTransform(translationX: moveX, y: moveY))
}

func scale(path: UIBezierPath, fromSize: CGSize, toSize: CGSize) {
    if fromSize.width == 0 || fromSize.height == 0 {
        return
    }
    let scaleWidth = toSize.width / fromSize.width
    let scaleHeight = toSize.height / fromSize.height
    path.apply(CGAffineTransform(scaleX: scaleWidth, y: scaleHeight))
}
like image 3
Roman Filippov Avatar answered Nov 13 '22 09:11

Roman Filippov