// // PDFSearchViewController.m // PDFReader // // Copyright © 2014-2022 PDF Technologies, Inc. All Rights Reserved. // // The PDF Reader Sample applications are licensed with a modified BSD license. // Please see License for details. This notice may not be removed from this file. // #import "PDFSearchViewController.h" #import "PDFFindTextView.h" #define kTextSearch_Content_Length_Max 100 @interface PDFSearchViewCell : UITableViewCell @property (nonatomic,assign) IBOutlet UILabel *label; @end @implementation PDFSearchViewCell @end @interface PDFSearchViewController () @property (nonatomic,assign) IBOutlet UITableView *tableView; @property (nonatomic,assign) IBOutlet UISearchBar *searchBar; @property (nonatomic,assign) IBOutlet UIView *pageView; @property (nonatomic,assign) IBOutlet UITextField *textField; @property (nonatomic,assign) IBOutlet UIView *titleView; @property (nonatomic,assign) IBOutlet UIButton *searchButton; @property (nonatomic,assign) IBOutlet UIButton *pageButton; @property (nonatomic,assign) IBOutlet UIView *indicator; @property (nonatomic,retain) IBOutlet PDFFindTextView *findTextView; @property (nonatomic, retain) UIActivityIndicatorView* loadingView; @property (nonatomic,retain) NSArray *resultArray; @property (nonatomic,retain) NSIndexPath *selecetdIndexPath; @end @implementation PDFSearchViewController #pragma mark - Init Methods - (void)dealloc { [_loadingView release]; [_findTextView release]; [_selecetdIndexPath release]; [_resultArray release]; [super dealloc]; } #pragma mark - UIViewController Methods - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. self.title = NSLocalizedString(@"Search", nil); self.preferredContentSize = CGSizeMake(320, 480); [self.tableView registerNib:[UINib nibWithNibName:@"PDFSearchViewCell" bundle:nil] forCellReuseIdentifier:@"cell"]; self.navigationItem.titleView = self.titleView; [self buttonItemClicked_Text:nil]; __block __typeof(self) blockSelf = self; self.findTextView.callback = ^(NSInteger index) { if (index == 0) { [blockSelf buttonItemClicked_Previous:nil]; } else if (index == 1) { [blockSelf buttonItemClicked_Next:nil]; } else { [blockSelf.findTextView removeFromSuperview]; [blockSelf.pdfViewController.pdfView setHighlightedSelection:nil animated:NO]; } }; } - (UIActivityIndicatorView *)loadingView { if (!_loadingView) { _loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; _loadingView.center = self.view.center; _loadingView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; } return _loadingView; } #pragma mark - Button Actions - (IBAction)buttonItemClicked_Text:(id)sender { CGRect frame = self.indicator.frame; CGPoint center = self.indicator.center; frame.size.width = self.searchButton.frame.size.width+20; center.x = self.searchButton.center.x; self.indicator.frame = frame; self.indicator.center = center; self.tableView.hidden = NO; self.pageView.hidden = YES; self.searchBar.keyboardType = UIKeyboardTypeDefault; self.searchBar.placeholder = NSLocalizedString(@"Search Text", nil); [self.searchBar becomeFirstResponder]; self.navigationItem.rightBarButtonItem = nil; } - (IBAction)buttonItemClicked_Page:(id)sender { CGRect frame = self.indicator.frame; CGPoint center = self.indicator.center; frame.size.width = self.pageButton.frame.size.width+20; center.x = self.pageButton.center.x; self.indicator.frame = frame; self.indicator.center = center; self.tableView.hidden = YES; self.pageView.hidden = NO; [self.textField setReturnKeyType:UIReturnKeyGo]; self.textField.clearButtonMode = UITextFieldViewModeWhileEditing; [self.textField setKeyboardType:UIKeyboardTypeNumberPad]; self.textField.placeholder = [NSString stringWithFormat:NSLocalizedString(@"%d - %d", nil), 1, self.pdfViewController.pdfView.document.pageCount]; [self.textField becomeFirstResponder]; UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(buttonItemClicked_Done:)]; self.navigationItem.rightBarButtonItem = doneItem; [doneItem release]; } - (void)buttonItemClicked_Done:(id)sender { [self dismissViewControllerAnimated:YES completion:^{ NSUInteger page = [self.textField.text integerValue]-1; [self.pdfViewController.pdfView goToPageIndex:page animated:NO]; }]; } - (void)buttonItemClicked_Previous:(id)sender { NSInteger section = 0; NSInteger row = 0; if (self.selecetdIndexPath.row == 0) { if (self.selecetdIndexPath.section == 0) { section = self.tableView.numberOfSections-1; } else { section = self.selecetdIndexPath.section-1; } row = [self.tableView numberOfRowsInSection:section]-1; } else { section = self.selecetdIndexPath.section; row = self.selecetdIndexPath.row-1; } self.selecetdIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; if (section >= self.resultArray.count) { return; } CPDFSelection *selection = self.resultArray[section][row]; NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page]; [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO]; [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES]; } - (void)buttonItemClicked_Next:(id)sender { NSInteger section = 0; NSInteger row = 0; NSInteger count = [self.tableView numberOfRowsInSection:self.selecetdIndexPath.section]; if (self.selecetdIndexPath.row == count-1) { if (self.selecetdIndexPath.section == self.tableView.numberOfSections-1) { section = 0; } else { section = self.selecetdIndexPath.section+1; } row = 0; } else { section = self.selecetdIndexPath.section; row = self.selecetdIndexPath.row+1; } self.selecetdIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; if (section >= self.resultArray.count) { return; } CPDFSelection *selection = self.resultArray[section][row]; NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page]; [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO]; [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES]; } #pragma mark - Private Methods - (NSAttributedString *)attributedString:(NSString *)desContent keyword:(NSString *)string location:(NSInteger)location { NSString *lowerContents = [desContent lowercaseString]; NSString *keyword = [string lowercaseString]; NSRange keywordRange = [lowerContents rangeOfString:keyword]; NSInteger index = 0; while (keywordRange.location != NSNotFound) { if (index == location) { break; } index++; NSInteger start = keywordRange.location+keywordRange.length; NSRange searchRange = NSMakeRange(start, lowerContents.length - start); keywordRange = [lowerContents rangeOfString:keyword options:NSCaseInsensitiveSearch range:searchRange]; } NSString* contents = nil; if (desContent.length < kTextSearch_Content_Length_Max) { contents = desContent; contents = [NSString stringWithFormat:@"%@...",contents]; } else { NSInteger offset = (kTextSearch_Content_Length_Max - keywordRange.length)/2.0; offset = keywordRange.location - offset; if (offset < 0) { contents = [desContent substringWithRange:NSMakeRange(0, kTextSearch_Content_Length_Max)]; contents = [NSString stringWithFormat:@"%@...",contents]; } else { if (offset >= [desContent length]) { offset = 0; } NSInteger tLen = 0; if (desContent.length - offset > kTextSearch_Content_Length_Max) { tLen = kTextSearch_Content_Length_Max; } else { tLen = desContent.length - offset; } contents = [desContent substringWithRange:NSMakeRange(offset, tLen)]; contents = [NSString stringWithFormat:@"%@...",contents]; keywordRange.location = keywordRange.location - offset; } } NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease]; NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc]init] autorelease]; paragraphStyle.firstLineHeadIndent = 10.0; paragraphStyle.headIndent = 10.0; paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; NSDictionary* dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:@"HelveticaNeue-Medium" size:14.0],NSFontAttributeName,[UIColor colorWithRed:25.0/255.0 green:25.0/255.0 blue:25.0/255.0 alpha:1.0],NSForegroundColorAttributeName,paragraphStyle,NSParagraphStyleAttributeName,nil]; NSRange range = [[attrString string] rangeOfString:[attrString string]]; [attrString setAttributes:dic range:range]; dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor colorWithRed:1.0 green:220.0/255.0 blue:27.0/255.0 alpha:1.0],NSBackgroundColorAttributeName,nil]; [attrString addAttributes:dic range:keywordRange]; return attrString; } #pragma mark - Public Methods - (void)showViewController:(UIViewController *)viewController inBarButtonItem:(UIBarButtonItem *)barButtonItem { UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self]; nav.modalPresentationStyle = UIModalPresentationPopover; UIPopoverPresentationController *popVC = nav.popoverPresentationController; popVC.delegate = self; popVC.barButtonItem = barButtonItem; popVC.permittedArrowDirections = UIPopoverArrowDirectionUp; [viewController presentViewController:nav animated:YES completion:nil]; [nav release]; } #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { return UIModalPresentationNone; } - (void)popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController { if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) { [self.pdfViewController.pdfView setHighlightedSelection:nil animated:NO]; } } #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; if (![[CPDFKit sharedInstance] allowsFeature:CPDFKitFeatureViewerSearch]) { UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:NSLocalizedString(@"Your license does not support this feature, please upgrade your license privilege.", nil) preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:cancelAction]; [self presentViewController:alert animated:YES completion:nil]; return; } NSString *string = searchBar.text; if ([string length] < 1) { return; } if (![self.loadingView superview]) { [self.view addSubview:self.loadingView]; } [self.loadingView startAnimating]; __block __typeof(self) block_self = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSArray *results = [block_self.pdfViewController.pdfView.document findString:string withOptions:CPDFSearchCaseInsensitive]; dispatch_async(dispatch_get_main_queue(), ^{ [block_self.loadingView removeFromSuperview]; block_self.resultArray = results; [block_self.tableView reloadData]; if ([block_self.resultArray count] < 1) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:NSLocalizedString(@"Text not found!", nil) preferredStyle:UIAlertControllerStyleAlert]; UIViewController *tRootViewControl = [UIApplication sharedApplication].keyWindow.rootViewController; if ([tRootViewControl presentedViewController]) { tRootViewControl = [tRootViewControl presentedViewController]; } [tRootViewControl presentViewController:alert animated:YES completion:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [alert dismissViewControllerAnimated:YES completion:nil]; }); } }); }); } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { self.resultArray = nil; [self.tableView reloadData]; } #pragma mark - UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField { [self buttonItemClicked_Done:nil]; return YES; } #pragma mark - UITableViewDatasource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [self.resultArray count]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSArray *array = [self.resultArray objectAtIndex:section]; return [array count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { PDFSearchViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; CPDFSelection *selection = self.resultArray[indexPath.section][indexPath.row]; NSInteger location = selection.range.location - 20; NSInteger length = selection.range.length + 40; if (location < 0) { location = 0; length = selection.range.location + selection.range.length + 20; } NSString *searchContent = [selection.page stringForRange:NSMakeRange(location, length)]; cell.label.attributedText = [self attributedString:searchContent keyword:self.searchBar.text location:location]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 30; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { NSArray *array = [self.resultArray objectAtIndex:section]; CPDFSelection *selection = array.firstObject; NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page]; NSString *pageStr = [NSString stringWithFormat:NSLocalizedString(@"Page %d",nil), (long)(pageIndex+1)]; NSString *countStr = [NSString stringWithFormat:@"%@", @(array.count)]; UITableViewHeaderFooterView *view = [[[UITableViewHeaderFooterView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)] autorelease]; view.autoresizingMask = UIViewAutoresizingFlexibleWidth; view.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1.0]; UILabel *label = [[[UILabel alloc] init] autorelease]; label.font = [UIFont boldSystemFontOfSize:16]; label.text = pageStr; [label sizeToFit]; label.frame = CGRectMake(10, 0, label.bounds.size.width, view.bounds.size.height); [view.contentView addSubview:label]; UILabel *sublabel = [[[UILabel alloc] init] autorelease]; sublabel.font = [UIFont systemFontOfSize:14]; sublabel.text = countStr; sublabel.textColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0]; [sublabel sizeToFit]; sublabel.frame = CGRectMake(view.bounds.size.width-sublabel.bounds.size.width-10, 0, sublabel.bounds.size.width, view.bounds.size.height); sublabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; [view.contentView addSubview:sublabel]; return view; } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; CPDFSelection *selection = self.resultArray[indexPath.section][indexPath.row]; if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) { NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page]; [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO]; [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES]; } else { [self dismissViewControllerAnimated:YES completion:^{ NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page]; [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO]; [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES]; self.selecetdIndexPath = indexPath; self.findTextView.center = CGPointMake(self.pdfViewController.view.frame.size.width/2.0, self.pdfViewController.view.frame.size.height-self.findTextView.frame.size.height/2.0-60); [self.pdfViewController.view addSubview:self.findTextView]; }]; } } @end