浏览代码

add NCViewerPDFSearch

marinofaggiana 5 年之前
父节点
当前提交
72e1ab900c

+ 17 - 1
Nextcloud.xcodeproj/project.pbxproj

@@ -365,6 +365,8 @@
 		F769454822E9F20D000A798A /* NCShareNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454722E9F20D000A798A /* NCShareNetworking.swift */; };
 		F769454822E9F20D000A798A /* NCShareNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454722E9F20D000A798A /* NCShareNetworking.swift */; };
 		F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
 		F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
 		F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
 		F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
+		F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */; };
+		F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */; };
 		F771E3D320E2392D00AFB62D /* FileProviderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D220E2392D00AFB62D /* FileProviderExtension.swift */; };
 		F771E3D320E2392D00AFB62D /* FileProviderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D220E2392D00AFB62D /* FileProviderExtension.swift */; };
 		F771E3D520E2392D00AFB62D /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D420E2392D00AFB62D /* FileProviderItem.swift */; };
 		F771E3D520E2392D00AFB62D /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D420E2392D00AFB62D /* FileProviderItem.swift */; };
 		F771E3D720E2392D00AFB62D /* FileProviderEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D620E2392D00AFB62D /* FileProviderEnumerator.swift */; };
 		F771E3D720E2392D00AFB62D /* FileProviderEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3D620E2392D00AFB62D /* FileProviderEnumerator.swift */; };
