State Customization

PSPDFViewController can be in five different states:

  • Default (showing a PDF document)
  • Loading
  • Empty
  • Locked (password-protected PDF document)
  • Error

Take a look at PSPDFControllerState for more information.

You can change the state’s strings and images or you can set -[PSPDFViewController overlayViewController] to take care of state handling yourself.

To create an overlay view controller, you have to implement the PSPDFControllerStateHandling protocol in a UIViewController subclass.

Implementing all of the states, with the exception of locked, is pretty straightforward, because they don’t feature any interaction. To unlock documents, you need to add a text field and handle the keyboard accordingly. Use -PSPDFDocument unlockWithPassword: to unlock the document. After that, you need to reload the PSPDFViewController with reloadData.

The following code snippets should help you create your own overlay view controller correctly:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class OverlayViewController: UIViewController, PSPDFControllerStateHandling {

    // MARK: Properties

    weak var pdfController: PSPDFViewController!

    private let label: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.textColor = .gray
        label.textAlignment = .center
        return label
    }()

    private let textField: UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.isSecureTextEntry = true
        textField.autocorrectionType = .no
        textField.autocapitalizationType = .none
        textField.borderStyle = .roundedRect
        return textField
    }()

    private let button: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Unlock", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.sizeToFit()
        return button
    }()

    // MARK: UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(unlock), for: .touchUpInside)

        let stackView = UIStackView(arrangedSubviews: [label, textField, button])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.distribution = .fillEqually
        stackView.spacing = 20
        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.widthAnchor.constraint(equalToConstant: 300),
            stackView.heightAnchor.constraint(equalToConstant: 150),
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    // MARK: Button Actions

    @objc
    private func unlock(sender: UIButton) {
        guard let document = document, let password = textField.text else { return }
        document.unlock(withPassword: password)
        pdfController.reloadData()
    }

    // MARK: PSPDFControllerStateHandling

    var document: PSPDFDocument?

    public func setControllerState(_ state: PSPDFControllerState, error: Error?, animated: Bool) {
        var text = ""
        var backgroundColor: UIColor? = .white

        switch state {
        case .default:
            backgroundColor = nil
        case .empty:
            text = "No document set"
        case .loading:
            text = "Loading..."
        case .locked:
            text = "Password:"
        case .error:
            text = "Unable to display document:\n\(error!.localizedDescription)"
        }

        label.text = text
        view.backgroundColor = backgroundColor
        view.isUserInteractionEnabled = state != .default

        if state == .locked {
            textField.isHidden = false
            button.isHidden = false
            textField.becomeFirstResponder()
        } else {
            textField.isHidden = true
            button.isHidden = true
            textField.resignFirstResponder()
        }
    }
}
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@interface OverlayViewController : UIViewController <PSPDFControllerStateHandling>

@property (nonatomic, weak) PSPDFViewController *pdfController;

@end

@interface OverlayViewController ()

@property (nonatomic) UILabel *label;
@property (nonatomic) UITextField *textField;
@property (nonatomic) UIButton *button;

@end

@implementation OverlayViewController

#pragma mark - UIViewController

-(void)viewDidLoad {
    [super viewDidLoad];

    self.label = [UILabel new];
    self.label.translatesAutoresizingMaskIntoConstraints = NO;
    self.label.numberOfLines = 0;
    self.label.textColor = [UIColor grayColor];
    self.label.textAlignment = NSTextAlignmentCenter;

    self.textField = [UITextField new];
    self.textField.translatesAutoresizingMaskIntoConstraints = NO;
    self.textField.secureTextEntry = YES;
    self.textField.autocorrectionType = UITextAutocorrectionTypeNo;
    self.textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.textField.borderStyle = UITextBorderStyleRoundedRect;

    self.button = [UIButton buttonWithType:UIButtonTypeSystem];
    self.button.translatesAutoresizingMaskIntoConstraints = NO;
    [self.button setTitle:@"Unlock" forState:UIControlStateNormal];
    [self.button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [self.button sizeToFit];
    [self.button addTarget:self action:@selector(unlock:) forControlEvents:UIControlEventTouchUpInside];

    UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[self.label, self.textField, self.button]];
    stackView.translatesAutoresizingMaskIntoConstraints = NO;
    stackView.axis = UILayoutConstraintAxisVertical;
    stackView.distribution = UIStackViewDistributionFillEqually;
    stackView.spacing = 20;
    [self.view addSubview:stackView];

    [NSLayoutConstraint activateConstraints:@[
        [stackView.widthAnchor constraintEqualToConstant:300],
        [stackView.heightAnchor constraintEqualToConstant:150],
        [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
        [stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]
    ]];
}

#pragma mark - Button Actions

- (void)unlock:(id)sender {
    NSString *password = self.textField.text;
    [self.document unlockWithPassword:password];
    [self.pdfController reloadData];
}

#pragma mark - PSPDFControllerStateHandling

@synthesize document;

-(void)setControllerState:(PSPDFControllerState)state error:(NSError *)error animated:(BOOL)animated {
    NSString *text = @"";
    UIColor *backgroundColor = [UIColor whiteColor];

    switch (state) {
        case PSPDFControllerStateDefault:
            backgroundColor = nil;
            break;

        case PSPDFControllerStateEmpty:
            text = @"No document set";
            break;

        case PSPDFControllerStateLoading:
            text = @"Loading...";
            break;

        case PSPDFControllerStateLocked:
            text = @"Password:";
            break;

        case PSPDFControllerStateError:
            text = [NSString stringWithFormat:@"Unable to display document:\n%@", error.localizedDescription];
            break;

        default:
            break;
    }

    self.label.text = text;
    self.view.backgroundColor = backgroundColor;
    self.view.userInteractionEnabled = state != PSPDFControllerStateDefault;

    if (state == PSPDFControllerStateLocked) {
        self.textField.hidden = NO;
        self.button.hidden = NO;
        [self.textField becomeFirstResponder];
    } else {
        self.textField.hidden = YES;
        self.button.hidden = YES;
        [self.textField resignFirstResponder];
    }
}

@end
Copy
1
2
3
4
let pdfController = PSPDFViewController(document: document)
let overlayViewController = OverlayViewController()
overlayViewController.pdfController = pdfController
pdfController.overlayViewController = overlayViewController
Copy
1
2
3
4
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];
OverlayViewController *overlayViewController = [OverlayViewController new];
overlayViewController.pdfController = pdfController;
pdfController.overlayViewController = overlayViewController;