旋转CGPath而不改变其位置

时间:2012-12-06 06:48:57

标签: iphone objective-c ios core-graphics

我想要转动CGPath,我正在使用以下代码:

CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
CGPathRef rotatedPath = CGPathCreateCopyByTransformingPath(myPath, &transform);

此代码工作正常,但它改变了路径的位置!我希望路径保持与旋转前相同的位置。

4 个答案:

答案 0 :(得分:59)

路径没有“位置”。路径是一组点(由线段和曲线段定义)。每一点都有自己的立场。

也许您想要围绕特定点旋转路径,而不是围绕原点旋转。诀窍是创建一个组合三个变换的复合变换:

  1. 将原点转换为旋转点。
  2. 旋转。
  3. 反转第1步的翻译。
  4. 例如,这是一个采用路径并返回一条新路径的函数,该路径是围绕其边界框中心旋转的原始路径:

    static CGPathRef createPathRotatedAroundBoundingBoxCenter(CGPathRef path, CGFloat radians) {
        CGRect bounds = CGPathGetBoundingBox(path); // might want to use CGPathGetPathBoundingBox
        CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
        CGAffineTransform transform = CGAffineTransformIdentity;
        transform = CGAffineTransformTranslate(transform, center.x, center.y);
        transform = CGAffineTransformRotate(transform, radians);
        transform = CGAffineTransformTranslate(transform, -center.x, -center.y);
        return CGPathCreateCopyByTransformingPath(path, &transform);
    }
    

    请注意,此函数返回一个新路径,其中包含+1保留计数,当您完成该计数后,您将负责释放该计数。例如,如果您尝试旋转形状图层的路径:

    - (IBAction)rotateButtonWasTapped:(id)sender {
        CGPathRef path = createPathRotatedAroundBoundingBoxCenter(shapeLayer_.path, M_PI / 8);
        shapeLayer_.path  = path;
        CGPathRelease(path);
    }
    

    更新

    这是使用Swift游乐场的演示。我们将从一个显示路径的辅助函数开始,并用十字准线标记原点:

    import UIKit
    import XCPlayground
    
    func showPath(label: String, path: UIBezierPath) {
        let graph = UIBezierPath()
        let r = 40
        graph.moveToPoint(CGPoint(x:0,y:r))
        graph.addLineToPoint(CGPoint(x:0,y:-r))
        graph.moveToPoint(CGPoint(x:-r,y:0))
        graph.addLineToPoint(CGPoint(x:r,y:0))
        graph.appendPath(path)
        XCPCaptureValue(label, graph)
    }
    

    接下来,这是我们的测试路径:

    var path = UIBezierPath()
    path.moveToPoint(CGPoint(x:1000,y:1000))
    path.addLineToPoint(CGPoint(x:1000,y:1200))
    path.addLineToPoint(CGPoint(x:1100,y:1200))
    showPath("original", path)
    

    original path

    (请记住,十字准线是原点,不是我们正在改造的路径的一部分。)

    我们获得中心并转换路径,使其以原点为中心:

    let bounds = CGPathGetBoundingBox(path.CGPath)
    let center = CGPoint(x:CGRectGetMidX(bounds), y:CGRectGetMidY(bounds))
    
    let toOrigin = CGAffineTransformMakeTranslation(-center.x, -center.y)
    path.applyTransform(toOrigin)
    showPath("translated center to origin", path)
    

    path translated to origin

    然后我们旋转它。所有的旋转都发生在原点周围:

    let rotation = CGAffineTransformMakeRotation(CGFloat(M_PI / 3.0))
    path.applyTransform(rotation)
    showPath("rotated", path)
    

    path rotated

    最后,我们将其翻译回来,完全颠倒原始翻译:

    let fromOrigin = CGAffineTransformMakeTranslation(center.x, center.y)
    path.applyTransform(fromOrigin)
    showPath("translated back to original center", path)
    

    path translated from origin

    请注意,我们必须反转原始翻译。我们不会通过其新边界框的中心进行翻译。回想一下(在这个例子中),原始中心位于(1050,1100)。但是在我们将它转​​换为原点并旋转它之后,新的边界框的中心位于(-25,0)。将路径翻译为(-25,0)不会使其接近原始位置!

答案 1 :(得分:1)

一种Swift 3方法,用于围绕其中心旋转矩形:

func createRotatedCGRect(rect: CGRect, radians: CGFloat) -> CGPath {
    let center = CGPoint(x: rect.midX, y: rect.midY)
    let path = UIBezierPath(rect: rect)
    path.apply(CGAffineTransform(translationX: center.x, y: center.y).inverted())
    path.apply(CGAffineTransform(rotationAngle: radians))
    path.apply(CGAffineTransform(translationX: center.x, y: center.y))
    return path.cgPath
}

所有rob mayoff用于将其应用于路径的代码及其边界框仍然适用,因此我没有看到重复它们的原因。

答案 2 :(得分:0)

Swift 5版本:

func rotate(path: UIBezierPath, degree: CGFloat) {
    let bounds: CGRect = path.cgPath.boundingBox
    let center = CGPoint(x: bounds.midX, y: bounds.midY)

    let radians = degree / 180.0 * .pi
    var transform: CGAffineTransform = .identity
    transform = transform.translatedBy(x: center.x, y: center.y)
    transform = transform.rotated(by: radians)
    transform = transform.translatedBy(x: -center.x, y: -center.y)
    path.apply(transform)
}

答案 3 :(得分:0)

如果您想旋转和/或缩放 UIBezierPath,或缩放 CAShapeLayer,同时保持 UIBezierPath 的中心。这里是 Swift 4/5 的函数。它基于 rob mayoff 的回答。

extension UIBezierPath {
 
/// perform scale on UIBezierPath and keep the position in the path's center
  func scale(scale:CGFloat) {
    let bounds = self.cgPath.boundingBox
    
    let center = CGPoint(x:bounds.midX,y:bounds.midY)
    var transform = CGAffineTransform.identity.translatedBy(x: center.x, y: center.y)
    transform = transform.scaledBy(x: scale, y: scale)
    transform = transform.translatedBy(x: -center.x, y: -center.y);
    
    self.apply(transform)
  }


  /// perform rotate on UIBezierPath and keep the center unchanged
  func rotate(radians:CGFloat) {
    let bounds = self.cgPath.boundingBox
    
    let center = CGPoint(x:bounds.midX,y:bounds.midY)
    var transform = CGAffineTransform.identity.translatedBy(x: center.x, y: center.y)
    transform = transform.rotated(by: radians)
    transform = transform.translatedBy(x: -center.x, y: -center.y);
    
    self.apply(transform)
  }
}