Reza Takhti
Reza Takhti

Reputation: 80

What is the best way to rotate a view in swift and convert it to an image?

I have been given the task of creating a dynamic "ticket" in Swift. I am passed the ticket number, amount, etc from our servers API, and I am to generate the barcode, along with all labels associated with this ticket. I am able to generate all the necessary data without any issues.

The problem

The issue arises with laying it out. I need to have a thumbnail view for this ticket, along with a fullscreen view. This seems to be best done by converting the view into an image (right?) as it allows for features like zooming, having the thumbnail view etc. The main cause of the issue is the ticket labels and barcode need to be laid out vertically, or basically in landscape mode.

What I've tried

UIGraphicsBeginImageContext

I have created the image manually with UIGraphicsBeginImageContext() and associated APIs. This allows me to flip each view and convert it to an image. However, this method forces me to manually create a frame for each view and loses all accuracy and does not seem like the right way to do it when I have to add 10-15labels to a blank image.

AutoLayout

Next I tried laying everything out in a UIView with autolayout and applying a CGAffineTransform to each view and then converting the whole view to an image. This seems to work with the exception that I lose precision and can't line up views correctly. CGAffineTransform throws off constraints completely and I have to experiment with constraint constants until I get the view looking somewhat right and even then that doesn't translate all that well to all device sizes.

Landscape Mode

Lastly, I tried laying out the views normally, and forcing the view into landscape mode. Aside from the number of issues that arose because my app only supports portrait mode, I got it to work when the view is presented, but I have no idea how to get the thumbnail view which is supposed to show before the ticket view is presented to be in landscape mode. If I try doing so the thumbnail comes out in portrait mode and not landscape.

Do you guys have any ideas on a better way to accomplish this or should I stick to one of the methods that I've tried and try to work out all the bugs? I can provide code as needed but there's a lot that goes into it so I didn't want to just throw all the code in here if it wasn't necessary.

The following is an example of what I need to create except I need to add additional labels on there such as issue date, expiration date, etc:

enter image description here

Any help would be appreciated!

Upvotes: 0

Views: 593

Answers (1)

Rob
Rob

Reputation: 438437

You asked:

What is the best way to rotate a view in swift and convert it to an image?

If you want to create a rotated snapshot of a view, apply a rotate and a translateBy to the context:

func clockwiseSnapshot(of subview: UIView) -> UIImage {
    var rect = subview.bounds
    swap(&rect.size.width, &rect.size.height)
    return UIGraphicsImageRenderer(bounds: rect).image { context in
        context.cgContext.rotate(by: .pi / 2)
        context.cgContext.translateBy(x: 0, y: -rect.width)
        subview.drawHierarchy(in: subview.bounds, afterScreenUpdates: true)
    }
}

Or

func counterClockwiseSnapshot(of subview: UIView) -> UIImage {
    var rect = subview.bounds
    swap(&rect.size.width, &rect.size.height)
    return UIGraphicsImageRenderer(bounds: rect).image { context in
        context.cgContext.rotate(by: -.pi / 2)
        context.cgContext.translateBy(x: -rect.height, y: 0)
        subview.drawHierarchy(in: subview.bounds, afterScreenUpdates: true)
    }
}

Obviously, if you want the Data associated with the image, instead, use pngData or jpegData instead:

func clockwiseSnapshotData(of subview: UIView) -> Data {
    var rect = subview.bounds
    swap(&rect.size.width, &rect.size.height)
    return UIGraphicsImageRenderer(bounds: rect).pngData { context in
        context.cgContext.rotate(by: .pi / 2)
        context.cgContext.translateBy(x: 0, y: -rect.width)
        subview.drawHierarchy(in: subview.bounds, afterScreenUpdates: true)
    }
}

Or

func counterClockwiseSnapshotData(of subview: UIView) -> Data {
    var rect = subview.bounds
    swap(&rect.size.width, &rect.size.height)
    return UIGraphicsImageRenderer(bounds: rect).pngData { context in
        context.cgContext.rotate(by: -.pi / 2)
        context.cgContext.translateBy(x: -rect.height, y: 0)
        subview.drawHierarchy(in: subview.bounds, afterScreenUpdates: true)
    }
}

If you don’t really need the image, but just want to rotate it in the UI, then apply a transform to the view that contains all of these subviews:

someView.transform = .init(rotationAngle: .pi / 2)

Upvotes: 1

Related Questions