// // CPDFTextViewController.m // PDFViewer // // Created by kdan on 2022/11/19. // #import "PDFTextViewController.h" #import "PDFTextView.h" #import "PDFDataModel.h" #import "PDFDrawView.h" #import "PDFClipView.h" #import "Masonry.h" #import "PDFWatermarkControllerHeader.h" @interface PDFTextViewController () @property (nonatomic,strong) UIAlertController *alertController; @property (nonatomic,strong) PDFDrawView *drawView; @property (nonatomic,strong) PDFClipView *cliView; @property (nonatomic,assign) CGRect normalWatermarkRect; @property (nonatomic,strong) UILabel *shadowWaterLabel; @end @implementation PDFTextViewController #pragma mark - UIViewController Methods - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _textPreview = [[PDFTextPreview alloc] init]; _textView = [[PDFTextView alloc] init]; _textView.verticalField.delegate = self; _textView.horizontalField.delegate = self; _alertController = [UIAlertController alertControllerWithTitle:@"Watermark Content" message:nil preferredStyle:UIAlertControllerStyleAlert]; [self.view addSubview:_textPreview]; [self.view addSubview:_textView]; [self.view setBackgroundColor:UIColor.systemGray5Color]; [self addConstraint]; [self initWatermarkView]; [self addTargets]; [self createGestureRecognizer]; _normalWatermarkRect = CGRectMake(69.666, 138.333, 88, 20.333); _shadowWaterLabel = [[UILabel alloc] initWithFrame:CGRectMake(69.666, 138.333, 88, 20.333)]; _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 PDFTextViewController *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 = [[PDFDrawView alloc] initWithFrame:self.textPreview.bounds]; [self.drawView setDataModel:self.dataModel]; [self.drawView setWaterLabelRect:self.normalWatermarkRect]; [self.drawView setDocumentViewRect:self.textPreview.documentView.frame]; [self.view addSubview:self.drawView]; [self.textPreview bringSubviewToFront:self.drawView]; self.cliView = [[PDFClipView alloc] initWithFrame:self.textPreview.bounds]; [self.cliView setDocumentRect:self.textPreview.documentView.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.documentView 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.documentView]; CGRect documentFrame = _textPreview.documentView.frame; documentFrame.origin.x -= _textPreview.documentView.frame.origin.x; documentFrame.origin.y -= _textPreview.documentView.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.documentView.center.x - _textPreview.documentView.frame.origin.x); self.dataModel.ty = _textPreview.documentView.center.y - _textPreview.documentView.frame.origin.y - _textPreview.preLabel.center.y; [recognizer setTranslation:CGPointZero inView:_textPreview.documentView]; 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 systemGray5Color].CGColor); CGContextFillRect(context, self.textPreview.bounds); CGContextClearRect(context, self.textPreview.documentView.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 { [_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 { [_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