@@ -1028,6 +1030,8 @@
 		F76C3B841C6388BC00DC4301 /* CCGraphics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGraphics.m; sourceTree = "<group>"; };
 		F76C3B841C6388BC00DC4301 /* CCGraphics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGraphics.m; sourceTree = "<group>"; };
 		F76C3B871C638A4C00DC4301 /* CCError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCError.h; sourceTree = "<group>"; };
 		F76C3B871C638A4C00DC4301 /* CCError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCError.h; sourceTree = "<group>"; };
 		F76C3B881C638A4C00DC4301 /* CCError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCError.m; sourceTree = "<group>"; };
 		F76C3B881C638A4C00DC4301 /* CCError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCError.m; sourceTree = "<group>"; };
+		F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerPDFSearch.swift; sourceTree = "<group>"; };
+		F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerPDFSearchCell.xib; sourceTree = "<group>"; };
 		F76E71E42244DF6900690001 /* Zip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Zip.framework; path = Carthage/Build/iOS/Zip.framework; sourceTree = "<group>"; };
 		F76E71E42244DF6900690001 /* Zip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Zip.framework; path = Carthage/Build/iOS/Zip.framework; sourceTree = "<group>"; };
 		F76F23321ED4600700C40023 /* Share-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Share-Bridging-Header.h"; sourceTree = "<group>"; };
 		F76F23321ED4600700C40023 /* Share-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Share-Bridging-Header.h"; sourceTree = "<group>"; };
 		F771E3D020E2392D00AFB62D /* File Provider Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "File Provider Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
 		F771E3D020E2392D00AFB62D /* File Provider Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "File Provider Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1999,6 +2003,16 @@
 			path = Section;
 			path = Section;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		F76D3CEF2428B3DD005DFA87 /* NCViewerPDF */ = {
+			isa = PBXGroup;
+			children = (
+				F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */,
+				F710D1F42405770F00A6033D /* NCViewerPDF.swift */,
+				F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */,
+			);
+			path = NCViewerPDF;
+			sourceTree = "<group>";
+		};
 		F771E3D120E2392D00AFB62D /* File Provider Extension */ = {
 		F771E3D120E2392D00AFB62D /* File Provider Extension */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -2094,7 +2108,7 @@
 			children = (
 			children = (
 				F79018B1240962C7007C9B6D /* NCViewerImage */,
 				F79018B1240962C7007C9B6D /* NCViewerImage */,
 				F72D404823D2082500A97FD0 /* NCViewerNextcloudText.swift */,
 				F72D404823D2082500A97FD0 /* NCViewerNextcloudText.swift */,
-				F710D1F42405770F00A6033D /* NCViewerPDF.swift */,
+				F76D3CEF2428B3DD005DFA87 /* NCViewerPDF */,
 				F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */,
 				F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */,
 				F7FB1D3D215E191D00D669EA /* NCViewerDocumentWeb.swift */,
 				F7FB1D3D215E191D00D669EA /* NCViewerDocumentWeb.swift */,
 				F79630ED215527D40015EEA5 /* NCViewerVideo.swift */,
 				F79630ED215527D40015EEA5 /* NCViewerVideo.swift */,
@@ -2891,6 +2905,7 @@
 				F710E8111EF95C9C00DC2427 /* ImagesIntro.xcassets in Resources */,
 				F710E8111EF95C9C00DC2427 /* ImagesIntro.xcassets in Resources */,
 				F760F78621F21F61006B1A73 /* LaunchScreen.storyboard in Resources */,
 				F760F78621F21F61006B1A73 /* LaunchScreen.storyboard in Resources */,
 				F7381EE5218218C9000B1560 /* NCOffline.storyboard in Resources */,
 				F7381EE5218218C9000B1560 /* NCOffline.storyboard in Resources */,
+				F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */,
 				F769453E22E9E97E000A798A /* NCShareUserMenuView.xib in Resources */,
 				F769453E22E9E97E000A798A /* NCShareUserMenuView.xib in Resources */,
 				F749C10E23C4A5340027D966 /* NCIntroCollectionViewCell.xib in Resources */,
 				F749C10E23C4A5340027D966 /* NCIntroCollectionViewCell.xib in Resources */,
 				F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */,
 				F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */,
@@ -3212,6 +3227,7 @@
 				F732BA061D76CE1500E9878B /* CCNetworking.m in Sources */,
 				F732BA061D76CE1500E9878B /* CCNetworking.m in Sources */,
 				F762CB061EACB66200B38484 /* XLFormTextViewCell.m in Sources */,
 				F762CB061EACB66200B38484 /* XLFormTextViewCell.m in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
+				F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */,
 				F78ACD4F2190440D0088454D /* NCLayout.swift in Sources */,
 				F78ACD4F2190440D0088454D /* NCLayout.swift in Sources */,
 				F760F79C21F21F61006B1A73 /* GradientView.swift in Sources */,
 				F760F79C21F21F61006B1A73 /* GradientView.swift in Sources */,
 				F73F537F1E929C8500F8678D /* CCMore.swift in Sources */,
 				F73F537F1E929C8500F8678D /* CCMore.swift in Sources */,

+ 0 - 0
iOSClient/Viewer/NCViewerPDF.swift → iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift


+ 169 - 0
iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift

@@ -0,0 +1,169 @@
+//
+//  NCViewerPDFSearch.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 23/03/2020.
+//  Copyright © 2020 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import PDFKit
+
+@available(iOS 11, *)
+
+@objc protocol NCViewerPDFSearchDelegate : class {
+    func searchPdfSelection(_ pdfSelection: PDFSelection)
+}
+
+@available(iOS 11, *)
+
+class NCViewerPDFSearch: UITableViewController, UISearchBarDelegate, PDFDocumentDelegate {
+    
+    var searchBar = UISearchBar()
+    var pdfDocument: PDFDocument?
+    var searchResultArray = [PDFSelection]()
+    
+    weak var delegate: NCViewerPDFSearchDelegate?
+    
+    //MARK: - LifeCycle
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        searchBar.delegate = self
+        searchBar.sizeToFit()
+        searchBar.searchBarStyle = .minimal
+        
+        navigationItem.titleView = searchBar
+        
+        tableView.dataSource = self
+        tableView.delegate = self
+        tableView.register(UINib.init(nibName: "NCViewerPDFSearchCell", bundle: nil), forCellReuseIdentifier: "Cell")
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        searchBar.becomeFirstResponder()
+    }
+    
+    //MARK: - UITableView DataSource / Delegate
+    
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 82
+    }
+    
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+    
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return self.searchResultArray.count
+    }
+    
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        
+        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! NCViewerPDFSearchCell
+        let pdfSelection = searchResultArray[indexPath.row] as PDFSelection
+        if let pdfOutline = pdfDocument?.outlineItem(for: pdfSelection) {
+            cell.outlineLabel.text = pdfOutline.label
+        }
+        let pdfPage = pdfSelection.pages.first
+        cell.pageNumberLabel.text = "Page: " + (pdfPage?.label ?? "")
+        
+        let extendSelection = pdfSelection.copy() as! PDFSelection
+        extendSelection.extend(atStart: 10)
+        extendSelection.extend(atEnd: 90)
+        extendSelection.extendForLineBoundaries()
+        
+        let nsRange = NSString(string: extendSelection.string!).range(of: pdfSelection.string!, options: String.CompareOptions.caseInsensitive)
+        if nsRange.location != NSNotFound {
+            
+            let attributedSubString = NSAttributedString.init(string: NSString(string: extendSelection.string!).substring(with: nsRange), attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 17)])
+            let attributedString = NSMutableAttributedString.init(string: extendSelection.string!)
+            attributedString.replaceCharacters(in: nsRange, with: attributedSubString)
+            cell.searchResultTextLabel.attributedText = attributedString
+        }
+        
+        return cell
+    }
+    
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        let pdfSelection = searchResultArray[indexPath.row]
+        delegate?.searchPdfSelection(pdfSelection)
+        dismiss(animated: true)
+    }
+    
+    //MARK: - PDFSelection Delegate
+    
+    func didMatchString(_ instance: PDFSelection) {
+        searchResultArray.append(instance)
+        tableView.reloadData()
+    }
+    
+    //MARK: - UIScrollView Delegate
+    
+    override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+        searchBar.resignFirstResponder()
+    }
+    
+    //MARK: - UISearchBarDelegate
+    
+    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
+        searchBar.resignFirstResponder()
+    }
+    
+    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
+        pdfDocument?.cancelFindString()
+        navigationItem.setRightBarButton(nil, animated: false)
+        dismiss(animated: true)
+    }
+    
+    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
+        if searchText.count < 2 { return }
+        
+        searchResultArray.removeAll()
+        tableView.reloadData()
+        pdfDocument?.cancelFindString()
+        pdfDocument?.delegate = self
+        pdfDocument?.beginFindString(searchText, withOptions: .caseInsensitive)
+    }
+    
+    func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
+        
+        let cancelBarButtonItem = UIBarButtonItem.init(title: "Cancel", style: .plain, target: self, action: #selector(cancelBarButtonItemClicked))
+        navigationItem.setRightBarButton(cancelBarButtonItem, animated: true)
+        return true
+    }
+    
+    @objc func cancelBarButtonItemClicked() {
+        searchBarCancelButtonClicked(searchBar)
+    }
+}
+
+class NCViewerPDFSearchCell: UITableViewCell {
+    
+    @IBOutlet weak var outlineLabel: UILabel!
+    @IBOutlet weak var pageNumberLabel: UILabel!
+    @IBOutlet weak var searchResultTextLabel: UILabel!
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+}
+

