How do vector images work in Xcode (i.e. pdf files)?
Asked Answered
B

6

193

How does vector support work in Xcode 6?

When I try resizing an image, it looks jagged, what gives?

Bleier answered 13/9, 2014 at 1:1 Comment(0)
B
375

How to use vectors in Xcode (7 and 6.3+):

  1. Save an image as a .pdf file at the proper @1x size (e.g. 24x24 for a toolbar button).
  2. In your Images.xcassets file, create a new Image Set.
  3. In the Attributes Inspector, set the Scale Factors to Single Vector.
  4. Drag and drop your pdf file into the All, Universal section.
  5. You can now refer to your image by its name, just as you would any .png file.

    UIImage(named: "myImage")
    

How to use vectors in older versions of Xcode (6.0 - 6.2):

  • Follow the steps above, except for step 3, set Types to Vectors.

How vectors work in Xcode

Vector support is confusing in Xcode, because when most people think of vectors, they think of images that can scale up and down and still look good. However, Xcode 6 and 7 don't have full vector support for iOS, so things work a little differently.

The vector system is really simple. It takes your .pdf image, and creates @1x.png, @2x.png, and @3x.png assets at build time. (You can use a tool to examine the contents of Assets.car to verify this.)

For example, assume you are given foo.pdf which is a 44x44 vector asset. At build time it will generate the following files:

This works the same for any sized image. For example, if you have bar.pdf which is 100x100, you will get:


Implications:

  • You cannot choose a new size for the image; it will only look good if you keep it at the 44x44 size. The reason is that full vector support is not implemented. The only thing these vectors do is save you the time of saving your image assets. If you have a tool (e.g. a Photoshop script) that already makes this a one-step process, the only thing you will gain by using pdf vectors is future-proof support (e.g. if in iOS 9 Apple begins to require @4x assets, these will just work), and you will have fewer files to maintain.
  • You should ask for all your assets at the @1x size, saved as PDF files. Among other things, this will allow UIImageViews to have the correct intrinsic content size.

Why it (probably) works this way:

  • This makes it backwards compatible with previous iOS versions.
  • Resizing vectors may be a computational intensive task at runtime; by implementing it this way, there are no performance hits.
Bleier answered 13/9, 2014 at 1:1 Comment(22)
Nice write up. This is discussed in 2014 WWDC Session 411 - "What's New in Interface Builder" at time 44:13. Note that they say the rasterization is done at build time. Also interestingly on the MAC it will use vectors at runtime. At some distant point in the future hopefully iOS will too.Celebrate
you can also use PaintCode v2 which instantly generates code in Swift, Objective-C,C++,etc for both iOS and MAC.-> paintcodeapp.comBeryl
Huh! That's really interesting, because on the Mac, it's perfectly normal for apps to ship with .pdf vector files as template images — without being rasterised at compile time.Brinkley
This is a great explanation! I would clarify that this works for any size, one may think this implies it only works for 44x44 images.Wolof
It unfortunately doesn't scale the slice values. So if you slice corners at 5 it won't scale it to 10 and 15 for the 2x and 3x images.Charitacharitable
@Charitacharitable that sounds like a bug. You may want to report it.Diastema
You suggest asking for all assets at 1x size, but surely it's better to generate all assets at 3x and scale down?Trudeau
@JamieBullock For PDF vector assets you provide a 1x version and Xcode creates the 1x, 2x and 3x bitmaps. The PDF vectors are resolution independent.Charitacharitable
Where are located the so generated images ? I mean I want to check these images so I need to find them on finder when using simulator.Eugeneeugenia
They said it builds at runtime, but interestingly the PDF is still compiled into your Assets.car fileTompion
@Charitacharitable if you want to scale the slice values, you can do it yourself in code by using resizableImageWithCapInsets: and dividing your slice values by [UIScreen mainScreen].scaleChug
Will this work in iOS 6 as well? I'm currently supporting iOS 6 and up and they have removed support of iOS 6 SDK in Xcode 6 so I can't test it.Vinnievinnitsa
To clarify @ArieLitovsky suggestion on slicing: you should remove slicing from asset and move it to code, something like CGFloat scale = [UIScreen mainScreen].scale; UIImage *image = [[UIImage imageNamed:@"my_unsliced_asset"] resizableImageWithCapInsets:UIEdgeInsetsMake(10 * scale, 11 * scale, 12 * scale, 13 * scale)];Nicety
Where can I get the generated PNG 1x,2x,3x from PDF vector image in xcode?Superego
i think the @1x toolbar icon size is 22x22Ricoriki
@NTNT: You would need to extract the contents of the Assets.car file that is generated.Bleier
I thought PDFs/vectors don't have pixel size? What do you mean a "assume you are given foo.pdf which is a 44x44 vector asset" ?Browne
How to show this vector pdf image when I download it from server in iOS app at runtime?Adara
How do I do this for XCode 8? the option seem to have disappeared!Mayolamayon
The option is now under the "Scales" heading in the Attributes Inspector -> Scales -> Single ScaleNaturally
@JamieBullock - the PDF vector approach only makes sense if you have source images that are vectors, not bitmaps.Continuant
In case someone is wondering what the exact sizes to use for the different types of icons are: the HIG lists them here: developer.apple.com/design/human-interface-guidelines/ios/… Please note that only 3x and 2x are listed so you'll have to divide 2x by 2 to get the 1x size required for the PDFsOperatic
M
33

