My view doesn't have a black background when displayed, but after saving it as an image, the content of the view is reduced, and a black background appears above it.
Here's the whole code.
import Foundation
import Photos
import SwiftUI
import UIKit
struct MyImageView: View {
@State private var image: UIImage?
@State var viewWidth = 0.0
@State var viewHeight = 0.0
@ObservedObject var imageSaver = ImageSaver()
var imageUrl = "https://pic.netbian.com/uploads/allimg/160709/215802-1468072682c2c8.jpg"
func calculateSize() {
let maxWidth = UIScreen.main.bounds.width
let maxHeight = UIScreen.main.bounds.height - 100
if maxHeight/maxWidth >= image!.size.height/image!.size.width {
viewWidth = maxWidth
viewHeight = maxWidth * image!.size.height/image!.size.width
} else {
viewHeight = maxHeight
viewWidth = maxHeight/(image!.size.height/image!.size.width)
}
}
private func loadImage() {
URLSession.shared.dataTask(with: URL(string: imageUrl)!) { data, _, error in
guard let data = data, error == nil else {
print("Error loading image: \(error?.localizedDescription ?? "Unknown error")")
return
}
if let uiImage = UIImage(data: data) {
DispatchQueue.main.async {
self.image = uiImage
calculateSize()
}
} else {
print("Failed to create image from data")
}
}.resume()
}
var myView: some View {
ZStack {
if self.image != nil {
Image(uiImage: self.image!)
.resizable()
.frame(width: viewWidth, height: viewHeight)
}
Text("hello girl").font(.title).foregroundColor(.red)
}.frame(width: viewWidth, height: viewHeight)
.background(Color.black)
.onAppear {
loadImage()
}
}
var body: some View {
VStack {
myView
Button("save image") {
myView.snapshot { image in
if let image = image {
imageSaver.saveSnapshot(compressedImage: image)
} else {
print("Failed to capture snapshot image.")
}
}
}
}
}
}
extension View {
func snapshot(completion: @escaping (UIImage?) -> Void) {
let controller = UIHostingController(rootView: self)
let view = controller.view
view?.translatesAutoresizingMaskIntoConstraints = false
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .black
view?.layoutIfNeeded()
let renderer = UIGraphicsImageRenderer(size: targetSize)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let image: UIImage = renderer.image { _ in
controller.view.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
controller.view.removeFromSuperview()
completion(image)
}
}
}
class ImageSaver: NSObject, ObservableObject {
@Published var isPresented = false
@Published var title: String = ""
@Published var message: String = ""
func saveSnapshot(compressedImage: UIImage) {
PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
switch status {
case .authorized:
UIImageWriteToSavedPhotosAlbum(
compressedImage, self, #selector(self.saveError), nil
)
case .denied:
self.title = NSLocalizedString("access_denied", comment: "")
self.message =
NSLocalizedString("snapshot_saver_1", comment: "")
self.isPresented = true
print("access denied")
case .limited:
self.title = NSLocalizedString("snapshot_saver_2", comment: "")
self.message =
NSLocalizedString("snapshot_saver_1", comment: "")
self.isPresented = true
print("limited access only")
case .notDetermined:
print("access not determined")
case .restricted:
print("access restricted")
@unknown default:
break
}
}
}
@objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
isPresented = true
if error != nil {
title = "failed"
message = "save image failed"
} else {
title = "success"
message = "save image success"
}
}
}
#Preview {
MyImageView()
}
To ensure a perfect consistency between the displayed view and the exported image, I have fixed the width and height of the view.

