// // CPDFTextViewController.m // PDFViewer // // Created by kdan on 2022/11/19. // #import "PDFTextWatermarkController.h" #import "PDFWatermarkTextView.h" #import "PDFWatermarkDataModel.h" #import "PDFWatermarkDrawView.h" #import "PDFWatermarkClipView.h" #import "Masonry.h" #import "PDFWatermarkControllerHeader.h" @interface PDFTextWatermarkController () @property (nonatomic,strong) UIAlertController *alertController; @property (nonatomic,strong) PDFWatermarkDrawView *drawView; @property (nonatomic,strong) PDFWatermarkClipView *cliView; @property (nonatomic,assign) CGRect normalWatermarkRect; @property (nonatomic,strong) UILabel *shadowWaterLabel; @end @implementation PDFTextWatermarkController #pragma mark - UIViewController Methods - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _textPreview = [[PDFWatermarkTextPreview alloc] init]; _textView = [[PDFWatermarkTextView alloc] init]; _textView.verticalField.delegate = self; _textView.horizontalField.delegate = self; _alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Watermark Content", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; [self.view addSubview:_textPreview]; [self.view addSubview:_textView]; [self.view setBackgroundColor:UIColor.grayColor]; [self addConstraint]; [self initWatermarkView]; [self addTargets]; [self createGestureRecognizer]; _shadowWaterLabel = [[UILabel alloc] init]; _shadowWaterLabel.text = _textPreview.preLabel.text; } #pragma mark - Initializers - (void)initDataModel { self.dataModel.textColor = UIColor.blackColor; self.dataModel.watermarkOpacity = 1; self.dataModel.text = @"Watermark"; self.dataModel.watermarkScale = 17.0 / 24.0; self.dataModel.isTile = NO; self.dataModel.watermarkRotation = 0; self.dataModel.verticalSpacing = 100; self.dataModel.horizontalSpacing = 100; } - (void)initWatermarkView { _textPreview.preLabel.text = self.dataModel.text; _textView.horizontalField.text = 0; _textView.verticalField.text = 0; _textView.horizontalField.enabled = NO; _textView.verticalField.enabled = NO; } - (void)addConstraint { [_textPreview mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.width.equalTo(self.view.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(-205); }]; [_textView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(_textPreview.mas_bottom).offset(0); make.width.equalTo(_textPreview.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(0); }]; } - (void)addTargets { for (NSInteger i = 0; i < _textView.colorArray.count; ++i) { [_textView.colorArray[i] addTarget:self action:@selector(onColorBtnClicked:) forControlEvents:UIControlEventTouchDown]; } __block PDFTextWatermarkController *strongBlock = self; [_alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { }]; [_alertController addAction:[UIAlertAction actionWithTitle:@"Done" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { strongBlock.dataModel.text = strongBlock.alertController.textFields.firstObject.text; strongBlock.textPreview.preLabel.text = strongBlock.alertController.textFields.firstObject.text; [strongBlock.textPreview.preLabel sizeToFit]; }]]; [_alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { strongBlock.alertController.view.hidden = YES; }]]; [_textView.opacitySlider addTarget:self action:@selector(onOpacityChanged:) forControlEvents:UIControlEventValueChanged]; [_textView.textScaleSlider addTarget:self action:@selector(onTextScaleChanged:) forControlEvents:UIControlEventValueChanged]; [_textView.tileSwitch addTarget:self action:@selector(onTileSwitchChanged:) forControlEvents:UIControlEventValueChanged]; [_textView.pageBtn addTarget:self action:@selector(onSelectPageRange:) forControlEvents:UIControlEventTouchDown]; [_textView.horizontalField addTarget:self action:@selector(horizontalChange:) forControlEvents:UIControlEventEditingDidEnd]; [_textView.verticalField addTarget:self action:@selector(verticalChage:) forControlEvents:UIControlEventEditingDidEnd]; } #pragma mark - UI Functions - (void)onColorBtnClicked:(UIButton *)sender { _textPreview.preLabel.textColor = sender.backgroundColor; [self.dataModel setTextColor:sender.backgroundColor]; } - (void)onOpacityChanged:(UISlider *)sender { _textPreview.preLabel.alpha = 1 - sender.value; [self.dataModel setWatermarkOpacity:1 - sender.value]; } - (void)onTextScaleChanged:(UISlider *)sender { if (!self.dataModel.isTile) { sender.minimumValue = 12; sender.maximumValue = 36; _textPreview.preLabel.font = [_textPreview.preLabel.font fontWithSize:sender.value]; self.shadowWaterLabel.font = [_textPreview.preLabel.font fontWithSize:sender.value]; self.shadowWaterLabel.center = _textPreview.preLabel.center; [_textPreview.preLabel sizeToFit]; [self.shadowWaterLabel sizeToFit]; [self.dataModel setWatermarkScale:sender.value / 24]; } self.normalWatermarkRect = self.shadowWaterLabel.frame; } - (void)onTileSwitchChanged:(UISwitch *) sender { if ([sender isOn]) { self.dataModel.isTile = YES; _textView.horizontalField.enabled = YES; _textView.verticalField.enabled = YES; _textView.textScaleSlider.enabled = NO; self.drawView = [[PDFWatermarkDrawView alloc] initWithFrame:self.textPreview.bounds]; if (_shadowWaterLabel.frame.size.height == 0) { _shadowWaterLabel.frame = _textPreview.preLabel.frame; } _normalWatermarkRect = _shadowWaterLabel.frame; [self.drawView setDataModel:self.dataModel]; [self.drawView setWaterLabelRect:self.normalWatermarkRect]; [self.drawView setDocumentViewRect:self.textPreview.documentImageView.frame]; [self.view addSubview:self.drawView]; [self.textPreview bringSubviewToFront:self.drawView]; self.cliView = [[PDFWatermarkClipView alloc] initWithFrame:self.textPreview.bounds]; [self.cliView setDocumentRect:self.textPreview.documentImageView.frame]; [self.view addSubview:self.cliView]; [self.drawView bringSubviewToFront:self.cliView]; } else { self.dataModel.isTile = NO; _textView.horizontalField.enabled = NO; _textView.verticalField.enabled = NO; _textView.textScaleSlider.enabled = YES; [self.drawView removeFromSuperview]; [self.cliView removeFromSuperview]; } } - (void)horizontalChange:(UITextField *)text { [self.drawView setHorizontal:[text.text floatValue]]; self.dataModel.horizontalSpacing = [text.text floatValue]; [self.drawView setNeedsDisplay]; } - (void)verticalChage:(UITextField *)text { [self.drawView setVertical:[text.text floatValue]]; self.dataModel.verticalSpacing = [text.text floatValue]; [self.drawView setNeedsDisplay]; } #pragma mark - Gesture - (void)createGestureRecognizer { [_textPreview.documentImageView setUserInteractionEnabled:YES]; [_textPreview.preLabel setUserInteractionEnabled:YES]; [_textPreview.rotationBtn setUserInteractionEnabled:YES]; UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapWatermarkLabel:)]; [_textPreview.preLabel addGestureRecognizer:tapGestureRecognizer]; UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panWatermarkLabel:)]; [_textPreview.preLabel addGestureRecognizer:panRecognizer]; UIPanGestureRecognizer *panRotationBtnRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(rotationWatermarkLabel:)]; [_textPreview.rotationBtn addGestureRecognizer:panRotationBtnRecognizer]; } - (void)tapWatermarkLabel:(UITapGestureRecognizer *)recognizer { _alertController.view.hidden = NO; _alertController.textFields.firstObject.text = _textPreview.preLabel.text; [self presentViewController:_alertController animated:true completion:nil]; } - (void)panWatermarkLabel:(UIPanGestureRecognizer *)recognizer { CGPoint point = [recognizer translationInView:_textPreview.documentImageView]; CGRect documentFrame = _textPreview.documentImageView.frame; documentFrame.origin.x -= _textPreview.documentImageView.frame.origin.x; documentFrame.origin.y -= _textPreview.documentImageView.frame.origin.y; [_textPreview.preLabel setCenter:CGPointMake(_textPreview.preLabel.center.x + point.x, _textPreview.preLabel.center.y + point.y)]; [_textPreview.rotationBtn setCenter:CGPointMake(_textPreview.rotationBtn.center.x + point.x, _textPreview.rotationBtn.center.y + point.y)]; if (!CGRectContainsRect(documentFrame,_textPreview.preLabel.frame)) { [_textPreview.preLabel setCenter:CGPointMake(_textPreview.preLabel.center.x - point.x, _textPreview.preLabel.center.y - point.y)]; [_textPreview.rotationBtn setCenter:CGPointMake(_textPreview.rotationBtn.center.x - point.x, _textPreview.rotationBtn.center.y - point.y)]; } self.dataModel.tx = _textPreview.preLabel.center.x - (_textPreview.documentImageView.center.x - _textPreview.documentImageView.frame.origin.x); self.dataModel.ty = _textPreview.documentImageView.center.y - _textPreview.documentImageView.frame.origin.y - _textPreview.preLabel.center.y; [recognizer setTranslation:CGPointZero inView:_textPreview.documentImageView]; self.shadowWaterLabel.center = _textPreview.preLabel.center; self.normalWatermarkRect = self.shadowWaterLabel.frame; } - (void)rotationWatermarkLabel:(UIPanGestureRecognizer *)recognizer { CGPoint point = [recognizer translationInView:_textPreview]; CGFloat radian = atan2(point.x + _textPreview.center.x - _textPreview.preLabel.center.x, point.y + _textPreview.center.y - _textPreview.preLabel.center.y); _textPreview.preLabel.transform = CGAffineTransformMakeRotation(-radian); self.dataModel.watermarkRotation = (-radian) * 180 / M_PI; } #pragma mark - Orientation - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight || [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) { [self addSideConstraint]; } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationPortrait) { [self addNormalConstraint]; } if ([self.drawView isDescendantOfView:self.view]) { _textView.tileSwitch.on = NO; [self.cliView removeFromSuperview]; [self.drawView removeFromSuperview]; } } - (void)addSideConstraint { [_textView mas_remakeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(self.view.mas_right).offset(-38); make.left.equalTo(_textPreview.mas_right).offset(0); make.height.equalTo(@205); make.width.equalTo(@(self.view.bounds.size.width)); make.bottom.equalTo(self.view.mas_bottom).offset(-40); }]; [_textPreview mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.right.equalTo(_textView.mas_left).offset(-5); make.left.equalTo(self.view.mas_left).offset(0); make.bottom.equalTo(self.view.mas_bottom).offset(0); }]; if ([self.drawView isDescendantOfView:self.view]) { [self.drawView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.right.equalTo(_textView.mas_left).offset(-5); make.left.equalTo(self.view.mas_left).offset(0); make.bottom.equalTo(self.view.mas_bottom).offset(0); }]; [self.cliView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.right.equalTo(_textView.mas_left).offset(-5); make.left.equalTo(self.view.mas_left).offset(0); make.bottom.equalTo(self.view.mas_bottom).offset(0); }]; } } - (void)addNormalConstraint { [_textPreview mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.width.equalTo(self.view.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(-205); }]; [_textView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(_textPreview.mas_bottom).offset(0); make.width.equalTo(_textPreview.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(0); }]; if ([self.drawView isDescendantOfView:self.view]) { [self.drawView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.width.equalTo(self.view.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(-205); }]; [self.cliView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).offset(0); make.width.equalTo(self.view.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(-205); }]; } } #pragma mark - CPDFClipTextPreviewDelegate - (void)clipText:(CGContextRef)context { CGContextSetFillColorWithColor(context, [UIColor grayColor].CGColor); CGContextFillRect(context, self.textPreview.bounds); CGContextClearRect(context, self.textPreview.documentImageView.frame); } #pragma mark - UITextFieldDelegate - (void)textFieldDidBeginEditing:(UITextField *)textField { self.cliView.hidden = YES; self.drawView.hidden = YES; if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight || [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft || [UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown) { } else { if (_textView.frame.origin.y > (self.view.frame.size.height - 300)) { [_textView mas_remakeConstraints:^(MASConstraintMaker *make) { make.height.mas_equalTo(205); make.width.equalTo(_textPreview.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(-300); }]; [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^ { self.textView.center = CGPointMake(self.textView.center.x, self.textView.center.y - 300); } completion:nil]; } } } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; self.cliView.hidden = NO; self.drawView.hidden = NO; if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight || [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft || [UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown) { } else { if(_textView.frame.origin.y < self.view.frame.size.height) { [_textView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(_textPreview.mas_bottom).offset(0); make.width.equalTo(_textPreview.mas_width); make.bottom.equalTo(self.view.mas_bottom).offset(0); make.height.mas_equalTo(205); }]; [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^ { self.textView.center = CGPointMake(self.textView.center.x, self.textView.center.y + 300); } completion:nil]; } } return YES; } - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if (_textView.horizontalField == textField || _textView.verticalField == textField) { return [self validateValue:string]; } return YES; } @end