Why do PDFs resized in SwiftUI getting sharp edges?

前端 未结 2 1148
夕颜
夕颜 2021-01-03 04:15

I try to include a pdf in my SwiftUI enabled app using Xcode 11.4 and iOS 13.4. However, when I resize the pdf, it gets crips edges. I have included two versions of the pdf:

相关标签:
2条回答
  • 2021-01-03 04:50

    There are essentially no differences between the two PDF files, other than the fact that in one case the co-ordinates of the content are scaled by a factor of ~14.2.

    I would guess th difference is not in the PDF files, but in the rendering engine you are using to draw the contents. Note that the PDF file uses transparency (it has a constant alpha of 0.4) so the blending calculations might lead to slightly different results at the edges.

    Looking at the two files in Adobe Acrobat, scaled to be the same size on screen, there is no visible difference between them.

    Zooming in to your PNG file I see that icon_small.pdf has anti-aliased edges, while icon.pdf does not. You don't say what you are using to render the PDF files to a PNG but I think you're going to have to discuss it with the authors of whatever tool that is.

    0 讨论(0)
  • 2021-01-03 05:03

    I did a side by side comparison for both vector images using the ones you provided:

    • http://simensolbakken.com/public/stackoverflow/icon.pdf
    • http://simensolbakken.com/public/stackoverflow/icon_small.pdf

    At first, I used SwiftUI's inbuilt Image and as mentioned, both performed badly at their extreme ends:

    • Large image got sharp edges when it scaled down
    • Small image got blurred as it scaled up

    At first I thought it might be your pdf vectors so I used ones that I know have worked well in my previous projects, but I got the same issues.
    Thinking it to be a UIImage issue, I used SwiftUIs Image(uiImage:) but same problem.

    Last guess was the image container, and knowing that UIImageView has handled vector images well, getting UIViewRepresentable to wrap the UIImageView seems to solve this issue. And for now it looks like a possible workaround.

    Workaround Solution:

    struct MyImageView: UIViewRepresentable {
      var name: String
      var contentMode: UIView.ContentMode = .scaleAspectFit
      var tintColor: UIColor = .black
    
      func makeUIView(context: Context) -> UIImageView {
        let imageView = UIImageView()
        imageView.setContentCompressionResistancePriority(.fittingSizeLevel, 
                                                          for: .vertical)
        return imageView
      }
    
      func updateUIView(_ uiView: UIImageView, context: Context) {
        uiView.contentMode = contentMode
        uiView.tintColor = tintColor
        if let image = UIImage(named: name) {
          uiView.image = image
        }
      }
    }
    

    This loses some SwiftUI Image modifiers (you still have normal View modifiers) but you can always pass in some parameters such as contentMode and tintColor as shown above. Add more if needed and handle accordingly.


    Usage Example:

    struct ContentView: View {
      var body: some View {
        VStack {
          MyImageView(name: "icon", //REQUIRED
                      contentMode: .scaleAspectFit, //OPTIONAL
                      tintColor: .black /*OPTIONAL*/)
            .frame(width: 27, height: 27)
          MyImageView(name: "icon_small", //REQUIRED
                      contentMode: .scaleAspectFit, //OPTIONAL
                      tintColor: .black /*OPTIONAL*/)
            .frame(width: 27, height: 27)
        }
      }
    }
    

    Now this is all speculation but it looks as though SwiftUI treats vector images as a PNG.

    The following example is a simple side by side comparison of the small and large vector images rendered in UIKit's UIImageView and SwiftUI's Image.

    Comparison:

    struct ContentView: View {
      let (largeImage, smallImage) = ("icon", "icon_small")
      let range = stride(from: 20, to: 320, by: 40).map { CGFloat($0) }
    
      var body: some View {
        List(range, id: \.self) { (side) in
          ScrollView(.horizontal) {
            VStack(alignment: .leading) {
              Text(String(format: "%gx%g", side, side))
              HStack {
                VStack {
                  Text("UIKit")
                  MyImageView(name: self.smallImage)
                    .frame(width: side, height: side)
                  MyImageView(name: self.largeImage)
                    .frame(width: side, height: side)
                }
                VStack {
                  Text("SwiftUI")
                  Image(self.smallImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: side)
                  Image(self.largeImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: side)
                }
              }
            }
          }
        }
      }
    }
    

    Results:

    1. Top row; Left : Small Image in UIImageView
    2. Top row; Right : Small Image in SwiftUI Image
    3. Bottom row; Left : Large Image in UIImageView
    4. Bottom row; Right : Large Image in SwiftUI Image

    UIKit's UIImageView has consistent performace while SwiftUI's Image is having trouble.




    0 讨论(0)
提交回复
热议问题