PDFSearchViewController.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. //
  2. // PDFSearchViewController.m
  3. // PDFReader
  4. //
  5. // Copyright © 2014-2022 PDF Technologies, Inc. All Rights Reserved.
  6. //
  7. // The PDF Reader Sample applications are licensed with a modified BSD license.
  8. // Please see License for details. This notice may not be removed from this file.
  9. //
  10. #import "PDFSearchViewController.h"
  11. #import "PDFFindTextView.h"
  12. #define kTextSearch_Content_Length_Max 100
  13. @interface PDFSearchViewCell : UITableViewCell
  14. @property (nonatomic,assign) IBOutlet UILabel *label;
  15. @end
  16. @implementation PDFSearchViewCell
  17. @end
  18. @interface PDFSearchViewController () <UIPopoverPresentationControllerDelegate>
  19. @property (nonatomic,assign) IBOutlet UITableView *tableView;
  20. @property (nonatomic,assign) IBOutlet UISearchBar *searchBar;
  21. @property (nonatomic,assign) IBOutlet UIView *pageView;
  22. @property (nonatomic,assign) IBOutlet UITextField *textField;
  23. @property (nonatomic,assign) IBOutlet UIView *titleView;
  24. @property (nonatomic,assign) IBOutlet UIButton *searchButton;
  25. @property (nonatomic,assign) IBOutlet UIButton *pageButton;
  26. @property (nonatomic,assign) IBOutlet UIView *indicator;
  27. @property (nonatomic,retain) IBOutlet PDFFindTextView *findTextView;
  28. @property (nonatomic, retain) UIActivityIndicatorView* loadingView;
  29. @property (nonatomic,retain) NSArray *resultArray;
  30. @property (nonatomic,retain) NSIndexPath *selecetdIndexPath;
  31. @end
  32. @implementation PDFSearchViewController
  33. #pragma mark - Init Methods
  34. - (void)dealloc {
  35. [_loadingView release];
  36. [_findTextView release];
  37. [_selecetdIndexPath release];
  38. [_resultArray release];
  39. [super dealloc];
  40. }
  41. #pragma mark - UIViewController Methods
  42. - (void)viewDidLoad {
  43. [super viewDidLoad];
  44. // Do any additional setup after loading the view from its nib.
  45. self.title = NSLocalizedString(@"Search", nil);
  46. self.preferredContentSize = CGSizeMake(320, 480);
  47. [self.tableView registerNib:[UINib nibWithNibName:@"PDFSearchViewCell" bundle:nil] forCellReuseIdentifier:@"cell"];
  48. self.navigationItem.titleView = self.titleView;
  49. [self buttonItemClicked_Text:nil];
  50. __block __typeof(self) blockSelf = self;
  51. self.findTextView.callback = ^(NSInteger index) {
  52. if (index == 0) {
  53. [blockSelf buttonItemClicked_Previous:nil];
  54. } else if (index == 1) {
  55. [blockSelf buttonItemClicked_Next:nil];
  56. } else {
  57. [blockSelf.findTextView removeFromSuperview];
  58. [blockSelf.pdfViewController.pdfView setHighlightedSelection:nil animated:NO];
  59. }
  60. };
  61. }
  62. - (UIActivityIndicatorView *)loadingView {
  63. if (!_loadingView) {
  64. _loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  65. _loadingView.center = self.view.center;
  66. _loadingView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  67. }
  68. return _loadingView;
  69. }
  70. #pragma mark - Button Actions
  71. - (IBAction)buttonItemClicked_Text:(id)sender {
  72. CGRect frame = self.indicator.frame;
  73. CGPoint center = self.indicator.center;
  74. frame.size.width = self.searchButton.frame.size.width+20;
  75. center.x = self.searchButton.center.x;
  76. self.indicator.frame = frame;
  77. self.indicator.center = center;
  78. self.tableView.hidden = NO;
  79. self.pageView.hidden = YES;
  80. self.searchBar.keyboardType = UIKeyboardTypeDefault;
  81. self.searchBar.placeholder = NSLocalizedString(@"Search Text", nil);
  82. [self.searchBar becomeFirstResponder];
  83. self.navigationItem.rightBarButtonItem = nil;
  84. }
  85. - (IBAction)buttonItemClicked_Page:(id)sender {
  86. CGRect frame = self.indicator.frame;
  87. CGPoint center = self.indicator.center;
  88. frame.size.width = self.pageButton.frame.size.width+20;
  89. center.x = self.pageButton.center.x;
  90. self.indicator.frame = frame;
  91. self.indicator.center = center;
  92. self.tableView.hidden = YES;
  93. self.pageView.hidden = NO;
  94. [self.textField setReturnKeyType:UIReturnKeyGo];
  95. self.textField.clearButtonMode = UITextFieldViewModeWhileEditing;
  96. [self.textField setKeyboardType:UIKeyboardTypeNumberPad];
  97. self.textField.placeholder = [NSString stringWithFormat:NSLocalizedString(@"%d - %d", nil), 1, self.pdfViewController.pdfView.document.pageCount];
  98. [self.textField becomeFirstResponder];
  99. UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
  100. target:self
  101. action:@selector(buttonItemClicked_Done:)];
  102. self.navigationItem.rightBarButtonItem = doneItem;
  103. [doneItem release];
  104. }
  105. - (void)buttonItemClicked_Done:(id)sender {
  106. [self dismissViewControllerAnimated:YES completion:^{
  107. NSUInteger page = [self.textField.text integerValue]-1;
  108. [self.pdfViewController.pdfView goToPageIndex:page animated:NO];
  109. }];
  110. }
  111. - (void)buttonItemClicked_Previous:(id)sender {
  112. NSInteger section = 0;
  113. NSInteger row = 0;
  114. if (self.selecetdIndexPath.row == 0) {
  115. if (self.selecetdIndexPath.section == 0) {
  116. section = self.tableView.numberOfSections-1;
  117. } else {
  118. section = self.selecetdIndexPath.section-1;
  119. }
  120. row = [self.tableView numberOfRowsInSection:section]-1;
  121. } else {
  122. section = self.selecetdIndexPath.section;
  123. row = self.selecetdIndexPath.row-1;
  124. }
  125. self.selecetdIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
  126. if (section >= self.resultArray.count) {
  127. return;
  128. }
  129. CPDFSelection *selection = self.resultArray[section][row];
  130. NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page];
  131. [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO];
  132. [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES];
  133. }
  134. - (void)buttonItemClicked_Next:(id)sender {
  135. NSInteger section = 0;
  136. NSInteger row = 0;
  137. NSInteger count = [self.tableView numberOfRowsInSection:self.selecetdIndexPath.section];
  138. if (self.selecetdIndexPath.row == count-1) {
  139. if (self.selecetdIndexPath.section == self.tableView.numberOfSections-1) {
  140. section = 0;
  141. } else {
  142. section = self.selecetdIndexPath.section+1;
  143. }
  144. row = 0;
  145. } else {
  146. section = self.selecetdIndexPath.section;
  147. row = self.selecetdIndexPath.row+1;
  148. }
  149. self.selecetdIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
  150. if (section >= self.resultArray.count) {
  151. return;
  152. }
  153. CPDFSelection *selection = self.resultArray[section][row];
  154. NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page];
  155. [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO];
  156. [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES];
  157. }
  158. #pragma mark - Private Methods
  159. - (NSAttributedString *)attributedString:(NSString *)desContent keyword:(NSString *)string location:(NSInteger)location {
  160. NSString *lowerContents = [desContent lowercaseString];
  161. NSString *keyword = [string lowercaseString];
  162. NSRange keywordRange = [lowerContents rangeOfString:keyword];
  163. NSInteger index = 0;
  164. while (keywordRange.location != NSNotFound) {
  165. if (index == location) {
  166. break;
  167. }
  168. index++;
  169. NSInteger start = keywordRange.location+keywordRange.length;
  170. NSRange searchRange = NSMakeRange(start, lowerContents.length - start);
  171. keywordRange = [lowerContents rangeOfString:keyword options:NSCaseInsensitiveSearch range:searchRange];
  172. }
  173. NSString* contents = nil;
  174. if (desContent.length < kTextSearch_Content_Length_Max) {
  175. contents = desContent;
  176. contents = [NSString stringWithFormat:@"%@...",contents];
  177. } else {
  178. NSInteger offset = (kTextSearch_Content_Length_Max - keywordRange.length)/2.0;
  179. offset = keywordRange.location - offset;
  180. if (offset < 0) {
  181. contents = [desContent substringWithRange:NSMakeRange(0, kTextSearch_Content_Length_Max)];
  182. contents = [NSString stringWithFormat:@"%@...",contents];
  183. } else {
  184. if (offset >= [desContent length]) {
  185. offset = 0;
  186. }
  187. NSInteger tLen = 0;
  188. if (desContent.length - offset > kTextSearch_Content_Length_Max) {
  189. tLen = kTextSearch_Content_Length_Max;
  190. } else {
  191. tLen = desContent.length - offset;
  192. }
  193. contents = [desContent substringWithRange:NSMakeRange(offset, tLen)];
  194. contents = [NSString stringWithFormat:@"%@...",contents];
  195. keywordRange.location = keywordRange.location - offset;
  196. }
  197. }
  198. NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
  199. NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc]init] autorelease];
  200. paragraphStyle.firstLineHeadIndent = 10.0;
  201. paragraphStyle.headIndent = 10.0;
  202. paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
  203. 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];
  204. NSRange range = [[attrString string] rangeOfString:[attrString string]];
  205. [attrString setAttributes:dic range:range];
  206. dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor colorWithRed:1.0 green:220.0/255.0 blue:27.0/255.0 alpha:1.0],NSBackgroundColorAttributeName,nil];
  207. [attrString addAttributes:dic range:keywordRange];
  208. return attrString;
  209. }
  210. #pragma mark - Public Methods
  211. - (void)showViewController:(UIViewController *)viewController inBarButtonItem:(UIBarButtonItem *)barButtonItem {
  212. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self];
  213. nav.modalPresentationStyle = UIModalPresentationPopover;
  214. UIPopoverPresentationController *popVC = nav.popoverPresentationController;
  215. popVC.delegate = self;
  216. popVC.barButtonItem = barButtonItem;
  217. popVC.permittedArrowDirections = UIPopoverArrowDirectionUp;
  218. [viewController presentViewController:nav animated:YES completion:nil];
  219. [nav release];
  220. }
  221. #pragma mark - UIPopoverPresentationControllerDelegate
  222. - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection {
  223. return UIModalPresentationNone;
  224. }
  225. - (void)popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController {
  226. if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
  227. [self.pdfViewController.pdfView setHighlightedSelection:nil animated:NO];
  228. }
  229. }
  230. #pragma mark - UISearchBarDelegate
  231. - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
  232. [searchBar resignFirstResponder];
  233. if (![[CPDFKit sharedInstance] allowsFeature:CPDFKitFeatureViewerSearch]) {
  234. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
  235. style:UIAlertActionStyleCancel
  236. handler:nil];
  237. UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
  238. message:NSLocalizedString(@"Your license does not support this feature, please upgrade your license privilege.", nil)
  239. preferredStyle:UIAlertControllerStyleAlert];
  240. [alert addAction:cancelAction];
  241. [self presentViewController:alert animated:YES completion:nil];
  242. return;
  243. }
  244. NSString *string = searchBar.text;
  245. if ([string length] < 1) {
  246. return;
  247. }
  248. if (![self.loadingView superview]) {
  249. [self.view addSubview:self.loadingView];
  250. }
  251. [self.loadingView startAnimating];
  252. __block __typeof(self) block_self = self;
  253. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  254. NSArray *results = [block_self.pdfViewController.pdfView.document findString:string withOptions:CPDFSearchCaseInsensitive];
  255. dispatch_async(dispatch_get_main_queue(), ^{
  256. [block_self.loadingView removeFromSuperview];
  257. block_self.resultArray = results;
  258. [block_self.tableView reloadData];
  259. if ([block_self.resultArray count] < 1) {
  260. UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
  261. message:NSLocalizedString(@"Text not found!", nil)
  262. preferredStyle:UIAlertControllerStyleAlert];
  263. UIViewController *tRootViewControl = [UIApplication sharedApplication].keyWindow.rootViewController;
  264. if ([tRootViewControl presentedViewController]) {
  265. tRootViewControl = [tRootViewControl presentedViewController];
  266. }
  267. [tRootViewControl presentViewController:alert animated:YES completion:nil];
  268. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  269. [alert dismissViewControllerAnimated:YES completion:nil];
  270. });
  271. }
  272. });
  273. });
  274. }
  275. - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
  276. {
  277. self.resultArray = nil;
  278. [self.tableView reloadData];
  279. }
  280. #pragma mark - UITextFieldDelegate
  281. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  282. [self buttonItemClicked_Done:nil];
  283. return YES;
  284. }
  285. #pragma mark - UITableViewDatasource
  286. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  287. return [self.resultArray count];
  288. }
  289. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  290. NSArray *array = [self.resultArray objectAtIndex:section];
  291. return [array count];
  292. }
  293. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  294. PDFSearchViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
  295. CPDFSelection *selection = self.resultArray[indexPath.section][indexPath.row];
  296. NSInteger location = selection.range.location - 20;
  297. NSInteger length = selection.range.length + 40;
  298. if (location < 0) {
  299. location = 0;
  300. length = selection.range.location + selection.range.length + 20;
  301. }
  302. NSString *searchContent = [selection.page stringForRange:NSMakeRange(location, length)];
  303. cell.label.attributedText = [self attributedString:searchContent keyword:self.searchBar.text location:location];
  304. return cell;
  305. }
  306. - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  307. return 30;
  308. }
  309. - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  310. NSArray *array = [self.resultArray objectAtIndex:section];
  311. CPDFSelection *selection = array.firstObject;
  312. NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page];
  313. NSString *pageStr = [NSString stringWithFormat:NSLocalizedString(@"Page %d",nil), (long)(pageIndex+1)];
  314. NSString *countStr = [NSString stringWithFormat:@"%@", @(array.count)];
  315. UITableViewHeaderFooterView *view = [[[UITableViewHeaderFooterView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)] autorelease];
  316. view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  317. view.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1.0];
  318. UILabel *label = [[[UILabel alloc] init] autorelease];
  319. label.font = [UIFont boldSystemFontOfSize:16];
  320. label.text = pageStr;
  321. [label sizeToFit];
  322. label.frame = CGRectMake(10, 0, label.bounds.size.width, view.bounds.size.height);
  323. [view.contentView addSubview:label];
  324. UILabel *sublabel = [[[UILabel alloc] init] autorelease];
  325. sublabel.font = [UIFont systemFontOfSize:14];
  326. sublabel.text = countStr;
  327. sublabel.textColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0];
  328. [sublabel sizeToFit];
  329. sublabel.frame = CGRectMake(view.bounds.size.width-sublabel.bounds.size.width-10, 0,
  330. sublabel.bounds.size.width, view.bounds.size.height);
  331. sublabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  332. [view.contentView addSubview:sublabel];
  333. return view;
  334. }
  335. #pragma mark - UITableViewDelegate
  336. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  337. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  338. CPDFSelection *selection = self.resultArray[indexPath.section][indexPath.row];
  339. if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
  340. NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page];
  341. [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO];
  342. [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES];
  343. } else {
  344. [self dismissViewControllerAnimated:YES completion:^{
  345. NSInteger pageIndex = [self.pdfViewController.pdfView.document indexForPage:selection.page];
  346. [self.pdfViewController.pdfView goToPageIndex:pageIndex animated:NO];
  347. [self.pdfViewController.pdfView setHighlightedSelection:selection animated:YES];
  348. self.selecetdIndexPath = indexPath;
  349. 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);
  350. [self.pdfViewController.view addSubview:self.findTextView];
  351. }];
  352. }
  353. }
  354. @end