Search code examples
iosswiftrotationuibezierpathcgaffinetransform

Rotate BezierPath around the x axis for PDF annotation Swift 4 iOS


I am trying to annotate a PDF using type .ink in my application using a UIBezierPath. I have included a snippet of the pertinent code below (can add the whole sample but issue is only with rotating the path). The issue is when I apply this path, it is rotated 180 degrees around the x- axis so basically it is flipped upside down. I would like to be able to rotate this path 180 degrees around the x-axis so it appears as initially intended. I have seen example of rotating around the z-axis but none around the x-axis. Any help would be greatly appreciated!

            let rect = CGRect(x: 110, y: 100, width: 400, height: 300)

            let annotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
            annotation.backgroundColor = .blue

            path.apply(CGAffineTransform(scaleX: 0.2, y: 0.2))
            annotation.add(path)

            // Add annotation to the first page
            page.addAnnotation(annotation)

            pdfView?.document?.page(at: 0)?.addAnnotation(annotation)

Solution

  • I was actually able to solve this issue using the following scale and a translation transforms:

    let rect = CGRect(x: 110, y: 100, width: 400, height: 300)
    
    let annotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
    annotation.backgroundColor = .blue
    
    // OVER HERE 🙂
    path.apply(CGAffineTransform(scaleX: 1.0, y: -1.0))
    path.apply(CGAffineTransform(translationX: 0, y: rect.size.height))
    
    annotation.add(path)
    
    // Add annotation to the first page
    page.addAnnotation(annotation)
    
    pdfView?.document?.page(at: 0)?.addAnnotation(annotation)
    

    This solution is inspired from the Apple Developer Documentation example.

    It was a bit tricky because the PDFKit Coordinate System uses the bottom/left as the origin, with the x- axis going left-to-right and the y- axis going bottom-to-top. That's contrary to the origin: top/left, x: left-to-right and y: top-to-bottom pattern usually encountered on iOS. But this is what we're doing:

    • CGAffineTransform(scaleX: 1.0, y: -1.0) - scaling the y coordinate to -1.0 makes your path flip 180 degrees around the x- axis (said axis visually being the bottom line of the rect). This means the path is now below of the rect, which might give you the impression that it has disappeared (you won't even find it by Capturing the View Hierarchy since it will show you the entire PDFView as one UIView component, which may or may not drive you insane).

    • CGAffineTransform(translationX: 0, y: rect.size.height) - now that the path is at the bottom of the rect (but actually at the "top" according to the PDFKit Coordinate System), we need to bring it back into the visible area. Which is why we need to apply a translation transform to move the path up (or down - thanks again PDFKit) into the rect.

    Hope this helps! Cheers