Reputation: 145
I'm trying to make a simple drawing app in swift, iOS.
So i've managed to draw using Path(). Now i want to save an image of what i've drawn.
I have found on apple docs that ImageRenderer() is the thing to use nowadays. But i can't seem to make it work with Path().
This renders just fine:
let renderer = ImageRenderer(content: Text("3"))
This doesn't:
let renderer = ImageRenderer(content: Path { path in
path.addLines(lines)
}.stroke(lineWidth: 5.0))
I've spent 3 days now with different methods and hacks, but nothing really works?
Why is it that cant render a path, which is a View with swifts ImageRenderer?
Upvotes: 2
Views: 1033
Reputation: 24247
You can very well draw a Path instance. Your problem is that you are not actually stroking the path and so it looks empty. Let's fix that and also make sure that the path we draw fits nicely inside an image.
func generateImage(from path: Path) -> UIImage? {
let originalRect = path.boundingRect
let newPath = path.offsetBy(dx: -originalRect.minX, dy: -originalRect.minY)
let boundingRect = newPath.boundingRect
let contentToDraw = newPath
.stroke(Color.red, lineWidth: 2)
.frame(width: boundingRect.width, height: boundingRect.height)
.padding()// feel free to disregard padding depending on your use case
let renderer = ImageRenderer(content: contentToDraw)
renderer.scale = 2 // choose a scale you prefer
renderer.proposedSize = .init(boundingRect.size)
let image = renderer.uiImage
return image
}
Testing this out we can draw a shape and then preview it as an image:
let size = CGSize(width: 100, height: 100)
let pathToDraw = Path { path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: size.width, y: 0))
path.addLine(to: CGPoint(x: size.width, y: size.height))
path.addLine(to: CGPoint(x: 0, y: size.height))
path.addLine(to: CGPoint(x: 0, y: 0))
}
let image = generateImage(from pathToDraw)
Upvotes: 0
Reputation: 519
I found a working solution. It was suggested by Fault. I wrapped all the Path parts in ZStack and gave the ZStack a frame:
struct ContentView: View {
@State private var imgData:Data?
var body: some View {
Grid(alignment: .center) {
GridRow {
Rectangle()
.foregroundColor(Color.green)
Rectangle()
.foregroundColor(Color.red)
Rectangle()
.foregroundColor(Color.yellow)
}
GridRow {
Rectangle()
.foregroundColor(Color.orange)
GeometryReader { geo in
ViewToRender(size: geo.size)
.onAppear {
saveview(ViewToRender(size: geo.size))
}
}
Rectangle()
.foregroundColor(Color.blue)
}
GridRow {
Rectangle()
.foregroundColor(Color.gray)
Rectangle()
.foregroundColor(Color.brown)
if let d=imgData, let img=UIImage(data: d) {
Image(uiImage: img)
.resizable()
.scaledToFit()
}else{
Rectangle()
.foregroundColor(Color.cyan)
}
}
}
}
@MainActor func saveview(_ view:ViewToRender) {
if let data=ImageRenderer(content: view).uiImage?.pngData() {
print("\(data)")
imgData=data
}
}
}
struct ViewToRender:View {
let size:CGSize
init(size:CGSize) {
self.size=size
}
var body: some View {
ZStack {
Path {path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: size.width, y: 0))
path.addLine(to: CGPoint(x: size.width, y: size.height))
path.addLine(to: CGPoint(x: 0, y: size.height))
path.addLine(to: CGPoint(x: 0, y: 0))
}.stroke(lineWidth: 2)
.foregroundColor(Color.red)
}.frame(width: size.width, height: size.height)
}
}
Upvotes: 0