├── .gitignore ├── MJPhotoBrowser.podspec ├── MJPhotoBrowser.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── MJPhotoBrowser.xcworkspace └── contents.xcworkspacedata ├── MJPhotoBrowser ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── CollectionViewCell.h ├── CollectionViewCell.m ├── CollectionViewController.h ├── CollectionViewController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── MJPhotoBrowser │ ├── MJPhoto.h │ ├── MJPhoto.m │ ├── MJPhotoBrowser.bundle │ │ ├── save_icon.png │ │ ├── save_icon@2x.png │ │ ├── save_icon_highlighted.png │ │ └── save_icon_highlighted@2x.png │ ├── MJPhotoBrowser.h │ ├── MJPhotoBrowser.m │ ├── MJPhotoLoadingView.h │ ├── MJPhotoLoadingView.m │ ├── MJPhotoProgressView.h │ ├── MJPhotoProgressView.m │ ├── MJPhotoToolbar.h │ ├── MJPhotoToolbar.m │ ├── MJPhotoView.h │ └── MJPhotoView.m └── main.m ├── Podfile ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Xcode ### 4 | build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.xcuserstate 18 | 19 | 20 | ### Objective-C ### 21 | # Xcode 22 | # 23 | build/ 24 | *.pbxuser 25 | !default.pbxuser 26 | *.mode1v3 27 | !default.mode1v3 28 | *.mode2v3 29 | !default.mode2v3 30 | *.perspectivev3 31 | !default.perspectivev3 32 | xcuserdata 33 | *.xccheckout 34 | *.moved-aside 35 | DerivedData 36 | *.hmap 37 | *.ipa 38 | *.xcuserstate 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 45 | # 46 | Pods/ 47 | -------------------------------------------------------------------------------- /MJPhotoBrowser.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "MJPhotoBrowser" 4 | s.version = "1.0.2" 5 | s.summary = "The easiest lightest way to use PhotoBrowser, enhanced by Sunnyyoung." 6 | s.homepage = "https://github.com/Sunnyyoung/MJPhotoBrowser" 7 | s.license = "MIT" 8 | 9 | s.authors = { 'azxfire' => 'https://github.com/azxfire' } 10 | 11 | s.platform = :ios, "6.0" 12 | s.source = { :git => "https://github.com/Sunnyyoung/MJPhotoBrowser.git", :tag => s.version } 13 | s.source_files = "MJPhotoBrowser/MJPhotoBrowser/*.{h,m}" 14 | s.resource = "MJPhotoBrowser/MJPhotoBrowser/*.bundle" 15 | s.requires_arc = true 16 | s.dependency 'SDWebImage' 17 | s.dependency 'SVProgressHUD' 18 | s.dependency 'YLGIFImage' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /MJPhotoBrowser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6B5C566EA0F1E3FCB2A72B1C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6307551182D5E93801436 /* libPods.a */; }; 11 | 7D7F322B1B0F0ADE00A010CD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F322A1B0F0ADE00A010CD /* main.m */; }; 12 | 7D7F322E1B0F0ADE00A010CD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F322D1B0F0ADE00A010CD /* AppDelegate.m */; }; 13 | 7D7F32341B0F0ADE00A010CD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7D7F32321B0F0ADE00A010CD /* Main.storyboard */; }; 14 | 7D7F32361B0F0ADE00A010CD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D7F32351B0F0ADE00A010CD /* Images.xcassets */; }; 15 | 7D7F32391B0F0ADE00A010CD /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D7F32371B0F0ADE00A010CD /* LaunchScreen.xib */; }; 16 | 7D7F325C1B0F0B9100A010CD /* MJPhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32501B0F0B9100A010CD /* MJPhoto.m */; }; 17 | 7D7F325D1B0F0B9100A010CD /* MJPhotoBrowser.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 7D7F32511B0F0B9100A010CD /* MJPhotoBrowser.bundle */; }; 18 | 7D7F325E1B0F0B9100A010CD /* MJPhotoBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32531B0F0B9100A010CD /* MJPhotoBrowser.m */; }; 19 | 7D7F325F1B0F0B9100A010CD /* MJPhotoLoadingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32551B0F0B9100A010CD /* MJPhotoLoadingView.m */; }; 20 | 7D7F32601B0F0B9100A010CD /* MJPhotoProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32571B0F0B9100A010CD /* MJPhotoProgressView.m */; }; 21 | 7D7F32611B0F0B9100A010CD /* MJPhotoToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32591B0F0B9100A010CD /* MJPhotoToolbar.m */; }; 22 | 7D7F32621B0F0B9100A010CD /* MJPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F325B1B0F0B9100A010CD /* MJPhotoView.m */; }; 23 | 7D7F326F1B0F0EB500A010CD /* CollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F326E1B0F0EB500A010CD /* CollectionViewCell.m */; }; 24 | 7D7F32741B0F0EDC00A010CD /* CollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7F32731B0F0EDC00A010CD /* CollectionViewController.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 61BDFDC6AFDCF8274D5596C5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 29 | 7D7F32251B0F0ADE00A010CD /* MJPhotoBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MJPhotoBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 7D7F32291B0F0ADE00A010CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 7D7F322A1B0F0ADE00A010CD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 7D7F322C1B0F0ADE00A010CD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 33 | 7D7F322D1B0F0ADE00A010CD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 34 | 7D7F32331B0F0ADE00A010CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 35 | 7D7F32351B0F0ADE00A010CD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 36 | 7D7F32381B0F0ADE00A010CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 37 | 7D7F324F1B0F0B9100A010CD /* MJPhoto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhoto.h; sourceTree = ""; }; 38 | 7D7F32501B0F0B9100A010CD /* MJPhoto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhoto.m; sourceTree = ""; }; 39 | 7D7F32511B0F0B9100A010CD /* MJPhotoBrowser.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = MJPhotoBrowser.bundle; sourceTree = ""; }; 40 | 7D7F32521B0F0B9100A010CD /* MJPhotoBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhotoBrowser.h; sourceTree = ""; }; 41 | 7D7F32531B0F0B9100A010CD /* MJPhotoBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhotoBrowser.m; sourceTree = ""; }; 42 | 7D7F32541B0F0B9100A010CD /* MJPhotoLoadingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhotoLoadingView.h; sourceTree = ""; }; 43 | 7D7F32551B0F0B9100A010CD /* MJPhotoLoadingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhotoLoadingView.m; sourceTree = ""; }; 44 | 7D7F32561B0F0B9100A010CD /* MJPhotoProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhotoProgressView.h; sourceTree = ""; }; 45 | 7D7F32571B0F0B9100A010CD /* MJPhotoProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhotoProgressView.m; sourceTree = ""; }; 46 | 7D7F32581B0F0B9100A010CD /* MJPhotoToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhotoToolbar.h; sourceTree = ""; }; 47 | 7D7F32591B0F0B9100A010CD /* MJPhotoToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhotoToolbar.m; sourceTree = ""; }; 48 | 7D7F325A1B0F0B9100A010CD /* MJPhotoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPhotoView.h; sourceTree = ""; }; 49 | 7D7F325B1B0F0B9100A010CD /* MJPhotoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPhotoView.m; sourceTree = ""; }; 50 | 7D7F326D1B0F0EB500A010CD /* CollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewCell.h; sourceTree = ""; }; 51 | 7D7F326E1B0F0EB500A010CD /* CollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewCell.m; sourceTree = ""; }; 52 | 7D7F32721B0F0EDC00A010CD /* CollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewController.h; sourceTree = ""; }; 53 | 7D7F32731B0F0EDC00A010CD /* CollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewController.m; sourceTree = ""; }; 54 | C8FF0A48B1D846F8AE8F6D19 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 55 | FBF6307551182D5E93801436 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 7D7F32221B0F0ADE00A010CD /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 6B5C566EA0F1E3FCB2A72B1C /* libPods.a in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 7D7F321C1B0F0ADE00A010CD = { 71 | isa = PBXGroup; 72 | children = ( 73 | 7D7F32271B0F0ADE00A010CD /* MJPhotoBrowser */, 74 | 7D7F32261B0F0ADE00A010CD /* Products */, 75 | B961F480196F1AC2CC8A31E6 /* Pods */, 76 | 8F7D9B37A64BEE3EE92EF84B /* Frameworks */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 7D7F32261B0F0ADE00A010CD /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 7D7F32251B0F0ADE00A010CD /* MJPhotoBrowser.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 7D7F32271B0F0ADE00A010CD /* MJPhotoBrowser */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 7D7F324E1B0F0B9100A010CD /* MJPhotoBrowser */, 92 | 7D7F32701B0F0EBB00A010CD /* View */, 93 | 7D7F32711B0F0ECB00A010CD /* Controller */, 94 | 7D7F322C1B0F0ADE00A010CD /* AppDelegate.h */, 95 | 7D7F322D1B0F0ADE00A010CD /* AppDelegate.m */, 96 | 7D7F32321B0F0ADE00A010CD /* Main.storyboard */, 97 | 7D7F32351B0F0ADE00A010CD /* Images.xcassets */, 98 | 7D7F32371B0F0ADE00A010CD /* LaunchScreen.xib */, 99 | 7D7F32281B0F0ADE00A010CD /* Supporting Files */, 100 | ); 101 | path = MJPhotoBrowser; 102 | sourceTree = ""; 103 | }; 104 | 7D7F32281B0F0ADE00A010CD /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 7D7F32291B0F0ADE00A010CD /* Info.plist */, 108 | 7D7F322A1B0F0ADE00A010CD /* main.m */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | 7D7F324E1B0F0B9100A010CD /* MJPhotoBrowser */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 7D7F324F1B0F0B9100A010CD /* MJPhoto.h */, 117 | 7D7F32501B0F0B9100A010CD /* MJPhoto.m */, 118 | 7D7F32511B0F0B9100A010CD /* MJPhotoBrowser.bundle */, 119 | 7D7F32521B0F0B9100A010CD /* MJPhotoBrowser.h */, 120 | 7D7F32531B0F0B9100A010CD /* MJPhotoBrowser.m */, 121 | 7D7F32541B0F0B9100A010CD /* MJPhotoLoadingView.h */, 122 | 7D7F32551B0F0B9100A010CD /* MJPhotoLoadingView.m */, 123 | 7D7F32561B0F0B9100A010CD /* MJPhotoProgressView.h */, 124 | 7D7F32571B0F0B9100A010CD /* MJPhotoProgressView.m */, 125 | 7D7F32581B0F0B9100A010CD /* MJPhotoToolbar.h */, 126 | 7D7F32591B0F0B9100A010CD /* MJPhotoToolbar.m */, 127 | 7D7F325A1B0F0B9100A010CD /* MJPhotoView.h */, 128 | 7D7F325B1B0F0B9100A010CD /* MJPhotoView.m */, 129 | ); 130 | path = MJPhotoBrowser; 131 | sourceTree = ""; 132 | }; 133 | 7D7F32701B0F0EBB00A010CD /* View */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 7D7F326D1B0F0EB500A010CD /* CollectionViewCell.h */, 137 | 7D7F326E1B0F0EB500A010CD /* CollectionViewCell.m */, 138 | ); 139 | name = View; 140 | sourceTree = ""; 141 | }; 142 | 7D7F32711B0F0ECB00A010CD /* Controller */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 7D7F32721B0F0EDC00A010CD /* CollectionViewController.h */, 146 | 7D7F32731B0F0EDC00A010CD /* CollectionViewController.m */, 147 | ); 148 | name = Controller; 149 | sourceTree = ""; 150 | }; 151 | 8F7D9B37A64BEE3EE92EF84B /* Frameworks */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | FBF6307551182D5E93801436 /* libPods.a */, 155 | ); 156 | name = Frameworks; 157 | sourceTree = ""; 158 | }; 159 | B961F480196F1AC2CC8A31E6 /* Pods */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 61BDFDC6AFDCF8274D5596C5 /* Pods.debug.xcconfig */, 163 | C8FF0A48B1D846F8AE8F6D19 /* Pods.release.xcconfig */, 164 | ); 165 | name = Pods; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 7D7F32241B0F0ADE00A010CD /* MJPhotoBrowser */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 7D7F32481B0F0ADE00A010CD /* Build configuration list for PBXNativeTarget "MJPhotoBrowser" */; 174 | buildPhases = ( 175 | 545E53229C79870D3652B9FD /* Check Pods Manifest.lock */, 176 | 7D7F32211B0F0ADE00A010CD /* Sources */, 177 | 7D7F32221B0F0ADE00A010CD /* Frameworks */, 178 | 7D7F32231B0F0ADE00A010CD /* Resources */, 179 | 445A2D9607833B8C874184E0 /* Copy Pods Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = MJPhotoBrowser; 186 | productName = MJPhotoBrowser; 187 | productReference = 7D7F32251B0F0ADE00A010CD /* MJPhotoBrowser.app */; 188 | productType = "com.apple.product-type.application"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | 7D7F321D1B0F0ADE00A010CD /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | LastUpgradeCheck = 0630; 197 | ORGANIZATIONNAME = Sunnyyoung; 198 | TargetAttributes = { 199 | 7D7F32241B0F0ADE00A010CD = { 200 | CreatedOnToolsVersion = 6.3.2; 201 | }; 202 | }; 203 | }; 204 | buildConfigurationList = 7D7F32201B0F0ADE00A010CD /* Build configuration list for PBXProject "MJPhotoBrowser" */; 205 | compatibilityVersion = "Xcode 3.2"; 206 | developmentRegion = English; 207 | hasScannedForEncodings = 0; 208 | knownRegions = ( 209 | en, 210 | Base, 211 | ); 212 | mainGroup = 7D7F321C1B0F0ADE00A010CD; 213 | productRefGroup = 7D7F32261B0F0ADE00A010CD /* Products */; 214 | projectDirPath = ""; 215 | projectRoot = ""; 216 | targets = ( 217 | 7D7F32241B0F0ADE00A010CD /* MJPhotoBrowser */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXResourcesBuildPhase section */ 223 | 7D7F32231B0F0ADE00A010CD /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 7D7F32341B0F0ADE00A010CD /* Main.storyboard in Resources */, 228 | 7D7F32391B0F0ADE00A010CD /* LaunchScreen.xib in Resources */, 229 | 7D7F325D1B0F0B9100A010CD /* MJPhotoBrowser.bundle in Resources */, 230 | 7D7F32361B0F0ADE00A010CD /* Images.xcassets in Resources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXResourcesBuildPhase section */ 235 | 236 | /* Begin PBXShellScriptBuildPhase section */ 237 | 445A2D9607833B8C874184E0 /* Copy Pods Resources */ = { 238 | isa = PBXShellScriptBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | ); 242 | inputPaths = ( 243 | ); 244 | name = "Copy Pods Resources"; 245 | outputPaths = ( 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | shellPath = /bin/sh; 249 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 250 | showEnvVarsInLog = 0; 251 | }; 252 | 545E53229C79870D3652B9FD /* Check Pods Manifest.lock */ = { 253 | isa = PBXShellScriptBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | inputPaths = ( 258 | ); 259 | name = "Check Pods Manifest.lock"; 260 | outputPaths = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | shellPath = /bin/sh; 264 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 265 | showEnvVarsInLog = 0; 266 | }; 267 | /* End PBXShellScriptBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 7D7F32211B0F0ADE00A010CD /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 7D7F325E1B0F0B9100A010CD /* MJPhotoBrowser.m in Sources */, 275 | 7D7F32601B0F0B9100A010CD /* MJPhotoProgressView.m in Sources */, 276 | 7D7F325F1B0F0B9100A010CD /* MJPhotoLoadingView.m in Sources */, 277 | 7D7F325C1B0F0B9100A010CD /* MJPhoto.m in Sources */, 278 | 7D7F322E1B0F0ADE00A010CD /* AppDelegate.m in Sources */, 279 | 7D7F326F1B0F0EB500A010CD /* CollectionViewCell.m in Sources */, 280 | 7D7F322B1B0F0ADE00A010CD /* main.m in Sources */, 281 | 7D7F32621B0F0B9100A010CD /* MJPhotoView.m in Sources */, 282 | 7D7F32741B0F0EDC00A010CD /* CollectionViewController.m in Sources */, 283 | 7D7F32611B0F0B9100A010CD /* MJPhotoToolbar.m in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 7D7F32321B0F0ADE00A010CD /* Main.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 7D7F32331B0F0ADE00A010CD /* Base */, 294 | ); 295 | name = Main.storyboard; 296 | sourceTree = ""; 297 | }; 298 | 7D7F32371B0F0ADE00A010CD /* LaunchScreen.xib */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 7D7F32381B0F0ADE00A010CD /* Base */, 302 | ); 303 | name = LaunchScreen.xib; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 7D7F32461B0F0ADE00A010CD /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_UNREACHABLE_CODE = YES; 325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 327 | COPY_PHASE_STRIP = NO; 328 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 329 | ENABLE_STRICT_OBJC_MSGSEND = YES; 330 | GCC_C_LANGUAGE_STANDARD = gnu99; 331 | GCC_DYNAMIC_NO_PIC = NO; 332 | GCC_NO_COMMON_BLOCKS = YES; 333 | GCC_OPTIMIZATION_LEVEL = 0; 334 | GCC_PREPROCESSOR_DEFINITIONS = ( 335 | "DEBUG=1", 336 | "$(inherited)", 337 | ); 338 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 346 | MTL_ENABLE_DEBUG_INFO = YES; 347 | ONLY_ACTIVE_ARCH = YES; 348 | SDKROOT = iphoneos; 349 | }; 350 | name = Debug; 351 | }; 352 | 7D7F32471B0F0ADE00A010CD /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_WARN_BOOL_CONVERSION = YES; 361 | CLANG_WARN_CONSTANT_CONVERSION = YES; 362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 363 | CLANG_WARN_EMPTY_BODY = YES; 364 | CLANG_WARN_ENUM_CONVERSION = YES; 365 | CLANG_WARN_INT_CONVERSION = YES; 366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 372 | ENABLE_NS_ASSERTIONS = NO; 373 | ENABLE_STRICT_OBJC_MSGSEND = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu99; 375 | GCC_NO_COMMON_BLOCKS = YES; 376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 378 | GCC_WARN_UNDECLARED_SELECTOR = YES; 379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 380 | GCC_WARN_UNUSED_FUNCTION = YES; 381 | GCC_WARN_UNUSED_VARIABLE = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 383 | MTL_ENABLE_DEBUG_INFO = NO; 384 | SDKROOT = iphoneos; 385 | VALIDATE_PRODUCT = YES; 386 | }; 387 | name = Release; 388 | }; 389 | 7D7F32491B0F0ADE00A010CD /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | baseConfigurationReference = 61BDFDC6AFDCF8274D5596C5 /* Pods.debug.xcconfig */; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 395 | INFOPLIST_FILE = MJPhotoBrowser/Info.plist; 396 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 397 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | }; 400 | name = Debug; 401 | }; 402 | 7D7F324A1B0F0ADE00A010CD /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | baseConfigurationReference = C8FF0A48B1D846F8AE8F6D19 /* Pods.release.xcconfig */; 405 | buildSettings = { 406 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 407 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 408 | INFOPLIST_FILE = MJPhotoBrowser/Info.plist; 409 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | }; 413 | name = Release; 414 | }; 415 | /* End XCBuildConfiguration section */ 416 | 417 | /* Begin XCConfigurationList section */ 418 | 7D7F32201B0F0ADE00A010CD /* Build configuration list for PBXProject "MJPhotoBrowser" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | 7D7F32461B0F0ADE00A010CD /* Debug */, 422 | 7D7F32471B0F0ADE00A010CD /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | 7D7F32481B0F0ADE00A010CD /* Build configuration list for PBXNativeTarget "MJPhotoBrowser" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 7D7F32491B0F0ADE00A010CD /* Debug */, 431 | 7D7F324A1B0F0ADE00A010CD /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | /* End XCConfigurationList section */ 437 | }; 438 | rootObject = 7D7F321D1B0F0ADE00A010CD /* Project object */; 439 | } 440 | -------------------------------------------------------------------------------- /MJPhotoBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MJPhotoBrowser.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MJPhotoBrowser/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /MJPhotoBrowser/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | return YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /MJPhotoBrowser/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /MJPhotoBrowser/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 | 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 | -------------------------------------------------------------------------------- /MJPhotoBrowser/CollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewCell.h 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CollectionViewCell : UICollectionViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /MJPhotoBrowser/CollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewCell.m 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import "CollectionViewCell.h" 10 | 11 | @implementation CollectionViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MJPhotoBrowser/CollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewController.h 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface CollectionViewController : UICollectionViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /MJPhotoBrowser/CollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewController.m 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import "CollectionViewController.h" 10 | #import "CollectionViewCell.h" 11 | #import "MJPhotoBrowser.h" 12 | 13 | static NSString * const reuseIdentifier = @"Cell"; 14 | 15 | @interface CollectionViewController () 16 | 17 | @property (nonatomic, strong) NSArray *imageURLArray; 18 | 19 | @end 20 | 21 | @implementation CollectionViewController 22 | 23 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 24 | self = [super initWithCoder:aDecoder]; 25 | if (self) { 26 | _imageURLArray = @[@"http://h.hiphotos.baidu.com/image/pic/item/b8389b504fc2d5628a2690ece51190ef76c66c80.jpg", 27 | @"http://g.hiphotos.baidu.com/image/pic/item/1ad5ad6eddc451da6709d67ab5fd5266d11632a0.jpg", 28 | @"http://b.hiphotos.baidu.com/image/pic/item/908fa0ec08fa513def983c7f3e6d55fbb2fbd965.jpg", 29 | @"http://f.hiphotos.baidu.com/image/pic/item/32fa828ba61ea8d398b30a7d950a304e251f5831.jpg", 30 | @"http://a.hiphotos.baidu.com/image/w%3D230/sign=525711e6a60f4bfb8cd09957334e788f/1e30e924b899a9019c5d254f1f950a7b0208f55b.jpg", 31 | @"http://e.hiphotos.baidu.com/image/pic/item/c83d70cf3bc79f3d8c709a49b8a1cd11728b2931.jpg", 32 | @"http://b.hiphotos.baidu.com/image/w%3D230/sign=87da5b715143fbf2c52ca120807fca1e/77c6a7efce1b9d16bc7ce418f1deb48f8c54640b.jpg", 33 | @"http://h.hiphotos.baidu.com/image/pic/item/a71ea8d3fd1f4134f08c7b07271f95cad1c85e0b.jpg", 34 | @"http://g.hiphotos.baidu.com/image/pic/item/adaf2edda3cc7cd9c3b328cd3b01213fb80e9103.jpg"]; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | } 42 | 43 | #pragma mark 44 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 45 | return 1; 46 | } 47 | 48 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 49 | return _imageURLArray.count; 50 | } 51 | 52 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 53 | CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; 54 | [cell.imageView sd_setImageWithURL:[NSURL URLWithString:_imageURLArray[indexPath.row]]]; 55 | return cell; 56 | } 57 | 58 | #pragma mark 59 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 60 | NSInteger row = 0; 61 | NSMutableArray *photoArray = [NSMutableArray array]; 62 | MJPhotoBrowser *photoBrowser = [[MJPhotoBrowser alloc] init]; 63 | for (NSString *imageURL in _imageURLArray) { 64 | MJPhoto *photo = ({ 65 | MJPhoto *photo = [[MJPhoto alloc] init]; 66 | photo.url = [NSURL URLWithString:imageURL]; 67 | photo.srcImageView = ((CollectionViewCell *)[collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]]).imageView; 68 | photo; 69 | }); 70 | row++; 71 | [photoArray addObject:photo]; 72 | } 73 | photoBrowser.photos = photoArray; 74 | photoBrowser.currentPhotoIndex = indexPath.row; 75 | [photoBrowser show]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /MJPhotoBrowser/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /MJPhotoBrowser/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "minimum-system-version" : "7.0", 7 | "subtype" : "retina4", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "idiom" : "iphone", 12 | "scale" : "1x", 13 | "orientation" : "portrait" 14 | }, 15 | { 16 | "idiom" : "iphone", 17 | "scale" : "2x", 18 | "orientation" : "portrait" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "iphone", 23 | "subtype" : "retina4", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "orientation" : "portrait", 28 | "idiom" : "iphone", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "2x" 31 | } 32 | ], 33 | "info" : { 34 | "version" : 1, 35 | "author" : "xcode" 36 | } 37 | } -------------------------------------------------------------------------------- /MJPhotoBrowser/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | net.Sunnyyoung.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhoto.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhoto.h 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | @interface MJPhoto : NSObject 16 | 17 | @property (nonatomic, strong) NSURL *url; 18 | @property (nonatomic, strong) UIImage *image; // 完整的图片 19 | 20 | @property (nonatomic, strong) UIImageView *srcImageView; // 来源view 21 | @property (nonatomic, strong, readonly) UIImage *placeholder; 22 | @property (nonatomic, strong, readonly) UIImage *capture; 23 | 24 | // 是否已经保存到相册 25 | @property (nonatomic, assign) BOOL save; 26 | @property (nonatomic, assign) int index; // 索引 27 | 28 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhoto.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhoto.m 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | 7 | #import "MJPhoto.h" 8 | 9 | @implementation MJPhoto 10 | 11 | #pragma mark 截图 12 | - (UIImage *)capture:(UIView *)view 13 | { 14 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0.0); 15 | [view.layer renderInContext:UIGraphicsGetCurrentContext()]; 16 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 17 | UIGraphicsEndImageContext(); 18 | return img; 19 | } 20 | 21 | - (void)setSrcImageView:(UIImageView *)srcImageView 22 | { 23 | _srcImageView = srcImageView; 24 | _placeholder = srcImageView.image; 25 | if (srcImageView.clipsToBounds) { 26 | _capture = [self capture:srcImageView]; 27 | } 28 | } 29 | 30 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyyoung/MJPhotoBrowser/06f4ddf7f5b430dc14670ac38cb7533aba07a77e/MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon.png -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyyoung/MJPhotoBrowser/06f4ddf7f5b430dc14670ac38cb7533aba07a77e/MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon@2x.png -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyyoung/MJPhotoBrowser/06f4ddf7f5b430dc14670ac38cb7533aba07a77e/MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon_highlighted.png -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon_highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyyoung/MJPhotoBrowser/06f4ddf7f5b430dc14670ac38cb7533aba07a77e/MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.bundle/save_icon_highlighted@2x.png -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoBrowser.h 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | 7 | #import "MJPhoto.h" 8 | 9 | @protocol MJPhotoBrowserDelegate; 10 | 11 | @interface MJPhotoBrowser : NSObject 12 | // 所有的图片对象 13 | @property (nonatomic, strong) NSArray *photos; 14 | // 当前展示的图片索引 15 | @property (nonatomic, assign) NSUInteger currentPhotoIndex; 16 | // 保存按钮 17 | @property (nonatomic, assign) NSUInteger showSaveBtn; 18 | 19 | // 显示 20 | - (void)show; 21 | 22 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoBrowser.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoBrowser.m 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | 7 | #import "MJPhotoBrowser.h" 8 | #import "MJPhoto.h" 9 | #import "MJPhotoView.h" 10 | #import "MJPhotoToolbar.h" 11 | #import 12 | 13 | #define kPadding 10 14 | #define kPhotoViewTagOffset 1000 15 | #define kPhotoViewIndex(photoView) ([photoView tag] - kPhotoViewTagOffset) 16 | 17 | @interface MJPhotoBrowser () 18 | @property (strong, nonatomic) UIView *view; 19 | @property (strong, nonatomic) UIScrollView *photoScrollView; 20 | @property (strong, nonatomic) NSMutableSet *visiblePhotoViews, *reusablePhotoViews; 21 | @property (strong, nonatomic) MJPhotoToolbar *toolbar; 22 | @end 23 | 24 | @implementation MJPhotoBrowser 25 | 26 | #pragma mark - init M 27 | 28 | - (instancetype)init 29 | { 30 | self = [super init]; 31 | if (self) { 32 | _showSaveBtn = YES; 33 | } 34 | return self; 35 | } 36 | 37 | #pragma mark - get M 38 | 39 | - (UIView *)view{ 40 | if (!_view) { 41 | _view = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.bounds]; 42 | _view.backgroundColor = [UIColor blackColor]; 43 | } 44 | return _view; 45 | } 46 | 47 | - (UIScrollView *)photoScrollView{ 48 | if (!_photoScrollView) { 49 | CGRect frame = self.view.bounds; 50 | frame.origin.x -= kPadding; 51 | frame.size.width += (2 * kPadding); 52 | _photoScrollView = [[UIScrollView alloc] initWithFrame:frame]; 53 | _photoScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 54 | _photoScrollView.pagingEnabled = YES; 55 | _photoScrollView.delegate = self; 56 | _photoScrollView.showsHorizontalScrollIndicator = NO; 57 | _photoScrollView.showsVerticalScrollIndicator = NO; 58 | _photoScrollView.backgroundColor = [UIColor clearColor]; 59 | } 60 | return _photoScrollView; 61 | } 62 | 63 | - (MJPhotoToolbar *)toolbar{ 64 | if (!_toolbar) { 65 | CGFloat barHeight = 49; 66 | CGFloat barY = self.view.frame.size.height - barHeight; 67 | _toolbar = [[MJPhotoToolbar alloc] init]; 68 | _toolbar.showSaveBtn = _showSaveBtn; 69 | _toolbar.frame = CGRectMake(0, barY, self.view.frame.size.width, barHeight); 70 | _toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; 71 | } 72 | return _toolbar; 73 | } 74 | 75 | - (void)show 76 | { 77 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 78 | 79 | //初始化数据 80 | { 81 | if (!_visiblePhotoViews) { 82 | _visiblePhotoViews = [NSMutableSet set]; 83 | } 84 | if (!_reusablePhotoViews) { 85 | _reusablePhotoViews = [NSMutableSet set]; 86 | } 87 | self.toolbar.photos = self.photos; 88 | 89 | 90 | CGRect frame = self.view.bounds; 91 | frame.origin.x -= kPadding; 92 | frame.size.width += (2 * kPadding); 93 | self.photoScrollView.contentSize = CGSizeMake(frame.size.width * self.photos.count, 0); 94 | self.photoScrollView.contentOffset = CGPointMake(self.currentPhotoIndex * frame.size.width, 0); 95 | 96 | [self.view addSubview:self.photoScrollView]; 97 | [self.view addSubview:self.toolbar]; 98 | [self updateTollbarState]; 99 | [self showPhotos]; 100 | } 101 | //渐变显示 102 | self.view.alpha = 0; 103 | [[UIApplication sharedApplication].keyWindow addSubview:self.view]; 104 | [UIView animateWithDuration:0.3 animations:^{ 105 | self.view.alpha = 1.0; 106 | } completion:^(BOOL finished) { 107 | [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade]; 108 | }]; 109 | } 110 | 111 | #pragma mark - set M 112 | - (void)setPhotos:(NSArray *)photos 113 | { 114 | _photos = photos; 115 | if (_photos.count <= 0) { 116 | return; 117 | } 118 | for (int i = 0; i<_photos.count; i++) { 119 | MJPhoto *photo = _photos[i]; 120 | photo.index = i; 121 | } 122 | } 123 | 124 | - (void)setCurrentPhotoIndex:(NSUInteger)currentPhotoIndex 125 | { 126 | _currentPhotoIndex = currentPhotoIndex; 127 | 128 | if (_photoScrollView) { 129 | _photoScrollView.contentOffset = CGPointMake(_currentPhotoIndex * _photoScrollView.frame.size.width, 0); 130 | 131 | // 显示所有的相片 132 | [self showPhotos]; 133 | } 134 | } 135 | 136 | #pragma mark - Show Photos 137 | - (void)showPhotos 138 | { 139 | CGRect visibleBounds = _photoScrollView.bounds; 140 | int firstIndex = (int)floorf((CGRectGetMinX(visibleBounds)+kPadding*2) / CGRectGetWidth(visibleBounds)); 141 | int lastIndex = (int)floorf((CGRectGetMaxX(visibleBounds)-kPadding*2-1) / CGRectGetWidth(visibleBounds)); 142 | if (firstIndex < 0) firstIndex = 0; 143 | if (firstIndex >= _photos.count) firstIndex = (int)_photos.count - 1; 144 | if (lastIndex < 0) lastIndex = 0; 145 | if (lastIndex >= _photos.count) lastIndex = (int)_photos.count - 1; 146 | 147 | // 回收不再显示的ImageView 148 | NSInteger photoViewIndex; 149 | for (MJPhotoView *photoView in _visiblePhotoViews) { 150 | photoViewIndex = kPhotoViewIndex(photoView); 151 | if (photoViewIndex < firstIndex || photoViewIndex > lastIndex) { 152 | [_reusablePhotoViews addObject:photoView]; 153 | [photoView removeFromSuperview]; 154 | } 155 | } 156 | 157 | [_visiblePhotoViews minusSet:_reusablePhotoViews]; 158 | while (_reusablePhotoViews.count > 2) { 159 | [_reusablePhotoViews removeObject:[_reusablePhotoViews anyObject]]; 160 | } 161 | 162 | for (NSUInteger index = firstIndex; index <= lastIndex; index++) { 163 | if (![self isShowingPhotoViewAtIndex:index]) { 164 | [self showPhotoViewAtIndex:(int)index]; 165 | } 166 | } 167 | 168 | } 169 | 170 | // 显示一个图片view 171 | - (void)showPhotoViewAtIndex:(int)index 172 | { 173 | MJPhotoView *photoView = [self dequeueReusablePhotoView]; 174 | if (!photoView) { // 添加新的图片view 175 | photoView = [[MJPhotoView alloc] init]; 176 | photoView.photoViewDelegate = self; 177 | } 178 | 179 | // 调整当前页的frame 180 | CGRect bounds = _photoScrollView.bounds; 181 | CGRect photoViewFrame = bounds; 182 | photoViewFrame.size.width -= (2 * kPadding); 183 | photoViewFrame.origin.x = (bounds.size.width * index) + kPadding; 184 | photoView.tag = kPhotoViewTagOffset + index; 185 | 186 | MJPhoto *photo = _photos[index]; 187 | photoView.frame = photoViewFrame; 188 | photoView.photo = photo; 189 | 190 | [_visiblePhotoViews addObject:photoView]; 191 | [_photoScrollView addSubview:photoView]; 192 | 193 | [self loadImageNearIndex:index]; 194 | } 195 | 196 | // 加载index附近的图片 197 | - (void)loadImageNearIndex:(int)index 198 | { 199 | if (index > 0) { 200 | MJPhoto *photo = _photos[index - 1]; 201 | [[SDWebImageManager sharedManager] downloadImageWithURL:photo.url options:SDWebImageRetryFailed|SDWebImageLowPriority progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 202 | //do nothing 203 | }]; 204 | } 205 | 206 | if (index < _photos.count - 1) { 207 | MJPhoto *photo = _photos[index + 1]; 208 | [[SDWebImageManager sharedManager] downloadImageWithURL:photo.url options:SDWebImageRetryFailed|SDWebImageLowPriority progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 209 | //do nothing 210 | }]; 211 | } 212 | } 213 | 214 | // index这页是否正在显示 215 | - (BOOL)isShowingPhotoViewAtIndex:(NSUInteger)index { 216 | for (MJPhotoView *photoView in _visiblePhotoViews) { 217 | if (kPhotoViewIndex(photoView) == index) { 218 | return YES; 219 | } 220 | } 221 | return NO; 222 | } 223 | // 重用页面 224 | - (MJPhotoView *)dequeueReusablePhotoView 225 | { 226 | MJPhotoView *photoView = [_reusablePhotoViews anyObject]; 227 | if (photoView) { 228 | [_reusablePhotoViews removeObject:photoView]; 229 | } 230 | return photoView; 231 | } 232 | 233 | #pragma mark - updateTollbarState 234 | - (void)updateTollbarState 235 | { 236 | _currentPhotoIndex = _photoScrollView.contentOffset.x / _photoScrollView.frame.size.width; 237 | _toolbar.currentPhotoIndex = _currentPhotoIndex; 238 | } 239 | 240 | 241 | 242 | #pragma mark - MJPhotoViewDelegate 243 | - (void)photoViewSingleTap:(MJPhotoView *)photoView 244 | { 245 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone]; 246 | // 移除工具条 247 | [self.toolbar removeFromSuperview]; 248 | 249 | [UIView animateWithDuration:0.3 animations:^{ 250 | self.view.alpha = 0; 251 | } completion:^(BOOL finished) { 252 | [self.view removeFromSuperview]; 253 | }]; 254 | } 255 | 256 | - (void)photoViewImageFinishLoad:(MJPhotoView *)photoView 257 | { 258 | [self updateTollbarState]; 259 | } 260 | 261 | #pragma mark - UIScrollView Delegate 262 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 263 | [self showPhotos]; 264 | [self updateTollbarState]; 265 | } 266 | 267 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoLoadingView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoLoadingView.h 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #define kMinProgress 0.0001 11 | 12 | @class MJPhotoBrowser; 13 | @class MJPhoto; 14 | 15 | @interface MJPhotoLoadingView : UIView 16 | @property (nonatomic) float progress; 17 | 18 | - (void)showLoading; 19 | - (void)showFailure; 20 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoLoadingView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoLoadingView.m 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import "MJPhotoLoadingView.h" 9 | #import "MJPhotoBrowser.h" 10 | #import 11 | #import "MJPhotoProgressView.h" 12 | 13 | @interface MJPhotoLoadingView () 14 | { 15 | UILabel *_failureLabel; 16 | MJPhotoProgressView *_progressView; 17 | } 18 | 19 | @end 20 | 21 | @implementation MJPhotoLoadingView 22 | 23 | - (void)setFrame:(CGRect)frame 24 | { 25 | [super setFrame:[UIScreen mainScreen].bounds]; 26 | } 27 | 28 | - (void)showFailure 29 | { 30 | [_progressView removeFromSuperview]; 31 | 32 | if (_failureLabel == nil) { 33 | _failureLabel = [[UILabel alloc] init]; 34 | _failureLabel.bounds = CGRectMake(0, 0, self.bounds.size.width, 44); 35 | _failureLabel.textAlignment = NSTextAlignmentCenter; 36 | _failureLabel.center = self.center; 37 | _failureLabel.text = @"网络不给力,图片下载失败"; 38 | _failureLabel.font = [UIFont boldSystemFontOfSize:20]; 39 | _failureLabel.textColor = [UIColor whiteColor]; 40 | _failureLabel.backgroundColor = [UIColor clearColor]; 41 | _failureLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 42 | } 43 | [self addSubview:_failureLabel]; 44 | } 45 | 46 | - (void)showLoading 47 | { 48 | [_failureLabel removeFromSuperview]; 49 | 50 | if (_progressView == nil) { 51 | _progressView = [[MJPhotoProgressView alloc] init]; 52 | _progressView.bounds = CGRectMake( 0, 0, 60, 60); 53 | _progressView.center = self.center; 54 | } 55 | _progressView.progress = kMinProgress; 56 | [self addSubview:_progressView]; 57 | } 58 | 59 | #pragma mark - customlize method 60 | - (void)setProgress:(float)progress 61 | { 62 | _progress = progress; 63 | _progressView.progress = progress; 64 | if (progress >= 1.0) { 65 | [_progressView removeFromSuperview]; 66 | } 67 | } 68 | @end 69 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoProgressView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoProgressView.h 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface MJPhotoProgressView : UIView 11 | 12 | @property(nonatomic, strong) UIColor *trackTintColor; 13 | @property(nonatomic, strong) UIColor *progressTintColor; 14 | @property (nonatomic) float progress; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoProgressView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoProgressView.m 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import "MJPhotoProgressView.h" 9 | 10 | #define kDegreeToRadian(x) (M_PI/180.0 * (x)) 11 | 12 | @implementation MJPhotoProgressView 13 | 14 | - (id)initWithFrame:(CGRect)frame 15 | { 16 | if (self = [super initWithFrame:frame]) { 17 | self.backgroundColor = [UIColor clearColor]; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)drawRect:(CGRect)rect 23 | { 24 | CGPoint centerPoint = CGPointMake(rect.size.height / 2, rect.size.width / 2); 25 | CGFloat radius = MIN(rect.size.height, rect.size.width) / 2; 26 | 27 | CGFloat pathWidth = radius * 0.3f; 28 | 29 | CGFloat radians = kDegreeToRadian((_progress*359.9)-90); 30 | CGFloat xOffset = radius*(1 + 0.85*cosf(radians)); 31 | CGFloat yOffset = radius*(1 + 0.85*sinf(radians)); 32 | CGPoint endPoint = CGPointMake(xOffset, yOffset); 33 | 34 | CGContextRef context = UIGraphicsGetCurrentContext(); 35 | 36 | [self.trackTintColor setFill]; 37 | CGMutablePathRef trackPath = CGPathCreateMutable(); 38 | CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y); 39 | CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, kDegreeToRadian(270), kDegreeToRadian(-90), NO); 40 | CGPathCloseSubpath(trackPath); 41 | CGContextAddPath(context, trackPath); 42 | CGContextFillPath(context); 43 | CGPathRelease(trackPath); 44 | 45 | [self.progressTintColor setFill]; 46 | CGMutablePathRef progressPath = CGPathCreateMutable(); 47 | CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y); 48 | CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, kDegreeToRadian(270), radians, NO); 49 | CGPathCloseSubpath(progressPath); 50 | CGContextAddPath(context, progressPath); 51 | CGContextFillPath(context); 52 | CGPathRelease(progressPath); 53 | 54 | CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth/2, 0, pathWidth, pathWidth)); 55 | CGContextFillPath(context); 56 | 57 | CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth/2, endPoint.y - pathWidth/2, pathWidth, pathWidth)); 58 | CGContextFillPath(context); 59 | 60 | CGContextSetBlendMode(context, kCGBlendModeClear);; 61 | CGFloat innerRadius = radius * 0.7; 62 | CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius); 63 | CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius*2, innerRadius*2)); 64 | CGContextFillPath(context); 65 | } 66 | 67 | #pragma mark - Property Methods 68 | 69 | - (UIColor *)trackTintColor 70 | { 71 | if (!_trackTintColor) 72 | { 73 | _trackTintColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.7f]; 74 | } 75 | return _trackTintColor; 76 | } 77 | 78 | - (UIColor *)progressTintColor 79 | { 80 | if (!_progressTintColor) 81 | { 82 | _progressTintColor = [UIColor whiteColor]; 83 | } 84 | return _progressTintColor; 85 | } 86 | 87 | - (void)setProgress:(float)progress 88 | { 89 | _progress = progress; 90 | [self setNeedsDisplay]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoToolbar.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoToolbar.h 3 | // FingerNews 4 | // 5 | // Created by mj on 13-9-24. 6 | // Copyright (c) 2013年 itcast. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJPhotoToolbar : UIView 12 | // 所有的图片对象 13 | @property (nonatomic, strong) NSArray *photos; 14 | // 当前展示的图片索引 15 | @property (nonatomic, assign) NSUInteger currentPhotoIndex; 16 | @property (nonatomic, assign) NSUInteger showSaveBtn; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoToolbar.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPhotoToolbar.m 3 | // FingerNews 4 | // 5 | // Created by mj on 13-9-24. 6 | // Copyright (c) 2013年 itcast. All rights reserved. 7 | // 8 | 9 | #import "MJPhotoToolbar.h" 10 | #import "MJPhoto.h" 11 | 12 | @interface MJPhotoToolbar() 13 | { 14 | // 显示页码 15 | UILabel *_indexLabel; 16 | UIButton *_saveImageBtn; 17 | } 18 | @end 19 | 20 | @implementation MJPhotoToolbar 21 | 22 | - (id)initWithFrame:(CGRect)frame 23 | { 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | // Initialization code 27 | } 28 | return self; 29 | } 30 | 31 | - (void)setPhotos:(NSArray *)photos 32 | { 33 | _photos = photos; 34 | 35 | if (_photos.count > 1) { 36 | _indexLabel = [[UILabel alloc] init]; 37 | _indexLabel.font = [UIFont boldSystemFontOfSize:20]; 38 | _indexLabel.frame = self.bounds; 39 | _indexLabel.backgroundColor = [UIColor clearColor]; 40 | _indexLabel.textColor = [UIColor whiteColor]; 41 | _indexLabel.textAlignment = NSTextAlignmentCenter; 42 | _indexLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 43 | [self addSubview:_indexLabel]; 44 | } 45 | 46 | // 保存图片按钮 47 | CGFloat btnWidth = self.bounds.size.height; 48 | _saveImageBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 49 | _saveImageBtn.frame = CGRectMake(20, 0, btnWidth, btnWidth); 50 | _saveImageBtn.autoresizingMask = UIViewAutoresizingFlexibleHeight; 51 | [_saveImageBtn setImage:[UIImage imageNamed:@"MJPhotoBrowser.bundle/save_icon.png"] forState:UIControlStateNormal]; 52 | [_saveImageBtn setImage:[UIImage imageNamed:@"MJPhotoBrowser.bundle/save_icon_highlighted.png"] forState:UIControlStateHighlighted]; 53 | [_saveImageBtn addTarget:self action:@selector(saveImage) forControlEvents:UIControlEventTouchUpInside]; 54 | [self addSubview:_saveImageBtn]; 55 | } 56 | 57 | - (void)saveImage 58 | { 59 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 60 | MJPhoto *photo = _photos[_currentPhotoIndex]; 61 | UIImageWriteToSavedPhotosAlbum(photo.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 62 | }); 63 | } 64 | 65 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo 66 | { 67 | if (error) { 68 | [SVProgressHUD showErrorWithStatus:@"保存失败"]; 69 | } else { 70 | MJPhoto *photo = _photos[_currentPhotoIndex]; 71 | photo.save = YES; 72 | _saveImageBtn.enabled = NO; 73 | [SVProgressHUD showSuccessWithStatus:@"成功保存到相册"]; 74 | } 75 | } 76 | 77 | - (void)setCurrentPhotoIndex:(NSUInteger)currentPhotoIndex 78 | { 79 | _currentPhotoIndex = currentPhotoIndex; 80 | 81 | // 更新页码 82 | _indexLabel.text = [NSString stringWithFormat:@"%d / %d", (int)_currentPhotoIndex + 1, (int)_photos.count]; 83 | 84 | MJPhoto *photo = _photos[_currentPhotoIndex]; 85 | // 按钮 86 | _saveImageBtn.enabled = photo.image != nil && !photo.save; 87 | _saveImageBtn.hidden =!_showSaveBtn; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJZoomingScrollView.h 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class MJPhotoBrowser, MJPhoto, MJPhotoView; 11 | 12 | @protocol MJPhotoViewDelegate 13 | - (void)photoViewImageFinishLoad:(MJPhotoView *)photoView; 14 | - (void)photoViewSingleTap:(MJPhotoView *)photoView; 15 | @end 16 | 17 | @interface MJPhotoView : UIScrollView 18 | // 图片 19 | @property (nonatomic, strong) MJPhoto *photo; 20 | // 代理 21 | @property (nonatomic, strong) id photoViewDelegate; 22 | 23 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/MJPhotoBrowser/MJPhotoView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJZoomingScrollView.m 3 | // 4 | // Created by mj on 13-3-4. 5 | // Copyright (c) 2013年 itcast. All rights reserved. 6 | // 7 | 8 | #import "MJPhotoView.h" 9 | #import "MJPhoto.h" 10 | #import "MJPhotoLoadingView.h" 11 | #import 12 | 13 | #define ESWeak(var, weakVar) __weak __typeof(&*var) weakVar = var 14 | #define ESStrong_DoNotCheckNil(weakVar, _var) __typeof(&*weakVar) _var = weakVar 15 | #define ESStrong(weakVar, _var) ESStrong_DoNotCheckNil(weakVar, _var); if (!_var) return; 16 | 17 | #define ESWeak_(var) ESWeak(var, weak_##var); 18 | #define ESStrong_(var) ESStrong(weak_##var, _##var); 19 | 20 | /** defines a weak `self` named `__weakSelf` */ 21 | #define ESWeakSelf ESWeak(self, __weakSelf); 22 | /** defines a strong `self` named `_self` from `__weakSelf` */ 23 | #define ESStrongSelf ESStrong(__weakSelf, _self); 24 | 25 | @interface MJPhotoView () 26 | { 27 | BOOL _zoomByDoubleTap; 28 | YLImageView *_imageView; 29 | MJPhotoLoadingView *_photoLoadingView; 30 | } 31 | @end 32 | 33 | @implementation MJPhotoView 34 | 35 | - (id)initWithFrame:(CGRect)frame 36 | { 37 | if ((self = [super initWithFrame:frame])) { 38 | self.clipsToBounds = YES; 39 | // 图片 40 | _imageView = [[YLImageView alloc] init]; 41 | _imageView.backgroundColor = [UIColor blackColor]; 42 | _imageView.contentMode = UIViewContentModeScaleAspectFit; 43 | [self addSubview:_imageView]; 44 | 45 | // 进度条 46 | _photoLoadingView = [[MJPhotoLoadingView alloc] init]; 47 | 48 | // 属性 49 | self.delegate = self; 50 | // self.showsHorizontalScrollIndicator = NO; 51 | // self.showsVerticalScrollIndicator = NO; 52 | self.decelerationRate = UIScrollViewDecelerationRateFast; 53 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 54 | 55 | // 监听点击 56 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; 57 | singleTap.delaysTouchesBegan = YES; 58 | singleTap.numberOfTapsRequired = 1; 59 | [self addGestureRecognizer:singleTap]; 60 | 61 | UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; 62 | doubleTap.numberOfTapsRequired = 2; 63 | [self addGestureRecognizer:doubleTap]; 64 | 65 | [singleTap requireGestureRecognizerToFail:doubleTap]; 66 | } 67 | return self; 68 | } 69 | 70 | //设置imageView的图片 71 | - (void)configImageViewWithImage:(UIImage *)image{ 72 | _imageView.image = image; 73 | } 74 | 75 | 76 | #pragma mark - photoSetter 77 | - (void)setPhoto:(MJPhoto *)photo { 78 | _photo = photo; 79 | 80 | [self showImage]; 81 | } 82 | 83 | #pragma mark 显示图片 84 | - (void)showImage 85 | { 86 | [self photoStartLoad]; 87 | 88 | [self adjustFrame]; 89 | } 90 | 91 | #pragma mark 开始加载图片 92 | - (void)photoStartLoad 93 | { 94 | if (_photo.image) { 95 | [_photoLoadingView removeFromSuperview]; 96 | _imageView.image = _photo.image; 97 | self.scrollEnabled = YES; 98 | } else { 99 | _imageView.image = _photo.placeholder; 100 | self.scrollEnabled = NO; 101 | // 直接显示进度条 102 | [_photoLoadingView showLoading]; 103 | [self addSubview:_photoLoadingView]; 104 | 105 | ESWeakSelf; 106 | ESWeak_(_photoLoadingView); 107 | ESWeak_(_imageView); 108 | 109 | [SDWebImageManager.sharedManager downloadImageWithURL:_photo.url options:SDWebImageRetryFailed| SDWebImageLowPriority| SDWebImageHandleCookies progress:^(NSInteger receivedSize, NSInteger expectedSize) { 110 | ESStrong_(_photoLoadingView); 111 | if (receivedSize > kMinProgress) { 112 | __photoLoadingView.progress = (float)receivedSize/expectedSize; 113 | } 114 | } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 115 | ESStrongSelf; 116 | ESStrong_(_imageView); 117 | __imageView.image = image; 118 | [_self photoDidFinishLoadWithImage:image]; 119 | }]; 120 | } 121 | } 122 | 123 | #pragma mark 加载完毕 124 | - (void)photoDidFinishLoadWithImage:(UIImage *)image 125 | { 126 | if (image) { 127 | self.scrollEnabled = YES; 128 | _photo.image = image; 129 | [_photoLoadingView removeFromSuperview]; 130 | 131 | if ([self.photoViewDelegate respondsToSelector:@selector(photoViewImageFinishLoad:)]) { 132 | [self.photoViewDelegate photoViewImageFinishLoad:self]; 133 | } 134 | } else { 135 | [self addSubview:_photoLoadingView]; 136 | [_photoLoadingView showFailure]; 137 | } 138 | 139 | // 设置缩放比例 140 | [self adjustFrame]; 141 | } 142 | #pragma mark 调整frame 143 | - (void)adjustFrame 144 | { 145 | if (_imageView.image == nil) return; 146 | 147 | // 基本尺寸参数 148 | CGFloat boundsWidth = self.bounds.size.width; 149 | CGFloat boundsHeight = self.bounds.size.height; 150 | CGFloat imageWidth = _imageView.image.size.width; 151 | CGFloat imageHeight = _imageView.image.size.height; 152 | 153 | // 设置伸缩比例 154 | CGFloat imageScale = boundsWidth / imageWidth; 155 | CGFloat minScale = MIN(1.0, imageScale); 156 | 157 | CGFloat maxScale = 2.0; 158 | if ([UIScreen instancesRespondToSelector:@selector(scale)]) { 159 | maxScale = maxScale / [[UIScreen mainScreen] scale]; 160 | } 161 | self.maximumZoomScale = maxScale; 162 | self.minimumZoomScale = minScale; 163 | self.zoomScale = minScale; 164 | 165 | CGRect imageFrame = CGRectMake(0, MAX(0, (boundsHeight- imageHeight*imageScale)/2), boundsWidth, imageHeight *imageScale); 166 | 167 | self.contentSize = CGSizeMake(CGRectGetWidth(imageFrame), CGRectGetHeight(imageFrame)); 168 | _imageView.frame = imageFrame; 169 | } 170 | 171 | #pragma mark - UIScrollViewDelegate 172 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 173 | if (_zoomByDoubleTap) { 174 | CGFloat insetY = (CGRectGetHeight(self.bounds) - CGRectGetHeight(_imageView.frame))/2; 175 | insetY = MAX(insetY, 0.0); 176 | if (ABS(_imageView.frame.origin.y - insetY) > 0.5) { 177 | CGRect imageViewFrame = _imageView.frame; 178 | imageViewFrame = CGRectMake(imageViewFrame.origin.x, insetY, imageViewFrame.size.width, imageViewFrame.size.height); 179 | _imageView.frame = imageViewFrame; 180 | } 181 | } 182 | return _imageView; 183 | } 184 | 185 | - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale{ 186 | _zoomByDoubleTap = NO; 187 | CGFloat insetY = (CGRectGetHeight(self.bounds) - CGRectGetHeight(_imageView.frame))/2; 188 | insetY = MAX(insetY, 0.0); 189 | if (ABS(_imageView.frame.origin.y - insetY) > 0.5) { 190 | [UIView animateWithDuration:0.2 animations:^{ 191 | CGRect imageViewFrame = _imageView.frame; 192 | imageViewFrame = CGRectMake(imageViewFrame.origin.x, insetY, imageViewFrame.size.width, imageViewFrame.size.height); 193 | _imageView.frame = imageViewFrame; 194 | }]; 195 | } 196 | } 197 | 198 | #pragma mark - 手势处理 199 | //单击隐藏 200 | - (void)handleSingleTap:(UITapGestureRecognizer *)tap { 201 | // 移除进度条 202 | [_photoLoadingView removeFromSuperview]; 203 | 204 | // 通知代理 205 | if ([self.photoViewDelegate respondsToSelector:@selector(photoViewSingleTap:)]) { 206 | [self.photoViewDelegate photoViewSingleTap:self]; 207 | } 208 | } 209 | //双击放大 210 | - (void)handleDoubleTap:(UITapGestureRecognizer *)tap { 211 | _zoomByDoubleTap = YES; 212 | 213 | if (self.zoomScale == self.maximumZoomScale) { 214 | [self setZoomScale:self.minimumZoomScale animated:YES]; 215 | } else { 216 | CGPoint touchPoint = [tap locationInView:self]; 217 | CGFloat scale = self.maximumZoomScale/ self.zoomScale; 218 | CGRect rectTozoom=CGRectMake(touchPoint.x * scale, touchPoint.y * scale, 1, 1); 219 | [self zoomToRect:rectTozoom animated:YES]; 220 | } 221 | } 222 | 223 | - (void)dealloc 224 | { 225 | // 取消请求 226 | [_imageView sd_setImageWithURL:[NSURL URLWithString:@"file:///abc"]]; 227 | } 228 | @end -------------------------------------------------------------------------------- /MJPhotoBrowser/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MJPhotoBrowser 4 | // 5 | // Created by Sunnyyoung on 15/5/22. 6 | // Copyright (c) 2015年 Sunnyyoung. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '6.0' 2 | 3 | pod 'SDWebImage' 4 | pod 'SVProgressHUD' 5 | pod 'YLGIFImage' -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SDWebImage (3.7.2): 3 | - SDWebImage/Core (= 3.7.2) 4 | - SDWebImage/Core (3.7.2) 5 | - SVProgressHUD (1.1) 6 | - YLGIFImage (0.11) 7 | 8 | DEPENDENCIES: 9 | - SDWebImage 10 | - SVProgressHUD 11 | - YLGIFImage 12 | 13 | SPEC CHECKSUMS: 14 | SDWebImage: 71b7cdc1d1721d6a82ed62889030225f2c249e29 15 | SVProgressHUD: 54de4d9ab38aa3f688da401f5766a5738084e7fb 16 | YLGIFImage: c0ae132fe80e5282b2762a97d14e23ba71a748ae 17 | 18 | COCOAPODS: 0.38.2 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MJPhotoBrowser增强版 2 | 3 | ## 来源 4 | 5 | - https://github.com/StephenZhuang/MJPhotoBrowser 6 | - https://github.com/azxfire/MJPhotoBrowser 7 | 8 | ## 改进 9 | 10 | 1. 修复缩放图片导致图片位置偏下方的Bug 11 | 2. 修改MBProgressHUD为SVProgressHUD 12 | 3. 增加GIF图片播放支持 13 | 4. 其它忘记咯~都是一些小Bug的修复之类的~ 14 | --------------------------------------------------------------------------------