├── .gitignore ├── .swift-version ├── LICENSE.txt ├── PDFReader.podspec ├── PDFReader.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── PDFReader.xcscheme ├── PDFReader ├── Demo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ExamplePDFs │ │ ├── apple.pdf │ │ ├── javaScript.pdf │ │ └── mongodb.pdf │ ├── Info.plist │ └── StartViewController.swift ├── Info.plist └── PDFReader.h ├── Package.swift ├── README.md ├── Screenshots ├── Screenshot1.png └── Screenshot2.png └── Sources ├── Assets └── PDFReader.storyboard └── Classes ├── PDFDocument.swift ├── PDFPageCollectionViewCell.swift ├── PDFPageView.swift ├── PDFThumbnailCell.swift ├── PDFThumbnailCollectionViewController.swift ├── PDFViewController.swift └── TiledView.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Swift ### 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Pods/ 30 | 31 | # Carthage 32 | # 33 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 34 | # Carthage/Checkouts 35 | .DS_Store 36 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alua-Kinzhebayeva 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PDFReader.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "PDFReader" 3 | s.version = "2.5.1" 4 | s.license = { :type => "MIT" } 5 | s.homepage = "https://github.com/Alua-Kinzhebayeva/iOS-PDF-Reader" 6 | s.author = { "Alua Kinzhebayeva" => "alua.kinzhebayeva@gmail.com" } 7 | s.summary = "PDF Reader for iOS written in Swift" 8 | s.source = { :git => "https://github.com/Alua-Kinzhebayeva/iOS-PDF-Reader.git", :tag => s.version.to_s } 9 | s.ios.deployment_target = "8.0" 10 | s.source_files = "Sources/Classes/*.swift" 11 | s.resources = "Sources/Assets/*.storyboard" 12 | s.requires_arc = true 13 | end 14 | -------------------------------------------------------------------------------- /PDFReader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 14083FDD1D4A953900909913 /* PDFReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 14083FDC1D4A953900909913 /* PDFReader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 14083FEF1D4A956E00909913 /* PDFReader.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14083FE61D4A956E00909913 /* PDFReader.storyboard */; }; 12 | 14083FF01D4A956E00909913 /* PDFDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FE81D4A956E00909913 /* PDFDocument.swift */; }; 13 | 14083FF11D4A956E00909913 /* PDFPageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FE91D4A956E00909913 /* PDFPageCollectionViewCell.swift */; }; 14 | 14083FF21D4A956E00909913 /* PDFPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FEA1D4A956E00909913 /* PDFPageView.swift */; }; 15 | 14083FF31D4A956E00909913 /* PDFThumbnailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FEB1D4A956E00909913 /* PDFThumbnailCell.swift */; }; 16 | 14083FF41D4A956E00909913 /* PDFThumbnailCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FEC1D4A956E00909913 /* PDFThumbnailCollectionViewController.swift */; }; 17 | 14083FF51D4A956E00909913 /* PDFViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FED1D4A956E00909913 /* PDFViewController.swift */; }; 18 | 14083FF61D4A956E00909913 /* TiledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14083FEE1D4A956E00909913 /* TiledView.swift */; }; 19 | 1456DCC81E2AA9A8001C3793 /* PDFReader.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14083FD91D4A953900909913 /* PDFReader.framework */; }; 20 | 1456DCC91E2AA9A8001C3793 /* PDFReader.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14083FD91D4A953900909913 /* PDFReader.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 21 | 146B85301E306460005F427E /* javaScript.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 146B852E1E30633A005F427E /* javaScript.pdf */; }; 22 | 146D370F1DAB7E23004EEFA5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 146D370E1DAB7E23004EEFA5 /* AppDelegate.swift */; }; 23 | 146D37141DAB7E23004EEFA5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 146D37121DAB7E23004EEFA5 /* Main.storyboard */; }; 24 | 146D37161DAB7E23004EEFA5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 146D37151DAB7E23004EEFA5 /* Assets.xcassets */; }; 25 | 146D37191DAB7E23004EEFA5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 146D37171DAB7E23004EEFA5 /* LaunchScreen.storyboard */; }; 26 | 146D371F1DAB7EDF004EEFA5 /* StartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 146D371E1DAB7EDF004EEFA5 /* StartViewController.swift */; }; 27 | 146D37231DAB7F0F004EEFA5 /* apple.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 146D37211DAB7F0F004EEFA5 /* apple.pdf */; }; 28 | 146D37241DAB7F0F004EEFA5 /* mongodb.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 146D37221DAB7F0F004EEFA5 /* mongodb.pdf */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 1456DCCA1E2AA9A8001C3793 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 14083FD01D4A953900909913 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 14083FD81D4A953900909913; 37 | remoteInfo = PDFReader; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | 1456DCCC1E2AA9A8001C3793 /* Embed Frameworks */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = ""; 46 | dstSubfolderSpec = 10; 47 | files = ( 48 | 1456DCC91E2AA9A8001C3793 /* PDFReader.framework in Embed Frameworks */, 49 | ); 50 | name = "Embed Frameworks"; 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXCopyFilesBuildPhase section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 14083FD91D4A953900909913 /* PDFReader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PDFReader.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 14083FDC1D4A953900909913 /* PDFReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PDFReader.h; sourceTree = ""; }; 58 | 14083FDE1D4A953900909913 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 14083FE61D4A956E00909913 /* PDFReader.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = PDFReader.storyboard; sourceTree = ""; }; 60 | 14083FE81D4A956E00909913 /* PDFDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFDocument.swift; sourceTree = ""; }; 61 | 14083FE91D4A956E00909913 /* PDFPageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPageCollectionViewCell.swift; sourceTree = ""; }; 62 | 14083FEA1D4A956E00909913 /* PDFPageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFPageView.swift; sourceTree = ""; }; 63 | 14083FEB1D4A956E00909913 /* PDFThumbnailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFThumbnailCell.swift; sourceTree = ""; }; 64 | 14083FEC1D4A956E00909913 /* PDFThumbnailCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFThumbnailCollectionViewController.swift; sourceTree = ""; }; 65 | 14083FED1D4A956E00909913 /* PDFViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFViewController.swift; sourceTree = ""; }; 66 | 14083FEE1D4A956E00909913 /* TiledView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiledView.swift; sourceTree = ""; }; 67 | 146B852E1E30633A005F427E /* javaScript.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = javaScript.pdf; sourceTree = ""; }; 68 | 146D370C1DAB7E23004EEFA5 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 146D370E1DAB7E23004EEFA5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 70 | 146D37131DAB7E23004EEFA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 71 | 146D37151DAB7E23004EEFA5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 72 | 146D37181DAB7E23004EEFA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 73 | 146D371A1DAB7E23004EEFA5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74 | 146D371E1DAB7EDF004EEFA5 /* StartViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartViewController.swift; sourceTree = ""; }; 75 | 146D37211DAB7F0F004EEFA5 /* apple.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = apple.pdf; sourceTree = ""; }; 76 | 146D37221DAB7F0F004EEFA5 /* mongodb.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = mongodb.pdf; sourceTree = ""; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | 14083FD51D4A953900909913 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | 146D37091DAB7E23004EEFA5 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | 1456DCC81E2AA9A8001C3793 /* PDFReader.framework in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 14083FCF1D4A953900909913 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 14083FDB1D4A953900909913 /* PDFReader */, 102 | 146D370D1DAB7E23004EEFA5 /* Demo */, 103 | 14083FDA1D4A953900909913 /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 14083FDA1D4A953900909913 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 14083FD91D4A953900909913 /* PDFReader.framework */, 111 | 146D370C1DAB7E23004EEFA5 /* Demo.app */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | 14083FDB1D4A953900909913 /* PDFReader */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 14083FE41D4A956E00909913 /* Sources */, 120 | 14083FDC1D4A953900909913 /* PDFReader.h */, 121 | 14083FDE1D4A953900909913 /* Info.plist */, 122 | ); 123 | path = PDFReader; 124 | sourceTree = ""; 125 | }; 126 | 14083FE41D4A956E00909913 /* Sources */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 14083FE51D4A956E00909913 /* Assets */, 130 | 14083FE71D4A956E00909913 /* Classes */, 131 | ); 132 | path = Sources; 133 | sourceTree = SOURCE_ROOT; 134 | }; 135 | 14083FE51D4A956E00909913 /* Assets */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 14083FE61D4A956E00909913 /* PDFReader.storyboard */, 139 | ); 140 | path = Assets; 141 | sourceTree = ""; 142 | }; 143 | 14083FE71D4A956E00909913 /* Classes */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 14083FE81D4A956E00909913 /* PDFDocument.swift */, 147 | 14083FE91D4A956E00909913 /* PDFPageCollectionViewCell.swift */, 148 | 14083FEA1D4A956E00909913 /* PDFPageView.swift */, 149 | 14083FEB1D4A956E00909913 /* PDFThumbnailCell.swift */, 150 | 14083FEC1D4A956E00909913 /* PDFThumbnailCollectionViewController.swift */, 151 | 14083FED1D4A956E00909913 /* PDFViewController.swift */, 152 | 14083FEE1D4A956E00909913 /* TiledView.swift */, 153 | ); 154 | path = Classes; 155 | sourceTree = ""; 156 | }; 157 | 146D370D1DAB7E23004EEFA5 /* Demo */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 146D37201DAB7F0F004EEFA5 /* ExamplePDFs */, 161 | 146D371E1DAB7EDF004EEFA5 /* StartViewController.swift */, 162 | 146D370E1DAB7E23004EEFA5 /* AppDelegate.swift */, 163 | 146D37121DAB7E23004EEFA5 /* Main.storyboard */, 164 | 146D37151DAB7E23004EEFA5 /* Assets.xcassets */, 165 | 146D37171DAB7E23004EEFA5 /* LaunchScreen.storyboard */, 166 | 146D371A1DAB7E23004EEFA5 /* Info.plist */, 167 | ); 168 | name = Demo; 169 | path = PDFReader/Demo; 170 | sourceTree = ""; 171 | }; 172 | 146D37201DAB7F0F004EEFA5 /* ExamplePDFs */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 146B852E1E30633A005F427E /* javaScript.pdf */, 176 | 146D37211DAB7F0F004EEFA5 /* apple.pdf */, 177 | 146D37221DAB7F0F004EEFA5 /* mongodb.pdf */, 178 | ); 179 | path = ExamplePDFs; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXHeadersBuildPhase section */ 185 | 14083FD61D4A953900909913 /* Headers */ = { 186 | isa = PBXHeadersBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 14083FDD1D4A953900909913 /* PDFReader.h in Headers */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXHeadersBuildPhase section */ 194 | 195 | /* Begin PBXNativeTarget section */ 196 | 14083FD81D4A953900909913 /* PDFReader */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 14083FE11D4A953900909913 /* Build configuration list for PBXNativeTarget "PDFReader" */; 199 | buildPhases = ( 200 | 14083FD41D4A953900909913 /* Sources */, 201 | 14083FD51D4A953900909913 /* Frameworks */, 202 | 14083FD61D4A953900909913 /* Headers */, 203 | 14083FD71D4A953900909913 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | ); 209 | name = PDFReader; 210 | productName = PDFReader; 211 | productReference = 14083FD91D4A953900909913 /* PDFReader.framework */; 212 | productType = "com.apple.product-type.framework"; 213 | }; 214 | 146D370B1DAB7E23004EEFA5 /* Demo */ = { 215 | isa = PBXNativeTarget; 216 | buildConfigurationList = 146D371D1DAB7E23004EEFA5 /* Build configuration list for PBXNativeTarget "Demo" */; 217 | buildPhases = ( 218 | 146D37081DAB7E23004EEFA5 /* Sources */, 219 | 146D37091DAB7E23004EEFA5 /* Frameworks */, 220 | 146D370A1DAB7E23004EEFA5 /* Resources */, 221 | 1456DCCC1E2AA9A8001C3793 /* Embed Frameworks */, 222 | ); 223 | buildRules = ( 224 | ); 225 | dependencies = ( 226 | 1456DCCB1E2AA9A8001C3793 /* PBXTargetDependency */, 227 | ); 228 | name = Demo; 229 | productName = Demo; 230 | productReference = 146D370C1DAB7E23004EEFA5 /* Demo.app */; 231 | productType = "com.apple.product-type.application"; 232 | }; 233 | /* End PBXNativeTarget section */ 234 | 235 | /* Begin PBXProject section */ 236 | 14083FD01D4A953900909913 /* Project object */ = { 237 | isa = PBXProject; 238 | attributes = { 239 | LastSwiftUpdateCheck = 0800; 240 | LastUpgradeCheck = 1110; 241 | ORGANIZATIONNAME = mytrus; 242 | TargetAttributes = { 243 | 14083FD81D4A953900909913 = { 244 | CreatedOnToolsVersion = 7.3.1; 245 | LastSwiftMigration = 1110; 246 | ProvisioningStyle = Automatic; 247 | }; 248 | 146D370B1DAB7E23004EEFA5 = { 249 | CreatedOnToolsVersion = 8.0; 250 | LastSwiftMigration = 1000; 251 | ProvisioningStyle = Automatic; 252 | }; 253 | }; 254 | }; 255 | buildConfigurationList = 14083FD31D4A953900909913 /* Build configuration list for PBXProject "PDFReader" */; 256 | compatibilityVersion = "Xcode 10.0"; 257 | developmentRegion = en; 258 | hasScannedForEncodings = 0; 259 | knownRegions = ( 260 | en, 261 | Base, 262 | ); 263 | mainGroup = 14083FCF1D4A953900909913; 264 | productRefGroup = 14083FDA1D4A953900909913 /* Products */; 265 | projectDirPath = ""; 266 | projectRoot = ""; 267 | targets = ( 268 | 14083FD81D4A953900909913 /* PDFReader */, 269 | 146D370B1DAB7E23004EEFA5 /* Demo */, 270 | ); 271 | }; 272 | /* End PBXProject section */ 273 | 274 | /* Begin PBXResourcesBuildPhase section */ 275 | 14083FD71D4A953900909913 /* Resources */ = { 276 | isa = PBXResourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | 14083FEF1D4A956E00909913 /* PDFReader.storyboard in Resources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | 146D370A1DAB7E23004EEFA5 /* Resources */ = { 284 | isa = PBXResourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 146D37191DAB7E23004EEFA5 /* LaunchScreen.storyboard in Resources */, 288 | 146D37161DAB7E23004EEFA5 /* Assets.xcassets in Resources */, 289 | 146D37141DAB7E23004EEFA5 /* Main.storyboard in Resources */, 290 | 146B85301E306460005F427E /* javaScript.pdf in Resources */, 291 | 146D37241DAB7F0F004EEFA5 /* mongodb.pdf in Resources */, 292 | 146D37231DAB7F0F004EEFA5 /* apple.pdf in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXResourcesBuildPhase section */ 297 | 298 | /* Begin PBXSourcesBuildPhase section */ 299 | 14083FD41D4A953900909913 /* Sources */ = { 300 | isa = PBXSourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | 14083FF21D4A956E00909913 /* PDFPageView.swift in Sources */, 304 | 14083FF11D4A956E00909913 /* PDFPageCollectionViewCell.swift in Sources */, 305 | 14083FF01D4A956E00909913 /* PDFDocument.swift in Sources */, 306 | 14083FF51D4A956E00909913 /* PDFViewController.swift in Sources */, 307 | 14083FF41D4A956E00909913 /* PDFThumbnailCollectionViewController.swift in Sources */, 308 | 14083FF31D4A956E00909913 /* PDFThumbnailCell.swift in Sources */, 309 | 14083FF61D4A956E00909913 /* TiledView.swift in Sources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | 146D37081DAB7E23004EEFA5 /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | 146D370F1DAB7E23004EEFA5 /* AppDelegate.swift in Sources */, 318 | 146D371F1DAB7EDF004EEFA5 /* StartViewController.swift in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXTargetDependency section */ 325 | 1456DCCB1E2AA9A8001C3793 /* PBXTargetDependency */ = { 326 | isa = PBXTargetDependency; 327 | target = 14083FD81D4A953900909913 /* PDFReader */; 328 | targetProxy = 1456DCCA1E2AA9A8001C3793 /* PBXContainerItemProxy */; 329 | }; 330 | /* End PBXTargetDependency section */ 331 | 332 | /* Begin PBXVariantGroup section */ 333 | 146D37121DAB7E23004EEFA5 /* Main.storyboard */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | 146D37131DAB7E23004EEFA5 /* Base */, 337 | ); 338 | name = Main.storyboard; 339 | sourceTree = ""; 340 | }; 341 | 146D37171DAB7E23004EEFA5 /* LaunchScreen.storyboard */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | 146D37181DAB7E23004EEFA5 /* Base */, 345 | ); 346 | name = LaunchScreen.storyboard; 347 | sourceTree = ""; 348 | }; 349 | /* End PBXVariantGroup section */ 350 | 351 | /* Begin XCBuildConfiguration section */ 352 | 14083FDF1D4A953900909913 /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 357 | CLANG_ANALYZER_NONNULL = YES; 358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 359 | CLANG_CXX_LIBRARY = "libc++"; 360 | CLANG_ENABLE_MODULES = YES; 361 | CLANG_ENABLE_OBJC_ARC = YES; 362 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 363 | CLANG_WARN_BOOL_CONVERSION = YES; 364 | CLANG_WARN_COMMA = YES; 365 | CLANG_WARN_CONSTANT_CONVERSION = YES; 366 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 367 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INFINITE_RECURSION = YES; 371 | CLANG_WARN_INT_CONVERSION = YES; 372 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 374 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 377 | CLANG_WARN_STRICT_PROTOTYPES = YES; 378 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 379 | CLANG_WARN_UNREACHABLE_CODE = YES; 380 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 381 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 382 | COPY_PHASE_STRIP = NO; 383 | CURRENT_PROJECT_VERSION = 1; 384 | DEBUG_INFORMATION_FORMAT = dwarf; 385 | ENABLE_STRICT_OBJC_MSGSEND = YES; 386 | ENABLE_TESTABILITY = YES; 387 | GCC_C_LANGUAGE_STANDARD = gnu99; 388 | GCC_DYNAMIC_NO_PIC = NO; 389 | GCC_NO_COMMON_BLOCKS = YES; 390 | GCC_OPTIMIZATION_LEVEL = 0; 391 | GCC_PREPROCESSOR_DEFINITIONS = ( 392 | "DEBUG=1", 393 | "$(inherited)", 394 | ); 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 402 | MTL_ENABLE_DEBUG_INFO = YES; 403 | ONLY_ACTIVE_ARCH = YES; 404 | SDKROOT = iphoneos; 405 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 406 | SWIFT_VERSION = 3.0; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | VERSIONING_SYSTEM = "apple-generic"; 409 | VERSION_INFO_PREFIX = ""; 410 | }; 411 | name = Debug; 412 | }; 413 | 14083FE01D4A953900909913 /* Release */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | ALWAYS_SEARCH_USER_PATHS = NO; 417 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 418 | CLANG_ANALYZER_NONNULL = YES; 419 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 420 | CLANG_CXX_LIBRARY = "libc++"; 421 | CLANG_ENABLE_MODULES = YES; 422 | CLANG_ENABLE_OBJC_ARC = YES; 423 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_COMMA = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 428 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 429 | CLANG_WARN_EMPTY_BODY = YES; 430 | CLANG_WARN_ENUM_CONVERSION = YES; 431 | CLANG_WARN_INFINITE_RECURSION = YES; 432 | CLANG_WARN_INT_CONVERSION = YES; 433 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 434 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 435 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 437 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 438 | CLANG_WARN_STRICT_PROTOTYPES = YES; 439 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 440 | CLANG_WARN_UNREACHABLE_CODE = YES; 441 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 442 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 443 | COPY_PHASE_STRIP = NO; 444 | CURRENT_PROJECT_VERSION = 1; 445 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 446 | ENABLE_NS_ASSERTIONS = NO; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | GCC_C_LANGUAGE_STANDARD = gnu99; 449 | GCC_NO_COMMON_BLOCKS = YES; 450 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 451 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 452 | GCC_WARN_UNDECLARED_SELECTOR = YES; 453 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 454 | GCC_WARN_UNUSED_FUNCTION = YES; 455 | GCC_WARN_UNUSED_VARIABLE = YES; 456 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 457 | MTL_ENABLE_DEBUG_INFO = NO; 458 | SDKROOT = iphoneos; 459 | SWIFT_COMPILATION_MODE = wholemodule; 460 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 461 | SWIFT_VERSION = 3.0; 462 | TARGETED_DEVICE_FAMILY = "1,2"; 463 | VALIDATE_PRODUCT = YES; 464 | VERSIONING_SYSTEM = "apple-generic"; 465 | VERSION_INFO_PREFIX = ""; 466 | }; 467 | name = Release; 468 | }; 469 | 14083FE21D4A953900909913 /* Debug */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 473 | CODE_SIGN_STYLE = Automatic; 474 | DEFINES_MODULE = YES; 475 | DEVELOPMENT_TEAM = ""; 476 | DYLIB_COMPATIBILITY_VERSION = 1; 477 | DYLIB_CURRENT_VERSION = 1; 478 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 479 | INFOPLIST_FILE = PDFReader/Info.plist; 480 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 481 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 482 | LD_RUNPATH_SEARCH_PATHS = ( 483 | "$(inherited)", 484 | "@executable_path/Frameworks", 485 | "@loader_path/Frameworks", 486 | ); 487 | PRODUCT_BUNDLE_IDENTIFIER = com.mytrus.PDFReader; 488 | PRODUCT_NAME = "$(TARGET_NAME)"; 489 | PROVISIONING_PROFILE_SPECIFIER = ""; 490 | SKIP_INSTALL = YES; 491 | SWIFT_VERSION = 5.0; 492 | }; 493 | name = Debug; 494 | }; 495 | 14083FE31D4A953900909913 /* Release */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 499 | CODE_SIGN_STYLE = Automatic; 500 | DEFINES_MODULE = YES; 501 | DEVELOPMENT_TEAM = ""; 502 | DYLIB_COMPATIBILITY_VERSION = 1; 503 | DYLIB_CURRENT_VERSION = 1; 504 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 505 | INFOPLIST_FILE = PDFReader/Info.plist; 506 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 507 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 508 | LD_RUNPATH_SEARCH_PATHS = ( 509 | "$(inherited)", 510 | "@executable_path/Frameworks", 511 | "@loader_path/Frameworks", 512 | ); 513 | PRODUCT_BUNDLE_IDENTIFIER = com.mytrus.PDFReader; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | PROVISIONING_PROFILE_SPECIFIER = ""; 516 | SKIP_INSTALL = YES; 517 | SWIFT_VERSION = 5.0; 518 | }; 519 | name = Release; 520 | }; 521 | 146D371B1DAB7E23004EEFA5 /* Debug */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 525 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 526 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 527 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 528 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 529 | CODE_SIGN_STYLE = Automatic; 530 | DEVELOPMENT_TEAM = HC9R4J3HM5; 531 | INFOPLIST_FILE = "$(SRCROOT)/PDFReader/Demo/Info.plist"; 532 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 533 | LD_RUNPATH_SEARCH_PATHS = ( 534 | "$(inherited)", 535 | "@executable_path/Frameworks", 536 | ); 537 | PRODUCT_BUNDLE_IDENTIFIER = com.mytrus.Demo; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | PROVISIONING_PROFILE_SPECIFIER = ""; 540 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 541 | SWIFT_VERSION = 4.2; 542 | }; 543 | name = Debug; 544 | }; 545 | 146D371C1DAB7E23004EEFA5 /* Release */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 549 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 550 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 551 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 552 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 553 | CODE_SIGN_STYLE = Automatic; 554 | DEVELOPMENT_TEAM = HC9R4J3HM5; 555 | INFOPLIST_FILE = "$(SRCROOT)/PDFReader/Demo/Info.plist"; 556 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 557 | LD_RUNPATH_SEARCH_PATHS = ( 558 | "$(inherited)", 559 | "@executable_path/Frameworks", 560 | ); 561 | PRODUCT_BUNDLE_IDENTIFIER = com.mytrus.Demo; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | PROVISIONING_PROFILE_SPECIFIER = ""; 564 | SWIFT_VERSION = 4.2; 565 | }; 566 | name = Release; 567 | }; 568 | /* End XCBuildConfiguration section */ 569 | 570 | /* Begin XCConfigurationList section */ 571 | 14083FD31D4A953900909913 /* Build configuration list for PBXProject "PDFReader" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 14083FDF1D4A953900909913 /* Debug */, 575 | 14083FE01D4A953900909913 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | 14083FE11D4A953900909913 /* Build configuration list for PBXNativeTarget "PDFReader" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 14083FE21D4A953900909913 /* Debug */, 584 | 14083FE31D4A953900909913 /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | 146D371D1DAB7E23004EEFA5 /* Build configuration list for PBXNativeTarget "Demo" */ = { 590 | isa = XCConfigurationList; 591 | buildConfigurations = ( 592 | 146D371B1DAB7E23004EEFA5 /* Debug */, 593 | 146D371C1DAB7E23004EEFA5 /* Release */, 594 | ); 595 | defaultConfigurationIsVisible = 0; 596 | defaultConfigurationName = Release; 597 | }; 598 | /* End XCConfigurationList section */ 599 | }; 600 | rootObject = 14083FD01D4A953900909913 /* Project object */; 601 | } 602 | -------------------------------------------------------------------------------- /PDFReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PDFReader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PDFReader.xcodeproj/xcshareddata/xcschemes/PDFReader.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /PDFReader/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // PDFReader 4 | // 5 | // Created by ALUA KINZHEBAYEVA on 4/23/15. 6 | // Copyright (c) 2015 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | return true 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /PDFReader/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /PDFReader/Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PDFReader/Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PDFReader/Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 36 | 45 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /PDFReader/Demo/ExamplePDFs/apple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/f16b099f5d877716a0b09ea5af9f1b001e606e6d/PDFReader/Demo/ExamplePDFs/apple.pdf -------------------------------------------------------------------------------- /PDFReader/Demo/ExamplePDFs/javaScript.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/f16b099f5d877716a0b09ea5af9f1b001e606e6d/PDFReader/Demo/ExamplePDFs/javaScript.pdf -------------------------------------------------------------------------------- /PDFReader/Demo/ExamplePDFs/mongodb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/f16b099f5d877716a0b09ea5af9f1b001e606e6d/PDFReader/Demo/ExamplePDFs/mongodb.pdf -------------------------------------------------------------------------------- /PDFReader/Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /PDFReader/Demo/StartViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartViewController.swift 3 | // PDFReader 4 | // 5 | // Created by Ricardo Nunez on 7/8/16. 6 | // Copyright © 2016 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PDFReader 11 | 12 | /// Presents user with some documents which can be viewed 13 | internal final class StartViewController: UIViewController { 14 | /// Displays a smaller sized PDF document 15 | @IBAction private func showSmallPDFDocument() { 16 | let smallPDFDocumentName = "apple" 17 | if let doc = document(smallPDFDocumentName) { 18 | showDocument(doc) 19 | } else { 20 | print("Document named \(smallPDFDocumentName) not found in the file system") 21 | } 22 | } 23 | 24 | /// Displays a larger sized PDF document 25 | @IBAction private func showRemotePDFDocument() { 26 | let remotePDFDocumentURLPath = "http://devstreaming.apple.com/videos/wwdc/2016/201h1g4asm31ti2l9n1/201/201_internationalization_best_practices.pdf" 27 | if let remotePDFDocumentURL = URL(string: remotePDFDocumentURLPath), let doc = document(remotePDFDocumentURL) { 28 | showDocument(doc) 29 | } else { 30 | print("Document named \(remotePDFDocumentURLPath) not found") 31 | } 32 | } 33 | 34 | /// Displays an insanely large sized PDF document 35 | @IBAction private func showInsanelyLargePDFDocument() { 36 | let insanelyLargePDFDocumentName = "javaScript" 37 | if let doc = document(insanelyLargePDFDocumentName) { 38 | showDocument(doc) 39 | } else { 40 | print("Document named \(insanelyLargePDFDocumentName) not found in the file system") 41 | } 42 | } 43 | 44 | /// Initializes a document with the name of the pdf in the file system 45 | private func document(_ name: String) -> PDFDocument? { 46 | guard let documentURL = Bundle.main.url(forResource: name, withExtension: "pdf") else { return nil } 47 | return PDFDocument(url: documentURL) 48 | } 49 | 50 | /// Initializes a document with the data of the pdf 51 | private func document(_ data: Data) -> PDFDocument? { 52 | return PDFDocument(fileData: data, fileName: "Sample PDF") 53 | } 54 | 55 | /// Initializes a document with the remote url of the pdf 56 | private func document(_ remoteURL: URL) -> PDFDocument? { 57 | return PDFDocument(url: remoteURL) 58 | } 59 | 60 | 61 | /// Presents a document 62 | /// 63 | /// - parameter document: document to present 64 | /// 65 | /// Add `thumbnailsEnabled:false` to `createNew` to not load the thumbnails in the controller. 66 | private func showDocument(_ document: PDFDocument) { 67 | let image = UIImage(named: "") 68 | let controller = PDFViewController.createNew(with: document, title: "", actionButtonImage: image, actionStyle: .activitySheet) 69 | navigationController?.pushViewController(controller, animated: true) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /PDFReader/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /PDFReader/PDFReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // PDFReader.h 3 | // PDFReader 4 | // 5 | // Created by Ricardo Nunez on 7/28/16. 6 | // Copyright © 2016 mytrus. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PDFReader. 12 | FOUNDATION_EXPORT double PDFReaderVersionNumber; 13 | 14 | //! Project version string for PDFReader. 15 | FOUNDATION_EXPORT const unsigned char PDFReaderVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "PDFReader" 5 | ) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated in favor of [PDFKIT](https://developer.apple.com/documentation/pdfkit) for iOS 11+ apps 2 | 3 | # iOS-PDF-Reader 4 | [![Version](https://img.shields.io/cocoapods/v/PDFReader.svg?style=flat)](http://cocoapods.org/pods/PDFReader) 5 | [![License](https://img.shields.io/cocoapods/l/PDFReader.svg?style=flat)](http://cocoapods.org/pods/PDFReader) 6 | [![Platform](https://img.shields.io/cocoapods/p/PDFReader.svg?style=flat)](http://cocoapods.org/pods/PDFReader) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | 9 | PDF Reader for iOS written in Swift 10 | * Fast and lightweight 11 | * Thumbnail bar on the bottom to navigate to a specific page 12 | * Print button on the top right 13 | 14 | ![](https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/master/Screenshots/Screenshot1.png) 15 | ![](https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/master/Screenshots/Screenshot2.png) 16 | 17 | ## Requirements 18 | 19 | - iOS 8.0+ 20 | - Swift 5.0 21 | 22 | ## Installation 23 | 24 | ### CocoaPods 25 | 26 | To install it, simply add the following line to your **Podfile**: 27 | 28 | ```ruby 29 | pod 'PDFReader' 30 | ``` 31 | 32 | You will also need to make sure you're opting into using frameworks: 33 | 34 | ```ruby 35 | use_frameworks! 36 | ``` 37 | 38 | Then run `pod install` with CocoaPods 1.0 or newer. 39 | 40 | ### Carthage 41 | 42 | To install it, simply add the following line to your **Cartfile**: 43 | 44 | ```ogdl 45 | github "Alua-Kinzhebayeva/iOS-PDF-Reader" 46 | ``` 47 | 48 | Run `carthage update` to build the framework and drag the built `PDFReader.framework` into your Xcode project. 49 | 50 | ## Usage 51 | 52 | ### Import Framework 53 | ```swift 54 | import PDFReader 55 | ``` 56 | 57 | ### Create a PDFDocument 58 | 59 | ##### From a file URL 60 | ```swift 61 | let documentFileURL = Bundle.main.url(forResource: "Cupcakes", withExtension: "pdf")! 62 | let document = PDFDocument(url: documentFileURL)! 63 | ``` 64 | 65 | ##### From a remote URL 66 | ```swift 67 | let remotePDFDocumentURLPath = "http://devstreaming.apple.com/videos/wwdc/2016/201h1g4asm31ti2l9n1/201/201_internationalization_best_practices.pdf" 68 | let remotePDFDocumentURL = URL(string: remotePDFDocumentURLPath)! 69 | let document = PDFDocument(url: remotePDFDocumentURL)! 70 | ``` 71 | 72 | ##### From Data 73 | ```swift 74 | let document = PDFDocument(fileData: fileData, fileName: "Sample PDF")! 75 | ``` 76 | 77 | ### Display a PDFDocument 78 | ```swift 79 | let readerController = PDFViewController.createNew(with: document) 80 | navigationController?.pushViewController(readerController, animated: true) 81 | ``` 82 | 83 | ## Customizations 84 | 85 | #### Controller Title 86 | ```swift 87 | PDFViewController.createNew(with: document, title: "Favorite Cupcakes") 88 | ``` 89 | 90 | #### Background Color 91 | ```swift 92 | controller.backgroundColor = .white 93 | ``` 94 | 95 | #### Action Button Image and Action 96 | 97 | ##### Available Action Styles 98 | 99 | ```swift 100 | /// Action button style 101 | public enum ActionStyle { 102 | /// Brings up a print modal allowing user to print current PDF 103 | case print 104 | 105 | /// Brings up an activity sheet to share or open PDF in another app 106 | case activitySheet 107 | 108 | /// Performs a custom action 109 | case customAction((Void) -> ()) 110 | } 111 | ``` 112 | 113 | ```swift 114 | let actionButtonImage = UIImage(named: "cupcakeActionButtonImage") 115 | PDFViewController.createNew(with: document, title: "Favorite Cupcakes", actionButtonImage: actionButtonImage, actionStyle: .activitySheet) 116 | 117 | ``` 118 | 119 | #### Override the default backbutton 120 | 121 | ```swift 122 | /// Create a button to override the default behavior of the backbutton. In the below example we create a cancel button which will call our myCancelFunc method on tap. 123 | let myBackButton = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(self.myCancelFunc(_:))) 124 | /// Provide your button to createNew using the backButton parameter. The PDFViewController will then use your button instead of the default backbutton. 125 | PDFViewController.createNew(with: document, title: "Favorite Cupcakes", backButton: myBackButton) 126 | 127 | ``` 128 | 129 | #### Do not load the thumbnails in the controller 130 | 131 | ```swift 132 | let controller = PDFViewController.createNew(with: document, isThumbnailsEnabled: false) 133 | ``` 134 | 135 | 136 | #### Change scroll direction of the pages from left to right to top to bottom 137 | 138 | ```swift 139 | controller.scrollDirection = .vertical 140 | ``` 141 | 142 | 143 | ## Acknowledgements 144 | 145 | Inspired by PDF Reader https://github.com/vfr/Reader and Apple's example on TiledScrollView 146 | -------------------------------------------------------------------------------- /Screenshots/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/f16b099f5d877716a0b09ea5af9f1b001e606e6d/Screenshots/Screenshot1.png -------------------------------------------------------------------------------- /Screenshots/Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alua-Kinzhebayeva/iOS-PDF-Reader/f16b099f5d877716a0b09ea5af9f1b001e606e6d/Screenshots/Screenshot2.png -------------------------------------------------------------------------------- /Sources/Assets/PDFReader.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /Sources/Classes/PDFDocument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFDocument.swift 3 | // PDFReader 4 | // 5 | // Created by ALUA KINZHEBAYEVA on 4/19/15. 6 | // Copyright (c) 2015 AK. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | import UIKit 11 | 12 | /// PDF Document on the system to be interacted with 13 | public struct PDFDocument { 14 | /// Number of pages document contains 15 | public let pageCount: Int 16 | 17 | /// Name of the PDF file, used to display on navigation titles 18 | public let fileName: String 19 | 20 | /// File url where this document resides 21 | let fileURL: URL? 22 | 23 | /// File data of the document 24 | let fileData: Data 25 | 26 | /// Core Graphics representation of the document 27 | let coreDocument: CGPDFDocument 28 | 29 | /// Password of the document 30 | let password: String? 31 | 32 | /// Image cache with the page index and and image of the page 33 | let images = NSCache() 34 | 35 | /// Returns a newly initialized document which is located on the file system. 36 | /// 37 | /// - parameter url: file or remote URL of the PDF document 38 | /// - parameter password: password for the locked PDF 39 | /// 40 | /// - returns: A newly initialized `PDFDocument` 41 | public init?(url: URL, password: String? = nil) { 42 | guard let fileData = try? Data(contentsOf: url) else { return nil } 43 | 44 | self.init(fileData: fileData, fileURL: url, fileName: url.lastPathComponent, password: password) 45 | } 46 | 47 | /// Returns a newly initialized document from data representing a PDF 48 | /// 49 | /// - parameter fileData: data of the PDF document 50 | /// - parameter fileName: name of the PDF file 51 | /// - parameter password: password for the locked pdf 52 | /// 53 | /// - returns: A newly initialized `PDFDocument` 54 | public init?(fileData: Data, fileName: String, password: String? = nil) { 55 | self.init(fileData: fileData, fileURL: nil, fileName: fileName, password: password) 56 | } 57 | 58 | /// Returns a newly initialized document 59 | /// 60 | /// - parameter fileData: data of the PDF document 61 | /// - parameter fileURL: file URL where the PDF document exists on the file system 62 | /// - parameter fileName: name of the PDF file 63 | /// - parameter password: password for the locked PDF 64 | /// 65 | /// - returns: A newly initialized `PDFDocument` 66 | private init?(fileData: Data, fileURL: URL?, fileName: String, password: String?) { 67 | guard let provider = CGDataProvider(data: fileData as CFData) else { return nil } 68 | guard let coreDocument = CGPDFDocument(provider) else { return nil } 69 | 70 | self.fileData = fileData 71 | self.fileURL = fileURL 72 | self.fileName = fileName 73 | 74 | if let password = password, let cPasswordString = password.cString(using: .utf8) { 75 | // Try a blank password first, per Apple's Quartz PDF example 76 | if coreDocument.isEncrypted && !coreDocument.unlockWithPassword("") { 77 | // Nope, now let's try the provided password to unlock the PDF 78 | if !coreDocument.unlockWithPassword(cPasswordString) { 79 | print("CGPDFDocumentCreateX: Unable to unlock \(fileName)") 80 | } 81 | self.password = password 82 | } else { 83 | self.password = nil 84 | } 85 | } else { 86 | self.password = nil 87 | } 88 | 89 | self.coreDocument = coreDocument 90 | self.pageCount = coreDocument.numberOfPages 91 | self.loadPages() 92 | } 93 | 94 | /// Extracts image representations of each page in a background thread and stores them in the cache 95 | func loadPages() { 96 | DispatchQueue.global(qos: .background).async { 97 | for pageNumber in 1...self.pageCount { 98 | self.imageFromPDFPage(at: pageNumber, callback: { backgroundImage in 99 | guard let backgroundImage = backgroundImage else { return } 100 | self.images.setObject(backgroundImage, forKey: NSNumber(value: pageNumber)) 101 | }) 102 | } 103 | } 104 | } 105 | 106 | /// Image representations of all the document pages 107 | func allPageImages(callback: ([UIImage]) -> Void) { 108 | var images = [UIImage]() 109 | var pagesCompleted = 0 110 | for pageNumber in 0.. Void) { 130 | if let image = images.object(forKey: NSNumber(value: pageNumber)) { 131 | callback(image) 132 | } else { 133 | imageFromPDFPage(at: pageNumber, callback: { image in 134 | guard let image = image else { 135 | callback(nil) 136 | return 137 | } 138 | 139 | images.setObject(image, forKey: NSNumber(value: pageNumber)) 140 | callback(image) 141 | }) 142 | } 143 | } 144 | 145 | /// Grabs the raw image representation of the document page from the document reference 146 | /// 147 | /// - parameter pageNumber: page number index of the page 148 | /// - parameter callback: callback to execute when finished 149 | /// 150 | /// - returns: Image representation of the document page 151 | private func imageFromPDFPage(at pageNumber: Int, callback: (UIImage?) -> Void) { 152 | guard let page = coreDocument.page(at: pageNumber) else { 153 | callback(nil) 154 | return 155 | } 156 | 157 | let originalPageRect = page.originalPageRect 158 | 159 | let scalingConstant: CGFloat = 240 160 | let pdfScale = min(scalingConstant/originalPageRect.width, scalingConstant/originalPageRect.height) 161 | let scaledPageSize = CGSize(width: originalPageRect.width * pdfScale, height: originalPageRect.height * pdfScale) 162 | let scaledPageRect = CGRect(origin: originalPageRect.origin, size: scaledPageSize) 163 | 164 | // Create a low resolution image representation of the PDF page to display before the TiledPDFView renders its content. 165 | UIGraphicsBeginImageContextWithOptions(scaledPageSize, true, 1) 166 | guard let context = UIGraphicsGetCurrentContext() else { 167 | callback(nil) 168 | return 169 | } 170 | 171 | // First fill the background with white. 172 | context.setFillColor(red: 1, green: 1, blue: 1, alpha: 1) 173 | context.fill(scaledPageRect) 174 | 175 | context.saveGState() 176 | 177 | // Flip the context so that the PDF page is rendered right side up. 178 | let rotationAngle: CGFloat 179 | switch page.rotationAngle { 180 | case 90: 181 | rotationAngle = 270 182 | context.translateBy(x: scaledPageSize.width, y: scaledPageSize.height) 183 | case 180: 184 | rotationAngle = 180 185 | context.translateBy(x: 0, y: scaledPageSize.height) 186 | case 270: 187 | rotationAngle = 90 188 | context.translateBy(x: scaledPageSize.width, y: scaledPageSize.height) 189 | default: 190 | rotationAngle = 0 191 | context.translateBy(x: 0, y: scaledPageSize.height) 192 | } 193 | 194 | context.scaleBy(x: 1, y: -1) 195 | context.rotate(by: rotationAngle.degreesToRadians) 196 | 197 | // Scale the context so that the PDF page is rendered at the correct size for the zoom level. 198 | context.scaleBy(x: pdfScale, y: pdfScale) 199 | context.drawPDFPage(page) 200 | context.restoreGState() 201 | 202 | defer { UIGraphicsEndImageContext() } 203 | guard let backgroundImage = UIGraphicsGetImageFromCurrentImageContext() else { 204 | callback(nil) 205 | return 206 | } 207 | 208 | callback(backgroundImage) 209 | } 210 | } 211 | 212 | extension CGPDFPage { 213 | /// original size of the PDF page. 214 | var originalPageRect: CGRect { 215 | switch rotationAngle { 216 | case 90, 270: 217 | let originalRect = getBoxRect(.mediaBox) 218 | let rotatedSize = CGSize(width: originalRect.height, height: originalRect.width) 219 | return CGRect(origin: originalRect.origin, size: rotatedSize) 220 | default: 221 | return getBoxRect(.mediaBox) 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Sources/Classes/PDFPageCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPageCollectionViewCell.swift 3 | // PDFReader 4 | // 5 | // Created by Ricardo Nunez on 7/12/16. 6 | // Copyright © 2016 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Delegate that is informed of important interaction events with the pdf page collection view 12 | protocol PDFPageCollectionViewCellDelegate: class { 13 | func handleSingleTap(_ cell: PDFPageCollectionViewCell, pdfPageView: PDFPageView) 14 | } 15 | 16 | /// A cell housing the interactable pdf page view 17 | internal final class PDFPageCollectionViewCell: UICollectionViewCell { 18 | /// Index of the page 19 | var pageIndex: Int? 20 | 21 | /// Page view of the current page in the document 22 | var pageView: PDFPageView? { 23 | didSet { 24 | subviews.forEach{ $0.removeFromSuperview() } 25 | if let pageView = pageView { 26 | addSubview(pageView) 27 | } 28 | } 29 | } 30 | 31 | /// Delegate informed of important events 32 | private weak var pageCollectionViewCellDelegate: PDFPageCollectionViewCellDelegate? 33 | 34 | 35 | /// Customizes and sets up the cell to be ready to be displayed 36 | /// 37 | /// - parameter indexPathRow: page index of the document to be displayed 38 | /// - parameter collectionViewBounds: bounds of the entire collection view 39 | /// - parameter document: document to be displayed 40 | /// - parameter pageCollectionViewCellDelegate: delegate informed of important events 41 | func setup(_ indexPathRow: Int, collectionViewBounds: CGRect, document: PDFDocument, pageCollectionViewCellDelegate: PDFPageCollectionViewCellDelegate?) { 42 | self.pageCollectionViewCellDelegate = pageCollectionViewCellDelegate 43 | document.pdfPageImage(at: indexPathRow + 1) { (backgroundImage) in 44 | pageView = PDFPageView(frame: bounds, document: document, pageNumber: indexPathRow, backgroundImage: backgroundImage, pageViewDelegate: self) 45 | pageIndex = indexPathRow 46 | } 47 | } 48 | } 49 | 50 | extension PDFPageCollectionViewCell: PDFPageViewDelegate { 51 | func handleSingleTap(_ pdfPageView: PDFPageView) { 52 | pageCollectionViewCellDelegate?.handleSingleTap(self, pdfPageView: pdfPageView) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Classes/PDFPageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFPageView.swift 3 | // PDFReader 4 | // 5 | // Created by ALUA KINZHEBAYEVA on 4/23/15. 6 | // Copyright (c) 2015 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Delegate that is informed of important interaction events with the current `PDFPageView` 12 | protocol PDFPageViewDelegate: class { 13 | /// User has tapped on the page view 14 | func handleSingleTap(_ pdfPageView: PDFPageView) 15 | } 16 | 17 | /// An interactable page of a document 18 | internal final class PDFPageView: UIScrollView { 19 | /// The TiledPDFView that is currently front most. 20 | private var tiledPDFView: TiledView 21 | 22 | /// Current scale of the scrolling view 23 | private var scale: CGFloat 24 | 25 | /// Number of zoom levels possible when double tapping 26 | private let zoomLevels: CGFloat = 2 27 | 28 | /// View which contains all of our content 29 | private var contentView: UIView 30 | 31 | /// A low resolution image of the PDF page that is displayed until the TiledPDFView renders its content. 32 | private let backgroundImageView: UIImageView 33 | 34 | /// Page reference being displayed 35 | private let pdfPage: CGPDFPage 36 | 37 | /// Current amount being zoomed 38 | private var zoomAmount: CGFloat? 39 | 40 | /// Delegate that is informed of important interaction events 41 | private weak var pageViewDelegate: PDFPageViewDelegate? 42 | 43 | /// Instantiates a scrollable page view 44 | /// 45 | /// - parameter frame: frame of the view 46 | /// - parameter document: document to be displayed 47 | /// - parameter pageNumber: specific page number of the document to display 48 | /// - parameter backgroundImage: background image of the page to display while rendering 49 | /// - parameter pageViewDelegate: delegate notified of any important interaction events 50 | /// 51 | /// - returns: a freshly initialized page view 52 | init(frame: CGRect, document: PDFDocument, pageNumber: Int, backgroundImage: UIImage?, pageViewDelegate: PDFPageViewDelegate?) { 53 | guard let pageRef = document.coreDocument.page(at: pageNumber + 1) else { fatalError() } 54 | 55 | pdfPage = pageRef 56 | self.pageViewDelegate = pageViewDelegate 57 | 58 | let originalPageRect = pageRef.originalPageRect 59 | 60 | scale = min(frame.width/originalPageRect.width, frame.height/originalPageRect.height) 61 | let scaledPageRectSize = CGSize(width: originalPageRect.width * scale, height: originalPageRect.height * scale) 62 | let scaledPageRect = CGRect(origin: originalPageRect.origin, size: scaledPageRectSize) 63 | 64 | guard !scaledPageRect.isEmpty else { fatalError() } 65 | 66 | // Create our content view based on the size of the PDF page 67 | contentView = UIView(frame: scaledPageRect) 68 | 69 | backgroundImageView = UIImageView(image: backgroundImage) 70 | backgroundImageView.frame = contentView.bounds 71 | 72 | // Create the TiledPDFView and scale it to fit the content view. 73 | tiledPDFView = TiledView(frame: contentView.bounds, scale: scale, newPage: pdfPage) 74 | 75 | super.init(frame: frame) 76 | 77 | let targetRect = bounds.insetBy(dx: 0, dy: 0) 78 | var zoomScale = zoomScaleThatFits(targetRect.size, source: bounds.size) 79 | 80 | minimumZoomScale = zoomScale // Set the minimum and maximum zoom scales 81 | maximumZoomScale = zoomScale * (zoomLevels * zoomLevels) // Max number of zoom levels 82 | zoomAmount = (maximumZoomScale - minimumZoomScale) / zoomLevels 83 | 84 | scale = 1 85 | if zoomScale > minimumZoomScale { 86 | zoomScale = minimumZoomScale 87 | } 88 | 89 | contentView.addSubview(backgroundImageView) 90 | contentView.sendSubviewToBack(backgroundImageView) 91 | contentView.addSubview(tiledPDFView) 92 | addSubview(contentView) 93 | 94 | let doubleTapOne = UITapGestureRecognizer(target: self, action:#selector(handleDoubleTap)) 95 | doubleTapOne.numberOfTapsRequired = 2 96 | doubleTapOne.cancelsTouchesInView = false 97 | addGestureRecognizer(doubleTapOne) 98 | 99 | let singleTapOne = UITapGestureRecognizer(target: self, action:#selector(handleSingleTap)) 100 | singleTapOne.numberOfTapsRequired = 1 101 | singleTapOne.cancelsTouchesInView = false 102 | addGestureRecognizer(singleTapOne) 103 | 104 | singleTapOne.require(toFail: doubleTapOne) 105 | 106 | bouncesZoom = false 107 | decelerationRate = UIScrollView.DecelerationRate.fast 108 | delegate = self 109 | autoresizesSubviews = true 110 | autoresizingMask = [.flexibleHeight, .flexibleWidth] 111 | } 112 | 113 | required init?(coder aDecoder: NSCoder) { 114 | fatalError("init(coder:) has not been implemented") 115 | } 116 | 117 | // Use layoutSubviews to center the PDF page in the view. 118 | override func layoutSubviews() { 119 | super.layoutSubviews() 120 | 121 | // Center the image as it becomes smaller than the size of the screen. 122 | let contentViewSize = contentView.frame.size 123 | 124 | // Center horizontally. 125 | let xOffset: CGFloat 126 | if contentViewSize.width < bounds.width { 127 | xOffset = (bounds.width - contentViewSize.width) / 2 128 | } else { 129 | xOffset = 0 130 | } 131 | 132 | // Center vertically. 133 | let yOffset: CGFloat 134 | if contentViewSize.height < bounds.height { 135 | yOffset = (bounds.height - contentViewSize.height) / 2 136 | } else { 137 | yOffset = 0 138 | } 139 | 140 | contentView.frame = CGRect(origin: CGPoint(x: xOffset, y: yOffset), size: contentViewSize) 141 | 142 | // To handle the interaction between CATiledLayer and high resolution screens, set the 143 | // tiling view's contentScaleFactor to 1.0. If this step were omitted, the content scale factor 144 | // would be 2.0 on high resolution screens, which would cause the CATiledLayer to ask for tiles of the wrong scale. 145 | tiledPDFView.contentScaleFactor = 1 146 | } 147 | 148 | /// Notifies the delegate that a single tap was performed 149 | @objc func handleSingleTap(_ tapRecognizer: UITapGestureRecognizer) { 150 | pageViewDelegate?.handleSingleTap(self) 151 | } 152 | 153 | /// Zooms in and out accordingly, based on the current zoom level 154 | @objc func handleDoubleTap(_ tapRecognizer: UITapGestureRecognizer) { 155 | var newScale = zoomScale * zoomLevels 156 | if newScale >= maximumZoomScale { 157 | newScale = minimumZoomScale 158 | } 159 | let zoomRect = zoomRectForScale(newScale, zoomPoint: tapRecognizer.location(in: tapRecognizer.view)) 160 | zoom(to: zoomRect, animated: true) 161 | } 162 | 163 | 164 | /// Calculates the zoom scale given a target size and a source size 165 | /// 166 | /// - parameter target: size of the target rect 167 | /// - parameter source: size of the source rect 168 | /// 169 | /// - returns: the zoom scale of the target in relation to the source 170 | private func zoomScaleThatFits(_ target: CGSize, source: CGSize) -> CGFloat { 171 | let widthScale = target.width / source.width 172 | let heightScale = target.height / source.height 173 | return (widthScale < heightScale) ? widthScale : heightScale 174 | } 175 | 176 | /// Calculates the new zoom rect given a desired scale and a point to zoom on 177 | /// 178 | /// - parameter scale: desired scale to zoom to 179 | /// - parameter zoomPoint: the reference point to zoom on 180 | /// 181 | /// - returns: a new zoom rect 182 | private func zoomRectForScale(_ scale: CGFloat, zoomPoint: CGPoint) -> CGRect { 183 | // Normalize current content size back to content scale of 1.0f 184 | let updatedContentSize = CGSize(width: contentSize.width/zoomScale, height: contentSize.height/zoomScale) 185 | 186 | let translatedZoomPoint = CGPoint(x: (zoomPoint.x / bounds.width) * updatedContentSize.width, 187 | y: (zoomPoint.y / bounds.height) * updatedContentSize.height) 188 | 189 | // derive the size of the region to zoom to 190 | let zoomSize = CGSize(width: bounds.width / scale, height: bounds.height / scale) 191 | 192 | // offset the zoom rect so the actual zoom point is in the middle of the rectangle 193 | return CGRect(x: translatedZoomPoint.x - zoomSize.width / 2.0, 194 | y: translatedZoomPoint.y - zoomSize.height / 2.0, 195 | width: zoomSize.width, 196 | height: zoomSize.height) 197 | } 198 | } 199 | 200 | extension PDFPageView: UIScrollViewDelegate { 201 | /// A UIScrollView delegate callback, called when the user starts zooming. 202 | /// Return the content view 203 | func viewForZooming(in scrollView: UIScrollView) -> UIView? { 204 | return contentView 205 | } 206 | 207 | /// A UIScrollView delegate callback, called when the user stops zooming. 208 | /// When the user stops zooming, create a new Tiled 209 | /// PDFView based on the new zoom level and draw it on top of the old TiledPDFView. 210 | func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { 211 | self.scale = scale 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Sources/Classes/PDFThumbnailCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFThumbnailCell.swift 3 | // PDFReader 4 | // 5 | // Created by Ricardo Nunez on 7/9/16. 6 | // Copyright © 2016 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// An individual thumbnail in the collection view 12 | internal final class PDFThumbnailCell: UICollectionViewCell { 13 | /// Preferred size of each cell 14 | static let cellSize = CGSize(width: 24, height: 44) 15 | 16 | @IBOutlet var imageView: UIImageView? 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Classes/PDFThumbnailCollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFThumbnailCollectionViewController.swift 3 | // PDFReader 4 | // 5 | // Created by Ricardo Nunez on 7/9/16. 6 | // Copyright © 2016 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Delegate that is informed of important interaction events with the current thumbnail collection view 12 | protocol PDFThumbnailControllerDelegate: class { 13 | /// User has tapped on thumbnail 14 | func didSelectIndexPath(_ indexPath: IndexPath) 15 | } 16 | 17 | /// Bottom collection of thumbnails that the user can interact with 18 | internal final class PDFThumbnailCollectionViewController: UICollectionViewController { 19 | /// Current document being displayed 20 | var document: PDFDocument! 21 | 22 | /// Current page index being displayed 23 | var currentPageIndex: Int = 0 { 24 | didSet { 25 | guard let collectionView = collectionView else { return } 26 | guard let pageImages = pageImages else { return } 27 | guard pageImages.count > 0 else { return } 28 | let curentPageIndexPath = IndexPath(row: currentPageIndex, section: 0) 29 | if !collectionView.indexPathsForVisibleItems.contains(curentPageIndexPath) { 30 | collectionView.scrollToItem(at: curentPageIndexPath, at: .centeredHorizontally, animated: true) 31 | } 32 | collectionView.reloadData() 33 | } 34 | } 35 | 36 | /// Calls actions when certain cells have been interacted with 37 | weak var delegate: PDFThumbnailControllerDelegate? 38 | 39 | /// Small thumbnail image representations of the pdf pages 40 | private var pageImages: [UIImage]? { 41 | didSet { 42 | collectionView?.reloadData() 43 | } 44 | } 45 | 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | DispatchQueue.global(qos: .background).async { 49 | self.document.allPageImages(callback: { (images) in 50 | DispatchQueue.main.async { 51 | self.pageImages = images 52 | } 53 | }) 54 | } 55 | } 56 | 57 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 58 | return pageImages?.count ?? 0 59 | } 60 | 61 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 62 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PDFThumbnailCell 63 | 64 | cell.imageView?.image = pageImages?[indexPath.row] 65 | cell.alpha = currentPageIndex == indexPath.row ? 1 : 0.2 66 | 67 | return cell 68 | } 69 | 70 | @objc func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { 71 | return PDFThumbnailCell.cellSize 72 | } 73 | 74 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 75 | delegate?.didSelectIndexPath(indexPath) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Classes/PDFViewController.swift: -------------------------------------------------------------------------------- 1 | // PDFViewController.swift 2 | // PDFReader 3 | // 4 | // Created by ALUA KINZHEBAYEVA on 4/19/15. 5 | // Copyright (c) 2015 AK. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | extension PDFViewController { 11 | /// Initializes a new `PDFViewController` 12 | /// 13 | /// - parameter document: PDF document to be displayed 14 | /// - parameter title: title that displays on the navigation bar on the PDFViewController; 15 | /// if nil, uses document's filename 16 | /// - parameter actionButtonImage: image of the action button; if nil, uses the default action system item image 17 | /// - parameter actionStyle: sytle of the action button 18 | /// - parameter backButton: button to override the default controller back button 19 | /// - parameter isThumbnailsEnabled: whether or not the thumbnails bar should be enabled 20 | /// - parameter startPageIndex: page index to start on load, defaults to 0; if out of bounds, set to 0 21 | /// 22 | /// - returns: a `PDFViewController` 23 | public class func createNew(with document: PDFDocument, title: String? = nil, actionButtonImage: UIImage? = nil, actionStyle: ActionStyle = .print, backButton: UIBarButtonItem? = nil, isThumbnailsEnabled: Bool = true, startPageIndex: Int = 0) -> PDFViewController { 24 | let storyboard = UIStoryboard(name: "PDFReader", bundle: Bundle(for: PDFViewController.self)) 25 | let controller = storyboard.instantiateInitialViewController() as! PDFViewController 26 | controller.document = document 27 | controller.actionStyle = actionStyle 28 | 29 | if let title = title { 30 | controller.title = title 31 | } else { 32 | controller.title = document.fileName 33 | } 34 | 35 | if startPageIndex >= 0 && startPageIndex < document.pageCount { 36 | controller.currentPageIndex = startPageIndex 37 | } else { 38 | controller.currentPageIndex = 0 39 | } 40 | 41 | controller.backButton = backButton 42 | 43 | if let actionButtonImage = actionButtonImage { 44 | controller.actionButton = UIBarButtonItem(image: actionButtonImage, style: .plain, target: controller, action: #selector(actionButtonPressed)) 45 | } else { 46 | controller.actionButton = UIBarButtonItem(barButtonSystemItem: .action, target: controller, action: #selector(actionButtonPressed)) 47 | } 48 | controller.isThumbnailsEnabled = isThumbnailsEnabled 49 | return controller 50 | } 51 | } 52 | 53 | /// Controller that is able to interact and navigate through pages of a `PDFDocument` 54 | public final class PDFViewController: UIViewController { 55 | /// Action button style 56 | public enum ActionStyle { 57 | /// Brings up a print modal allowing user to print current PDF 58 | case print 59 | 60 | /// Brings up an activity sheet to share or open PDF in another app 61 | case activitySheet 62 | 63 | /// Performs a custom action 64 | case customAction(() -> ()) 65 | } 66 | 67 | /// Collection veiw where all the pdf pages are rendered 68 | @IBOutlet public var collectionView: UICollectionView! 69 | 70 | /// Height of the thumbnail bar (used to hide/show) 71 | @IBOutlet private var thumbnailCollectionControllerHeight: NSLayoutConstraint! 72 | 73 | /// Distance between the bottom thumbnail bar with bottom of page (used to hide/show) 74 | @IBOutlet private var thumbnailCollectionControllerBottom: NSLayoutConstraint! 75 | 76 | /// Width of the thumbnail bar (used to resize on rotation events) 77 | @IBOutlet private var thumbnailCollectionControllerWidth: NSLayoutConstraint! 78 | 79 | /// PDF document that should be displayed 80 | private var document: PDFDocument! 81 | 82 | private var actionStyle = ActionStyle.print 83 | 84 | /// Image used to override the default action button image 85 | private var actionButtonImage: UIImage? 86 | 87 | /// Current page being displayed 88 | private var currentPageIndex: Int = 0 89 | 90 | /// Bottom thumbnail controller 91 | private var thumbnailCollectionController: PDFThumbnailCollectionViewController? 92 | 93 | /// UIBarButtonItem used to override the default action button 94 | private var actionButton: UIBarButtonItem? 95 | 96 | /// Backbutton used to override the default back button 97 | private var backButton: UIBarButtonItem? 98 | 99 | /// Background color to apply to the collectionView. 100 | public var backgroundColor: UIColor? = .lightGray { 101 | didSet { 102 | collectionView?.backgroundColor = backgroundColor 103 | } 104 | } 105 | 106 | /// Whether or not the thumbnails bar should be enabled 107 | private var isThumbnailsEnabled = true { 108 | didSet { 109 | if thumbnailCollectionControllerHeight == nil { 110 | _ = view 111 | } 112 | if !isThumbnailsEnabled { 113 | thumbnailCollectionControllerHeight.constant = 0 114 | } 115 | } 116 | } 117 | 118 | /// Slides horizontally (from left to right, default) or vertically (from top to bottom) 119 | public var scrollDirection: UICollectionView.ScrollDirection = .horizontal { 120 | didSet { 121 | if collectionView == nil { // if the user of the controller is trying to change the scrollDiecton before it 122 | _ = view // is on the sceen, we need to show it ofscreen to access it's collectionView. 123 | } 124 | if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { 125 | layout.scrollDirection = scrollDirection 126 | } 127 | } 128 | } 129 | 130 | /// Reset page when its unpresented 131 | public var resetZoom: Bool = false 132 | 133 | override public func viewDidLoad() { 134 | super.viewDidLoad() 135 | 136 | collectionView.backgroundColor = backgroundColor 137 | collectionView.register(PDFPageCollectionViewCell.self, forCellWithReuseIdentifier: "page") 138 | 139 | navigationItem.rightBarButtonItem = actionButton 140 | if let backItem = backButton { 141 | navigationItem.leftBarButtonItem = backItem 142 | } 143 | 144 | let numberOfPages = CGFloat(document.pageCount) 145 | let cellSpacing = CGFloat(2.0) 146 | let totalSpacing = (numberOfPages - 1.0) * cellSpacing 147 | let thumbnailWidth = (numberOfPages * PDFThumbnailCell.cellSize.width) + totalSpacing 148 | let width = min(thumbnailWidth, view.bounds.width) 149 | thumbnailCollectionControllerWidth.constant = width 150 | } 151 | 152 | public override func viewDidLayoutSubviews() { 153 | super.viewDidLayoutSubviews() 154 | didSelectIndexPath(IndexPath(row: currentPageIndex, section: 0)) 155 | } 156 | 157 | override public var prefersStatusBarHidden: Bool { 158 | return navigationController?.isNavigationBarHidden == true 159 | } 160 | 161 | override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { 162 | return .slide 163 | } 164 | 165 | public override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { 166 | return isThumbnailsEnabled 167 | } 168 | 169 | override public func prepare(for segue: UIStoryboardSegue, sender: Any?) { 170 | if let controller = segue.destination as? PDFThumbnailCollectionViewController { 171 | thumbnailCollectionController = controller 172 | controller.document = document 173 | controller.delegate = self 174 | controller.currentPageIndex = currentPageIndex 175 | } 176 | } 177 | 178 | public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 179 | coordinator.animate(alongsideTransition: { context in 180 | let currentIndexPath = IndexPath(row: self.currentPageIndex, section: 0) 181 | self.collectionView.reloadItems(at: [currentIndexPath]) 182 | self.collectionView.scrollToItem(at: currentIndexPath, at: .centeredHorizontally, animated: false) 183 | }) { context in 184 | self.thumbnailCollectionController?.currentPageIndex = self.currentPageIndex 185 | } 186 | 187 | super.viewWillTransition(to: size, with: coordinator) 188 | } 189 | 190 | /// Takes an appropriate action based on the current action style 191 | @objc func actionButtonPressed() { 192 | switch actionStyle { 193 | case .print: 194 | print() 195 | case .activitySheet: 196 | presentActivitySheet() 197 | case .customAction(let customAction): 198 | customAction() 199 | } 200 | } 201 | 202 | /// Presents activity sheet to share or open PDF in another app 203 | private func presentActivitySheet() { 204 | let controller = UIActivityViewController(activityItems: [document.fileData], applicationActivities: nil) 205 | controller.popoverPresentationController?.barButtonItem = actionButton 206 | present(controller, animated: true, completion: nil) 207 | } 208 | 209 | /// Presents print sheet to print PDF 210 | private func print() { 211 | guard UIPrintInteractionController.isPrintingAvailable else { return } 212 | guard UIPrintInteractionController.canPrint(document.fileData) else { return } 213 | guard document.password == nil else { return } 214 | let printInfo = UIPrintInfo.printInfo() 215 | printInfo.duplex = .longEdge 216 | printInfo.outputType = .general 217 | printInfo.jobName = document.fileName 218 | 219 | let printInteraction = UIPrintInteractionController.shared 220 | printInteraction.printInfo = printInfo 221 | printInteraction.printingItem = document.fileData 222 | printInteraction.showsPageRange = true 223 | printInteraction.present(animated: true, completionHandler: nil) 224 | } 225 | } 226 | 227 | extension PDFViewController: PDFThumbnailControllerDelegate { 228 | func didSelectIndexPath(_ indexPath: IndexPath) { 229 | collectionView.scrollToItem(at: indexPath, at: .left, animated: false) 230 | thumbnailCollectionController?.currentPageIndex = currentPageIndex 231 | } 232 | } 233 | 234 | extension PDFViewController: UICollectionViewDataSource { 235 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 236 | return document.pageCount 237 | } 238 | 239 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 240 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "page", for: indexPath) as! PDFPageCollectionViewCell 241 | cell.setup(indexPath.row, collectionViewBounds: collectionView.bounds, document: document, pageCollectionViewCellDelegate: self) 242 | return cell 243 | } 244 | } 245 | 246 | extension PDFViewController: PDFPageCollectionViewCellDelegate { 247 | /// Toggles the hiding/showing of the thumbnail controller 248 | /// 249 | /// - parameter shouldHide: whether or not the controller should hide the thumbnail controller 250 | private func hideThumbnailController(_ shouldHide: Bool) { 251 | self.thumbnailCollectionControllerBottom.constant = shouldHide ? -thumbnailCollectionControllerHeight.constant : 0 252 | } 253 | 254 | func handleSingleTap(_ cell: PDFPageCollectionViewCell, pdfPageView: PDFPageView) { 255 | var shouldHide: Bool { 256 | guard let isNavigationBarHidden = navigationController?.isNavigationBarHidden else { 257 | return false 258 | } 259 | return !isNavigationBarHidden 260 | } 261 | UIView.animate(withDuration: 0.25) { 262 | self.hideThumbnailController(shouldHide) 263 | self.navigationController?.setNavigationBarHidden(shouldHide, animated: true) 264 | } 265 | } 266 | } 267 | 268 | extension PDFViewController: UICollectionViewDelegateFlowLayout { 269 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 270 | return CGSize(width: collectionView.frame.width - 1, height: collectionView.frame.height) 271 | } 272 | } 273 | 274 | extension PDFViewController: UIScrollViewDelegate { 275 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 276 | let updatedPageIndex: Int 277 | if self.scrollDirection == .vertical { 278 | updatedPageIndex = Int(round(max(scrollView.contentOffset.y, 0) / scrollView.bounds.height)) 279 | } else { 280 | updatedPageIndex = Int(round(max(scrollView.contentOffset.x, 0) / scrollView.bounds.width)) 281 | } 282 | 283 | if updatedPageIndex != currentPageIndex { 284 | if resetZoom { 285 | self.collectionView.reloadItems(at: [IndexPath(item: currentPageIndex, section: 0)]) 286 | } 287 | currentPageIndex = updatedPageIndex 288 | thumbnailCollectionController?.currentPageIndex = currentPageIndex 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /Sources/Classes/TiledView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiledView.swift 3 | // PDFReader 4 | // 5 | // Created by ALUA KINZHEBAYEVA on 4/22/15. 6 | // Copyright (c) 2015 AK. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | 12 | extension Int { 13 | var degreesToRadians: Double { return Double(self) * .pi / 180 } 14 | } 15 | 16 | extension FloatingPoint { 17 | var degreesToRadians: Self { return self * .pi / 180 } 18 | var radiansToDegrees: Self { return self * 180 / .pi } 19 | } 20 | 21 | /// Tiled representation of a portion of a rendered pdf page 22 | internal final class TiledView: UIView { 23 | /// Page of the PDF to be tiled 24 | private var leftPdfPage: CGPDFPage? 25 | 26 | /// Current PDF scale 27 | private let myScale: CGFloat 28 | 29 | /// Initializes a fresh tiled view 30 | /// 31 | /// - parameter frame: desired frame of the tiled view 32 | /// - parameter scale: scale factor 33 | /// - parameter newPage: new page representation 34 | init(frame: CGRect, scale: CGFloat, newPage: CGPDFPage) { 35 | myScale = scale 36 | leftPdfPage = newPage 37 | super.init(frame: frame) 38 | 39 | // levelsOfDetail and levelsOfDetailBias determine how the layer is 40 | // rendered at different zoom levels. This only matters while the view 41 | // is zooming, because once the the view is done zooming a new TiledPDFView 42 | // is created at the correct size and scale. 43 | let tiledLayer = self.layer as? CATiledLayer 44 | tiledLayer?.levelsOfDetail = 16 45 | tiledLayer?.levelsOfDetailBias = 15 46 | tiledLayer?.tileSize = CGSize(width: 1024, height: 1024) 47 | } 48 | 49 | required init?(coder aDecoder: NSCoder) { 50 | fatalError("init(coder:) has not been implemented") 51 | } 52 | 53 | override class var layerClass : AnyClass { 54 | return CATiledLayer.self 55 | } 56 | 57 | // Draw the CGPDFPage into the layer at the correct scale. 58 | override func draw(_ layer: CALayer, in con: CGContext) { 59 | guard let leftPdfPage = leftPdfPage else { return } 60 | 61 | // Fill the background with white. 62 | con.setFillColor(red: 1, green: 1, blue: 1, alpha: 1) 63 | con.fill(layer.bounds) 64 | 65 | con.saveGState() 66 | // Flip the context so that the PDF page is rendered right side up. 67 | 68 | let rotationAngle: CGFloat 69 | switch leftPdfPage.rotationAngle { 70 | case 90: 71 | rotationAngle = 270 72 | con.translateBy(x: layer.bounds.width, y: layer.bounds.height) 73 | case 180: 74 | rotationAngle = 180 75 | con.translateBy(x: 0, y: layer.bounds.height) 76 | case 270: 77 | rotationAngle = 90 78 | con.translateBy(x: layer.bounds.width, y: layer.bounds.height) 79 | default: 80 | rotationAngle = 0 81 | con.translateBy(x: 0, y: layer.bounds.height) 82 | } 83 | 84 | con.scaleBy(x: 1, y: -1) 85 | con.rotate(by: rotationAngle.degreesToRadians) 86 | 87 | // Scale the context so that the PDF page is rendered at the correct size for the zoom level. 88 | con.scaleBy(x: myScale, y: myScale) 89 | con.drawPDFPage(leftPdfPage) 90 | con.restoreGState() 91 | } 92 | 93 | // Stops drawLayer 94 | deinit { 95 | leftPdfPage = nil 96 | layer.contents = nil 97 | layer.delegate = nil 98 | layer.removeFromSuperlayer() 99 | } 100 | } 101 | --------------------------------------------------------------------------------