+ 66 - 0
iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearchCell.xib

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="KGk-i7-Jjw" customClass="NCViewerPDFSearchCell" customModule="Nextcloud" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Outline" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E3H-Rb-4ds">
+                        <rect key="frame" x="16" y="2" width="234" height="21"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="21" id="hgI-GG-pNA"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                        <color key="textColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="123" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IPV-cv-CwC">
+                        <rect key="frame" x="252" y="3.5" width="60" height="18"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="60" id="r7H-Es-IdO"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                        <color key="textColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Search result text can be multiple line too" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Lr-ib-Zu4">
+                        <rect key="frame" x="16" y="25" width="296" height="55"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="7Lr-ib-Zu4" firstAttribute="top" secondItem="E3H-Rb-4ds" secondAttribute="bottom" constant="2" id="Gxv-JM-Hcf"/>
+                    <constraint firstAttribute="bottom" secondItem="7Lr-ib-Zu4" secondAttribute="bottom" constant="2" id="KkH-X8-AfP"/>
+                    <constraint firstItem="7Lr-ib-Zu4" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="SVW-5G-2lQ"/>
+                    <constraint firstItem="E3H-Rb-4ds" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="2" id="gGE-Jo-XR5"/>
+                    <constraint firstItem="IPV-cv-CwC" firstAttribute="centerY" secondItem="E3H-Rb-4ds" secondAttribute="centerY" id="nVx-9j-a9B"/>
+                    <constraint firstAttribute="trailing" secondItem="IPV-cv-CwC" secondAttribute="trailing" constant="8" id="paH-z6-Sss"/>
+                    <constraint firstItem="E3H-Rb-4ds" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="qoY-iB-BD9"/>
+                    <constraint firstAttribute="trailing" secondItem="7Lr-ib-Zu4" secondAttribute="trailing" constant="8" id="wh5-DZ-WGF"/>
+                    <constraint firstItem="IPV-cv-CwC" firstAttribute="leading" secondItem="E3H-Rb-4ds" secondAttribute="trailing" constant="2" id="zVp-GO-gLK"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
+            <connections>
+                <outlet property="outlineLabel" destination="E3H-Rb-4ds" id="636-BP-coH"/>
+                <outlet property="pageNumberLabel" destination="IPV-cv-CwC" id="iV9-cx-ytj"/>
+                <outlet property="searchResultTextLabel" destination="7Lr-ib-Zu4" id="Apc-28-dqe"/>
+            </connections>
+            <point key="canvasLocation" x="-61" y="47"/>
+        </tableViewCell>
+    </objects>
+</document>