Reputation: 80
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 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.
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.
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.
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:
Any help would be appreciated!
Upvotes: 0
Views: 593
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