In Xcode 8, you can still add a pdf, create a new Image Set, and in the Attributes Inspector, set the Scales to Single Scale option. enter image description here

Mallette answered 15/12, 2016 at 14:12 Comment(0)
B
17

This is a supplement to the excellent answer by @Senseful.

How to make vector images in .pdf format

I will tell how to do this in Inkscape since it is free and open source but other programs should be similar.

In Inkscape:

  1. Create a new project.
  2. Go to File > Document Properties and set the custom page size to whatever your @1x size is (44x44, 100x100, etc) with the units in px.
  3. Make your artwork.
  4. Go to File > Save As... > Printable Document Format (*.pdf) > Save > OK. (Alternatively, you could go to Print > Print to File > Output format: PDF > Print but there are not as many options.)

Notes:

  • As is mentioned in the accepted answer, you cannot resize your image because Xcode still produces the rasterized images at build time. If you need to resize your image you should make a new .pdf file with a different size.
  • If you already have an .svg image that is the wrong page size, do the following:

    1. Change the page size (Inkscape > File > Document Properties)
    2. Select all objects (Ctrl+A) on the work space and resize them to fit in the new page size. (Hold down Ctrl to keep aspect size.)
  • To convert an .svg file into a .pdf you can also find online utilities to do the job for you. Here is one example from this answer. This has the benefit of allowing you to set the .pdf size easily.

Further reading

Bandore answered 29/12, 2015 at 4:30 Comment(0)
P
16

For those who still not updated, there were changes in Xcode 9 (iOS 11).

What’s new in Cocoa Touch (WWDC 2017 Session 201) (@32:55) https://developer.apple.com/videos/play/wwdc2017/201/

In few words, Asset Catalog now includes the new checkbox in Attributes Inspector named “Preserve Vector Data”. When checked, PDF data will be included in the compiled binary, increasing its size of course. But it gives a chance for iOS to scale the vector data in both directions and provide nice images.(With its own difficulties). For iOS below 11, old scaling mechanisms described in answers upwards is used.

Phocaea answered 20/2, 2018 at 18:46 Comment(0)
D
2

Xcode image types

1. Raster image .png

scale factor @1x, @2x, @3x

As a developer you are responsible for setting .png into corresponding factor

Official doc - Image Size and Resolution

2. Vector image .pdf and .svg

Vector PDF(Portable Document Format) Not all pdf files are vector files.

  • Xcode 6 - single scale; Build time; vector PDF -> PNG(@1x, @2x, @3x);
  • Xcode 9 and iOS 11 - Preserve Vector Data; Run time; Dynamic scale

SVG(Scalable Vector Graphics)

  • Xcode 12 and iOS < 13 - SVG -> PNG(@1x, @2x, @3x)
  • Xcode 12 and iOS 13 - Preserve Vector Data; Run time; Dynamic scale

Diff

  • Most of the time, SVG are smaller than PDF
  • SVG is readable and editable in text editor

Experiments

If you create a project and build it (not only for specific device - Any iOS device) with .pdf and .svg file you will see that they work at the same manner

  • .png files are generated(Assets.car file[About])
  • when you select Preserve Vector Data and Individual scales (NOT Single scale) the result will be Dynamic scale

Preserve Vector Data off and on

Generated files

Darton answered 29/1, 2022 at 22:18 Comment(0)
K
1

You can use normal PDF files inside your project as Vector images and render images of any size using this extension. This way is way better because iOS will not generate .PNG images out of your PDF files, plus you can render you images with any size you want:

    extension UIImage {

    static func fromPDF(filename: String, size: CGSize) -> UIImage? {
        guard let path = Bundle.main.path(forResource: filename, ofType: "pdf") else { return nil }
        let url = URL(fileURLWithPath: path)
        guard let document = CGPDFDocument(url as CFURL) else { return nil }
        guard let page = document.page(at: 1) else { return nil }

        let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(size: size)
            let img = renderer.image { ctx in
                UIColor.white.withAlphaComponent(0).set()
                ctx.fill(imageRect)
                ctx.cgContext.translateBy(x: 0, y: size.height)
                ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
                ctx.cgContext.concatenate(page.getDrawingTransform(.artBox, rect: imageRect, rotate: 0, preserveAspectRatio: true))
                ctx.cgContext.drawPDFPage(page);
            }

            return img
        } else {
            // Fallback on earlier versions
            UIGraphicsBeginImageContextWithOptions(size, false, 2.0)
            if let context = UIGraphicsGetCurrentContext() {
                context.interpolationQuality = .high
                context.setAllowsAntialiasing(true)
                context.setShouldAntialias(true)
                context.setFillColor(red: 1, green: 1, blue: 1, alpha: 0)
                context.fill(imageRect)
                context.saveGState()
                context.translateBy(x: 0.0, y: size.height)
                context.scaleBy(x: 1.0, y: -1.0)
                context.concatenate(page.getDrawingTransform(.cropBox, rect: imageRect, rotate: 0, preserveAspectRatio: true))
                context.drawPDFPage(page)
                let image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return image
            }
            return nil
        }
    }

}
Kit answered 6/12, 2016 at 17:7 Comment(1)
I tried this, but it doesn't seem like it scales the image up to larger pixel sizes? The UIImage itself that I'm getting is the correct size; it's just that the vector image is centered and still at the original pixel size of the PDF. (I wanted to scale it to the size of the UIImage.) Do you know if this would be possible?Amateurism

© 2022 - 2024 — McMap. All rights reserved.