Fixed Size Annotations


This article explains how to create an annotation that has a fixed size, which doesn't scale when the page is zoomed in or out.

Note that this will only work with annotations that have the isOverlay flag set, which is not preserved when the document is saved and reopened and therefore only works and is respected for new annotations. This won't work for annotations that are already in the document when it is being opened. Please delete and readd these annotations if you want to give them a fixed size.

First you need to inherit from the PSPDFAnnotation subclass you want to use a fixed size on, and override the following properties and methods:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
override var isFixedSize: Bool {
    return true
}

override var fixedSize: CGSize {
    return CGSize(width: 100, height: 100)
}

override var isOverlay: Bool {
    get {
        return true
    }
    set {}
}
Copy
1
2
3
4
5
6
7
8
9
10
11
- (BOOL)isFixedSize {
    return YES;
}

- (CGSize)fixedSize {
    return CGSizeMake(100.f, 100.f);
}

- (BOOL)isOverlay {
    return YES;
}

You also need to create a custom PSPDFAnnotationView subclass that will be used to display the annotation on the page view, as it can't be rendered on the page like annotations usually would, but it will need to be a custom view on the page view.

The following code shows how to create a custom view, that respects the fixed size:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class FixedSizeAnnotationView: PSPDFAnnotationView {

    private lazy var annotationImageView: UIImageView = {
        let imageView = UIImageView(frame: self.bounds)
        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(annotationImageView)
    }

    override var annotation: PSPDFAnnotation? {
        didSet {
            updateImage()
        }
    }

    override var zoomScale: CGFloat {
        didSet {
            let scale = fmax(1, zoomScale)
            annotationImageView.transform = CGAffineTransform(scaleX: 1 / scale, y: 1 / scale)
        }
    }

    func renderAnnotationImage() -> UIImage? {
        guard let annotation = annotation else { return nil }
        return annotation.image(with: annotation.fixedSize)
    }

    override func annotationChangedNotification(_ notification: Notification) {
        super.annotationChangedNotification(notification)
        updateImage()
    }

    func updateImage() {
        annotationImageView.image = renderAnnotationImage()
        annotationImageView.alpha = annotation?.alpha ?? 0
    }
}
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@interface PSCFixedSizeAnnotationView : PSPDFAnnotationView
@property (nonatomic, nullable) UIImageView *annotationImageView;
@end

@implementation PSCFixedSizeAnnotationView

- (void)setAnnotation:(PSPDFAnnotation *)annotation {
    super.annotation = annotation;

    if (!self.annotationImageView) {
        UIImageView *annotationImageView = [[UIImageView alloc] initWithFrame:self.bounds];
        annotationImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        annotationImageView.contentMode = UIViewContentModeScaleAspectFit;
        [self addSubview:annotationImageView];
        self.annotationImageView = annotationImageView;
    }
    [self updateImage];
}

- (void)setZoomScale:(CGFloat)zoomScale {
    _zoomScale = zoomScale;
    CGFloat scale = fmax(1.f, zoomScale);
    self.annotationImageView.transform = CGAffineTransformMakeScale(1 / scale, 1 / scale);
}

- (UIImage *)renderAnnotationImage {
    CGSize fixedSize = self.annotation.fixedSize;
    return [self.annotation imageWithSize:fixedSize withOptions:nil];
}

- (void)annotationChangedNotification:(NSNotification *)notification {
    [super annotationChangedNotification:notification];
    [self updateImage];
}

- (void)updateImage {
    self.annotationImageView.image = self.renderAnnotationImage;
    self.annotationImageView.alpha = self.annotation.alpha;
}

@end

To make PSPDFKit use this custom annotation view class you will need to conform to PSPDFViewControllerDelegate, implement pdfViewController:annotationView:forAnnotation:onPageView: and return an object of the custom annotation view instead of the default annotation view.

Copy
1
2
3
4
5
6
7
func pdfViewController(_ pdfController: PSPDFViewController, annotationView: UIView?, for annotation: PSPDFAnnotation, on pageView: PSPDFPageView) -> UIView? {
    if annotation is PSPDFStampAnnotation {
        let frame = annotation.boundingBox(forPageRect: pageView.bounds)
        return FixedSizeAnnotationView(frame: frame)
    }
    return annotationView
}
Copy
1
2
3
4
5
6
7
- (UIView<PSPDFAnnotationViewProtocol> *)pdfViewController:(PSPDFViewController *)pdfController annotationView:(UIView<PSPDFAnnotationViewProtocol> *)annotationView forAnnotation:(PSPDFAnnotation *)annotation onPageView:(PSPDFPageView *)pageView {
    if ([annotation isKindOfClass:PSPDFStampAnnotation.class]) {
        CGRect frame = [annotation boundingBoxForPageRect:pageView.bounds];
        return [[PSCFixedSizeAnnotationView alloc] initWithFrame:frame];
    }
    return annotationView;
}

Finally, don't forget to override the custom annotation class with -[PSPDFConfigurationBuilder overrideClass:withClass:] to override the default class defined in PSPDFKit.

Please also have a look at the FloatingStampsExample.swift in the PSPDFCatalog example project to see this in action.

Was this page helpful? We're happy to answer any questions.