1

I have a goal to convert View to Image using SwiftUI.

From the first image I have a look that is exactly what I want.

Link picture View will be convert to an Image:

but when I convert the display to an image, the background object and the sticker (Santa's hat) size and position are messed up, even though I've made it neat and fit exactly what I want. the following is the result of the image:

View results that have been converted into an Image:

This is my Code View

import SwiftUI

struct SnapshotView: View {
  @ObservedObject var effectData: EffectViewModel
  @ObservedObject var eraseModel: EraseViewModel
  @ObservedObject var stickerData: StickerViewModel
  @Binding var image: UIImage?
  @Binding var isActive: Bool
  @Binding var isEffectAvailable: Bool
  @Binding var sliderValueBlur: Double
  
  init(image: Binding<UIImage?>, isActive: Binding<Bool>,
       isEffectAvailable: Binding<Bool>,
       sliderValueBlur: Binding<Double>,
       effectData: ObservedObject<EffectViewModel>,
       eraseModel: ObservedObject<EraseViewModel>,
       stickerData: ObservedObject<StickerViewModel>) {
    _image = image
    _isActive = isActive
    _isEffectAvailable = isEffectAvailable
    _sliderValueBlur = sliderValueBlur
    _effectData = effectData
    _eraseModel = eraseModel
    _stickerData = stickerData
  }
  
  var body: some View {
    GeometryReader { geo in
      VStack {
        ZStack(alignment: .center) {
          if isEffectAvailable {
            VStack {
              Image(uiImage: self.effectData.modelEffect!.image_effect)
                .resizable()
                .scaledToFit()
                .frame(width: geo.size.width, alignment: .center)
                .position(self.effectData.modelEffect!.position_effect)
                .scaleEffect(self.effectData.modelEffect!.currentScale)
                .mask(
                  Image(uiImage: image!)
                    .resizable()
                    .scaledToFit()
                    .frame(width: geo.size.width, alignment: .center)
                )
                .blur(radius: CGFloat(self.effectData.modelEffect!.valueBlur))
                .opacity(self.effectData.modelEffect!.opacity_effect)
            }
            .background(
              ZStack {
                Image(uiImage: image!)
                  .resizable()
                  .scaledToFit()
                  .frame(width: geo.size.width, alignment: .top)
                  .blur(radius: CGFloat(sliderValueBlur)*4)
              }
              .border(Color.black, width: 1)
            )
          } else {
            ZStack {
              Image(uiImage: image!)
                .resizable()
                .scaledToFit()
                .frame(width: geo.size.width, alignment: .top)
                .blur(radius: CGFloat(sliderValueBlur)*4)
            }
            .border(Color.black, width: 1)
          }
          
          
          Image(uiImage: self.eraseModel.inputImage)
            .resizable()
            .scaledToFit()
            .frame(width: geo.size.width, alignment: .center)
            .overlay(
              VStack {
                ForEach(stickerData.selectedSticker.indices, id: \.self) { index in
                  ZStack(alignment: .bottomTrailing) {
                    Image(uiImage: stickerData.selectedSticker[index].image)
                      .resizable()
                      .frame(width: stickerData.selectedSticker[index].width, height: stickerData.selectedSticker[index].height, alignment: .center)
                      .scaleEffect(stickerData.selectedSticker[index].scale)
                      .overlay(
                        Rectangle()
                          .stroke(Color.gray, lineWidth: stickerData.selectedSticker[index].isEditing ? 4 : 0)
                      )
                  }
                  .frame(width: 130, height: 130, alignment: .center)
                  .position(stickerData.selectedSticker[index].position)
                  .scaleEffect(stickerData.selectedSticker[index].scale)
                }
              }
            )
        }
      }
    }
  }
}

And this is the function I use to convert View to Image

extension UIView {
    var renderedImage: UIImage {
        // rect of capure
        let rect = self.bounds
        // create the context of bitmap
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
        let context: CGContext = UIGraphicsGetCurrentContext()!
        self.layer.render(in: context)
        // get a image from current context bitmap
        let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return capturedImage
    }
}

extension View {
    func takeScreenshot(origin: CGPoint, size: CGSize) -> UIImage {
        let window = UIWindow(frame: CGRect(origin: origin, size: size))
        let hosting = UIHostingController(rootView: self)
        hosting.view.frame = window.frame
        window.addSubview(hosting.view)
        window.makeKeyAndVisible()
        return hosting.view.renderedImage
    }
}

Usage example:

    Button(action: {
      let viewSnapshot = SnapshotView(image: self.$image, isActive: self.$isActive, isEffectAvailable: self.$isEffectAvailable, sliderValueBlur: self.$sliderValueBlur, effectData: self._effectData, eraseModel: self._eraseModel, stickerData: self._stickerData)
      let saveImage = viewSnapshot.takeScreenshot(origin: geo.frame(in: .global).origin, size: self.image!.size)
      UIImageWriteToSavedPhotosAlbum(saveImage, nil, nil, nil)
    }, label: {
      Text("SAVE")
        .foregroundColor(.white)
    })

Maybe you can help me, what's wrong here

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • I have a (sort of) related issue [here](https://stackoverflow.com/q/69034515/9607863). Have you tried both iOS 14 & iOS 15? Mine only worked properly on iOS 14. – George Sep 23 '21 at 02:11
  • Wow, that's an old code, I don't even know how to use it. Maybe there's another easier reference? Because I'm fairly new to this programming. – Riswan Gani Padilah Sep 23 '21 at 06:28

0 Answers0