├── .gitignore
├── .gitmodules
├── Carthage
└── Build
│ └── iOS
│ └── Haneke.framework.dSYM
│ └── Contents
│ ├── Info.plist
│ └── Resources
│ └── DWARF
│ └── Haneke
├── ImageViewer.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── ImageViewer.xccheckout
│ └── xcuserdata
│ │ ├── quentindequelen.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── tannghiala.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ └── ImageViewer.xcscheme
└── xcuserdata
│ ├── quentindequelen.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── tannghiala.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── ImageViewer
├── ImageViewer.swift
├── Images.xcassets
│ └── Close.imageset
│ │ ├── Close.png
│ │ ├── Close@2x.png
│ │ ├── Close@3x.png
│ │ └── Contents.json
├── Info.plist
└── UIImageView+ImageViewer.swift
├── ImageViewerTests
├── ImageViewerTests.swift
└── Info.plist
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | Carthage/Build/iOS/ImageViewer.framework/Assets.car
2 | Carthage/Build/iOS/ImageViewer.framework/ImageViewer
3 | Carthage/Build/iOS/ImageViewer.framework/Info.plist
4 | Carthage/Build/iOS/ImageViewer.framework/_CodeSignature/CodeResources
5 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Haneke
6 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Info.plist
7 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/_CodeSignature/CodeResources
8 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Headers/Haneke-Swift.h
9 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Headers/Haneke.h
10 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/module.modulemap
11 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/arm.swiftdoc
12 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/arm.swiftmodule
13 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/arm64.swiftdoc
14 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/arm64.swiftmodule
15 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/i386.swiftdoc
16 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/i386.swiftmodule
17 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/x86_64.swiftdoc
18 | Carthage/Build/iOS/ImageViewer.framework/Frameworks/Haneke.framework/Modules/Haneke.swiftmodule/x86_64.swiftmodule
19 | Carthage/Build/iOS/ImageViewer.framework/Headers/ImageViewer-Swift.h
20 | Carthage/Build/iOS/ImageViewer.framework/Headers/ImageViewer.h
21 | Carthage/Build/iOS/ImageViewer.framework/Modules/module.modulemap
22 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/arm.swiftdoc
23 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/arm.swiftmodule
24 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/arm64.swiftdoc
25 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/arm64.swiftmodule
26 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/i386.swiftdoc
27 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/i386.swiftmodule
28 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/x86_64.swiftdoc
29 | Carthage/Build/iOS/ImageViewer.framework/Modules/ImageViewer.swiftmodule/x86_64.swiftmodule
30 | .DS_Store
31 | **/.DS_Store
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Carthage/Checkouts/HanekeSwift"]
2 | path = Carthage/Checkouts/HanekeSwift
3 | url = https://github.com/Haneke/HanekeSwift.git
4 |
--------------------------------------------------------------------------------
/Carthage/Build/iOS/Haneke.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.io.haneke.Haneke
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/iOS/Haneke.framework.dSYM/Contents/Resources/DWARF/Haneke:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/Carthage/Build/iOS/Haneke.framework.dSYM/Contents/Resources/DWARF/Haneke
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C68A2CCC1AF64F68000E8A94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C68A2CCB1AF64F68000E8A94 /* Images.xcassets */; };
11 | C6A0AC131AF641D800B79957 /* ImageViewer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6A0AC071AF641D800B79957 /* ImageViewer.framework */; };
12 | C6A0AC1A1AF641D800B79957 /* ImageViewerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A0AC191AF641D800B79957 /* ImageViewerTests.swift */; };
13 | C6A0AC241AF6424400B79957 /* UIImageView+ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A0AC231AF6424400B79957 /* UIImageView+ImageViewer.swift */; };
14 | C6A0AC261AF6426600B79957 /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A0AC251AF6426600B79957 /* ImageViewer.swift */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXContainerItemProxy section */
18 | C6A0AC141AF641D800B79957 /* PBXContainerItemProxy */ = {
19 | isa = PBXContainerItemProxy;
20 | containerPortal = C6A0ABFE1AF641D800B79957 /* Project object */;
21 | proxyType = 1;
22 | remoteGlobalIDString = C6A0AC061AF641D800B79957;
23 | remoteInfo = ImageViewer;
24 | };
25 | /* End PBXContainerItemProxy section */
26 |
27 | /* Begin PBXFileReference section */
28 | C68A2CCB1AF64F68000E8A94 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
29 | C6A0AC071AF641D800B79957 /* ImageViewer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageViewer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
30 | C6A0AC0B1AF641D800B79957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | C6A0AC121AF641D800B79957 /* ImageViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageViewerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
32 | C6A0AC181AF641D800B79957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | C6A0AC191AF641D800B79957 /* ImageViewerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewerTests.swift; sourceTree = ""; };
34 | C6A0AC231AF6424400B79957 /* UIImageView+ImageViewer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+ImageViewer.swift"; sourceTree = ""; };
35 | C6A0AC251AF6426600B79957 /* ImageViewer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | C6A0AC031AF641D800B79957 /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | C6A0AC0F1AF641D800B79957 /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | C6A0AC131AF641D800B79957 /* ImageViewer.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | C6A0ABFD1AF641D800B79957 = {
58 | isa = PBXGroup;
59 | children = (
60 | C6A0AC091AF641D800B79957 /* ImageViewer */,
61 | C6A0AC161AF641D800B79957 /* ImageViewerTests */,
62 | C6A0AC081AF641D800B79957 /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | C6A0AC081AF641D800B79957 /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | C6A0AC071AF641D800B79957 /* ImageViewer.framework */,
70 | C6A0AC121AF641D800B79957 /* ImageViewerTests.xctest */,
71 | );
72 | name = Products;
73 | sourceTree = "";
74 | };
75 | C6A0AC091AF641D800B79957 /* ImageViewer */ = {
76 | isa = PBXGroup;
77 | children = (
78 | C6A0AC231AF6424400B79957 /* UIImageView+ImageViewer.swift */,
79 | C6A0AC251AF6426600B79957 /* ImageViewer.swift */,
80 | C6A0AC0A1AF641D800B79957 /* Supporting Files */,
81 | );
82 | path = ImageViewer;
83 | sourceTree = "";
84 | };
85 | C6A0AC0A1AF641D800B79957 /* Supporting Files */ = {
86 | isa = PBXGroup;
87 | children = (
88 | C6A0AC0B1AF641D800B79957 /* Info.plist */,
89 | C68A2CCB1AF64F68000E8A94 /* Images.xcassets */,
90 | );
91 | name = "Supporting Files";
92 | sourceTree = "";
93 | };
94 | C6A0AC161AF641D800B79957 /* ImageViewerTests */ = {
95 | isa = PBXGroup;
96 | children = (
97 | C6A0AC191AF641D800B79957 /* ImageViewerTests.swift */,
98 | C6A0AC171AF641D800B79957 /* Supporting Files */,
99 | );
100 | path = ImageViewerTests;
101 | sourceTree = "";
102 | };
103 | C6A0AC171AF641D800B79957 /* Supporting Files */ = {
104 | isa = PBXGroup;
105 | children = (
106 | C6A0AC181AF641D800B79957 /* Info.plist */,
107 | );
108 | name = "Supporting Files";
109 | sourceTree = "";
110 | };
111 | /* End PBXGroup section */
112 |
113 | /* Begin PBXHeadersBuildPhase section */
114 | C6A0AC041AF641D800B79957 /* Headers */ = {
115 | isa = PBXHeadersBuildPhase;
116 | buildActionMask = 2147483647;
117 | files = (
118 | );
119 | runOnlyForDeploymentPostprocessing = 0;
120 | };
121 | /* End PBXHeadersBuildPhase section */
122 |
123 | /* Begin PBXNativeTarget section */
124 | C6A0AC061AF641D800B79957 /* ImageViewer */ = {
125 | isa = PBXNativeTarget;
126 | buildConfigurationList = C6A0AC1D1AF641D800B79957 /* Build configuration list for PBXNativeTarget "ImageViewer" */;
127 | buildPhases = (
128 | C6A0AC021AF641D800B79957 /* Sources */,
129 | C6A0AC031AF641D800B79957 /* Frameworks */,
130 | C6A0AC041AF641D800B79957 /* Headers */,
131 | C6A0AC051AF641D800B79957 /* Resources */,
132 | C6805E111AF678BD005FB3EE /* ShellScript */,
133 | );
134 | buildRules = (
135 | );
136 | dependencies = (
137 | );
138 | name = ImageViewer;
139 | productName = ImageViewer;
140 | productReference = C6A0AC071AF641D800B79957 /* ImageViewer.framework */;
141 | productType = "com.apple.product-type.framework";
142 | };
143 | C6A0AC111AF641D800B79957 /* ImageViewerTests */ = {
144 | isa = PBXNativeTarget;
145 | buildConfigurationList = C6A0AC201AF641D800B79957 /* Build configuration list for PBXNativeTarget "ImageViewerTests" */;
146 | buildPhases = (
147 | C6A0AC0E1AF641D800B79957 /* Sources */,
148 | C6A0AC0F1AF641D800B79957 /* Frameworks */,
149 | C6A0AC101AF641D800B79957 /* Resources */,
150 | );
151 | buildRules = (
152 | );
153 | dependencies = (
154 | C6A0AC151AF641D800B79957 /* PBXTargetDependency */,
155 | );
156 | name = ImageViewerTests;
157 | productName = ImageViewerTests;
158 | productReference = C6A0AC121AF641D800B79957 /* ImageViewerTests.xctest */;
159 | productType = "com.apple.product-type.bundle.unit-test";
160 | };
161 | /* End PBXNativeTarget section */
162 |
163 | /* Begin PBXProject section */
164 | C6A0ABFE1AF641D800B79957 /* Project object */ = {
165 | isa = PBXProject;
166 | attributes = {
167 | LastSwiftMigration = 0700;
168 | LastSwiftUpdateCheck = 0700;
169 | LastUpgradeCheck = 0700;
170 | ORGANIZATIONNAME = "Tan Nghia La";
171 | TargetAttributes = {
172 | C6A0AC061AF641D800B79957 = {
173 | CreatedOnToolsVersion = 6.4;
174 | LastSwiftMigration = 0830;
175 | };
176 | C6A0AC111AF641D800B79957 = {
177 | CreatedOnToolsVersion = 6.4;
178 | LastSwiftMigration = 0830;
179 | };
180 | };
181 | };
182 | buildConfigurationList = C6A0AC011AF641D800B79957 /* Build configuration list for PBXProject "ImageViewer" */;
183 | compatibilityVersion = "Xcode 3.2";
184 | developmentRegion = English;
185 | hasScannedForEncodings = 0;
186 | knownRegions = (
187 | en,
188 | );
189 | mainGroup = C6A0ABFD1AF641D800B79957;
190 | productRefGroup = C6A0AC081AF641D800B79957 /* Products */;
191 | projectDirPath = "";
192 | projectRoot = "";
193 | targets = (
194 | C6A0AC061AF641D800B79957 /* ImageViewer */,
195 | C6A0AC111AF641D800B79957 /* ImageViewerTests */,
196 | );
197 | };
198 | /* End PBXProject section */
199 |
200 | /* Begin PBXResourcesBuildPhase section */
201 | C6A0AC051AF641D800B79957 /* Resources */ = {
202 | isa = PBXResourcesBuildPhase;
203 | buildActionMask = 2147483647;
204 | files = (
205 | C68A2CCC1AF64F68000E8A94 /* Images.xcassets in Resources */,
206 | );
207 | runOnlyForDeploymentPostprocessing = 0;
208 | };
209 | C6A0AC101AF641D800B79957 /* Resources */ = {
210 | isa = PBXResourcesBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | );
214 | runOnlyForDeploymentPostprocessing = 0;
215 | };
216 | /* End PBXResourcesBuildPhase section */
217 |
218 | /* Begin PBXShellScriptBuildPhase section */
219 | C6805E111AF678BD005FB3EE /* ShellScript */ = {
220 | isa = PBXShellScriptBuildPhase;
221 | buildActionMask = 2147483647;
222 | files = (
223 | );
224 | inputPaths = (
225 | );
226 | outputPaths = (
227 | );
228 | runOnlyForDeploymentPostprocessing = 0;
229 | shellPath = /bin/sh;
230 | shellScript = "/usr/local/bin/carthage copy-frameworks";
231 | };
232 | /* End PBXShellScriptBuildPhase section */
233 |
234 | /* Begin PBXSourcesBuildPhase section */
235 | C6A0AC021AF641D800B79957 /* Sources */ = {
236 | isa = PBXSourcesBuildPhase;
237 | buildActionMask = 2147483647;
238 | files = (
239 | C6A0AC241AF6424400B79957 /* UIImageView+ImageViewer.swift in Sources */,
240 | C6A0AC261AF6426600B79957 /* ImageViewer.swift in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | C6A0AC0E1AF641D800B79957 /* Sources */ = {
245 | isa = PBXSourcesBuildPhase;
246 | buildActionMask = 2147483647;
247 | files = (
248 | C6A0AC1A1AF641D800B79957 /* ImageViewerTests.swift in Sources */,
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | /* End PBXSourcesBuildPhase section */
253 |
254 | /* Begin PBXTargetDependency section */
255 | C6A0AC151AF641D800B79957 /* PBXTargetDependency */ = {
256 | isa = PBXTargetDependency;
257 | target = C6A0AC061AF641D800B79957 /* ImageViewer */;
258 | targetProxy = C6A0AC141AF641D800B79957 /* PBXContainerItemProxy */;
259 | };
260 | /* End PBXTargetDependency section */
261 |
262 | /* Begin XCBuildConfiguration section */
263 | C6A0AC1B1AF641D800B79957 /* Debug */ = {
264 | isa = XCBuildConfiguration;
265 | buildSettings = {
266 | ALWAYS_SEARCH_USER_PATHS = NO;
267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
268 | CLANG_CXX_LIBRARY = "libc++";
269 | CLANG_ENABLE_MODULES = YES;
270 | CLANG_ENABLE_OBJC_ARC = YES;
271 | CLANG_WARN_BOOL_CONVERSION = YES;
272 | CLANG_WARN_CONSTANT_CONVERSION = YES;
273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
274 | CLANG_WARN_EMPTY_BODY = YES;
275 | CLANG_WARN_ENUM_CONVERSION = YES;
276 | CLANG_WARN_INT_CONVERSION = YES;
277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
278 | CLANG_WARN_UNREACHABLE_CODE = YES;
279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
280 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
281 | COPY_PHASE_STRIP = NO;
282 | CURRENT_PROJECT_VERSION = 1;
283 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
284 | ENABLE_STRICT_OBJC_MSGSEND = YES;
285 | ENABLE_TESTABILITY = YES;
286 | GCC_C_LANGUAGE_STANDARD = gnu99;
287 | GCC_DYNAMIC_NO_PIC = NO;
288 | GCC_NO_COMMON_BLOCKS = YES;
289 | GCC_OPTIMIZATION_LEVEL = 0;
290 | GCC_PREPROCESSOR_DEFINITIONS = (
291 | "DEBUG=1",
292 | "$(inherited)",
293 | );
294 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
295 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
296 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
297 | GCC_WARN_UNDECLARED_SELECTOR = YES;
298 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
299 | GCC_WARN_UNUSED_FUNCTION = YES;
300 | GCC_WARN_UNUSED_VARIABLE = YES;
301 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
302 | MTL_ENABLE_DEBUG_INFO = YES;
303 | ONLY_ACTIVE_ARCH = YES;
304 | SDKROOT = iphoneos;
305 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
306 | TARGETED_DEVICE_FAMILY = "1,2";
307 | VERSIONING_SYSTEM = "apple-generic";
308 | VERSION_INFO_PREFIX = "";
309 | };
310 | name = Debug;
311 | };
312 | C6A0AC1C1AF641D800B79957 /* Release */ = {
313 | isa = XCBuildConfiguration;
314 | buildSettings = {
315 | ALWAYS_SEARCH_USER_PATHS = NO;
316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
317 | CLANG_CXX_LIBRARY = "libc++";
318 | CLANG_ENABLE_MODULES = YES;
319 | CLANG_ENABLE_OBJC_ARC = YES;
320 | CLANG_WARN_BOOL_CONVERSION = YES;
321 | CLANG_WARN_CONSTANT_CONVERSION = YES;
322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
323 | CLANG_WARN_EMPTY_BODY = YES;
324 | CLANG_WARN_ENUM_CONVERSION = YES;
325 | CLANG_WARN_INT_CONVERSION = YES;
326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
327 | CLANG_WARN_UNREACHABLE_CODE = YES;
328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
330 | COPY_PHASE_STRIP = NO;
331 | CURRENT_PROJECT_VERSION = 1;
332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
333 | ENABLE_NS_ASSERTIONS = NO;
334 | ENABLE_STRICT_OBJC_MSGSEND = YES;
335 | GCC_C_LANGUAGE_STANDARD = gnu99;
336 | GCC_NO_COMMON_BLOCKS = YES;
337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
339 | GCC_WARN_UNDECLARED_SELECTOR = YES;
340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
341 | GCC_WARN_UNUSED_FUNCTION = YES;
342 | GCC_WARN_UNUSED_VARIABLE = YES;
343 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
344 | MTL_ENABLE_DEBUG_INFO = NO;
345 | SDKROOT = iphoneos;
346 | TARGETED_DEVICE_FAMILY = "1,2";
347 | VALIDATE_PRODUCT = YES;
348 | VERSIONING_SYSTEM = "apple-generic";
349 | VERSION_INFO_PREFIX = "";
350 | };
351 | name = Release;
352 | };
353 | C6A0AC1E1AF641D800B79957 /* Debug */ = {
354 | isa = XCBuildConfiguration;
355 | buildSettings = {
356 | CLANG_ENABLE_MODULES = YES;
357 | DEFINES_MODULE = YES;
358 | DYLIB_COMPATIBILITY_VERSION = 1;
359 | DYLIB_CURRENT_VERSION = 1;
360 | DYLIB_INSTALL_NAME_BASE = "@rpath";
361 | FRAMEWORK_SEARCH_PATHS = (
362 | "$(inherited)",
363 | "$(PROJECT_DIR)/Carthage/Build/iOS",
364 | );
365 | INFOPLIST_FILE = ImageViewer/Info.plist;
366 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
367 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
368 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
369 | PRODUCT_BUNDLE_IDENTIFIER = "mollywoodnini.$(PRODUCT_NAME:rfc1034identifier)";
370 | PRODUCT_NAME = "$(TARGET_NAME)";
371 | SKIP_INSTALL = YES;
372 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
373 | SWIFT_VERSION = 3.0;
374 | };
375 | name = Debug;
376 | };
377 | C6A0AC1F1AF641D800B79957 /* Release */ = {
378 | isa = XCBuildConfiguration;
379 | buildSettings = {
380 | CLANG_ENABLE_MODULES = YES;
381 | DEFINES_MODULE = YES;
382 | DYLIB_COMPATIBILITY_VERSION = 1;
383 | DYLIB_CURRENT_VERSION = 1;
384 | DYLIB_INSTALL_NAME_BASE = "@rpath";
385 | FRAMEWORK_SEARCH_PATHS = (
386 | "$(inherited)",
387 | "$(PROJECT_DIR)/Carthage/Build/iOS",
388 | );
389 | INFOPLIST_FILE = ImageViewer/Info.plist;
390 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
391 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
393 | PRODUCT_BUNDLE_IDENTIFIER = "mollywoodnini.$(PRODUCT_NAME:rfc1034identifier)";
394 | PRODUCT_NAME = "$(TARGET_NAME)";
395 | SKIP_INSTALL = YES;
396 | SWIFT_VERSION = 3.0;
397 | };
398 | name = Release;
399 | };
400 | C6A0AC211AF641D800B79957 /* Debug */ = {
401 | isa = XCBuildConfiguration;
402 | buildSettings = {
403 | FRAMEWORK_SEARCH_PATHS = (
404 | "$(SDKROOT)/Developer/Library/Frameworks",
405 | "$(inherited)",
406 | );
407 | GCC_PREPROCESSOR_DEFINITIONS = (
408 | "DEBUG=1",
409 | "$(inherited)",
410 | );
411 | INFOPLIST_FILE = ImageViewerTests/Info.plist;
412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
413 | PRODUCT_BUNDLE_IDENTIFIER = "mollywoodnini.$(PRODUCT_NAME:rfc1034identifier)";
414 | PRODUCT_NAME = "$(TARGET_NAME)";
415 | SWIFT_VERSION = 3.0;
416 | };
417 | name = Debug;
418 | };
419 | C6A0AC221AF641D800B79957 /* Release */ = {
420 | isa = XCBuildConfiguration;
421 | buildSettings = {
422 | FRAMEWORK_SEARCH_PATHS = (
423 | "$(SDKROOT)/Developer/Library/Frameworks",
424 | "$(inherited)",
425 | );
426 | INFOPLIST_FILE = ImageViewerTests/Info.plist;
427 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
428 | PRODUCT_BUNDLE_IDENTIFIER = "mollywoodnini.$(PRODUCT_NAME:rfc1034identifier)";
429 | PRODUCT_NAME = "$(TARGET_NAME)";
430 | SWIFT_VERSION = 3.0;
431 | };
432 | name = Release;
433 | };
434 | /* End XCBuildConfiguration section */
435 |
436 | /* Begin XCConfigurationList section */
437 | C6A0AC011AF641D800B79957 /* Build configuration list for PBXProject "ImageViewer" */ = {
438 | isa = XCConfigurationList;
439 | buildConfigurations = (
440 | C6A0AC1B1AF641D800B79957 /* Debug */,
441 | C6A0AC1C1AF641D800B79957 /* Release */,
442 | );
443 | defaultConfigurationIsVisible = 0;
444 | defaultConfigurationName = Release;
445 | };
446 | C6A0AC1D1AF641D800B79957 /* Build configuration list for PBXNativeTarget "ImageViewer" */ = {
447 | isa = XCConfigurationList;
448 | buildConfigurations = (
449 | C6A0AC1E1AF641D800B79957 /* Debug */,
450 | C6A0AC1F1AF641D800B79957 /* Release */,
451 | );
452 | defaultConfigurationIsVisible = 0;
453 | defaultConfigurationName = Release;
454 | };
455 | C6A0AC201AF641D800B79957 /* Build configuration list for PBXNativeTarget "ImageViewerTests" */ = {
456 | isa = XCConfigurationList;
457 | buildConfigurations = (
458 | C6A0AC211AF641D800B79957 /* Debug */,
459 | C6A0AC221AF641D800B79957 /* Release */,
460 | );
461 | defaultConfigurationIsVisible = 0;
462 | defaultConfigurationName = Release;
463 | };
464 | /* End XCConfigurationList section */
465 | };
466 | rootObject = C6A0ABFE1AF641D800B79957 /* Project object */;
467 | }
468 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/project.xcworkspace/xcshareddata/ImageViewer.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | B89EFB83-72CA-498B-BD2C-4B496920580C
9 | IDESourceControlProjectName
10 | ImageViewer
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 37311F780D4092CEB0604EB681F1BC64903C6B2B
14 | https://github.com/mollywoodnini/ImageViewer.git
15 |
16 | IDESourceControlProjectPath
17 | ImageViewer.xcodeproj
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | 37311F780D4092CEB0604EB681F1BC64903C6B2B
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | https://github.com/mollywoodnini/ImageViewer.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | 37311F780D4092CEB0604EB681F1BC64903C6B2B
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | 37311F780D4092CEB0604EB681F1BC64903C6B2B
36 | IDESourceControlWCCName
37 | ImageViewer
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/project.xcworkspace/xcuserdata/quentindequelen.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/ImageViewer.xcodeproj/project.xcworkspace/xcuserdata/quentindequelen.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/project.xcworkspace/xcuserdata/tannghiala.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/ImageViewer.xcodeproj/project.xcworkspace/xcuserdata/tannghiala.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/xcshareddata/xcschemes/ImageViewer.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
16 |
22 |
23 |
24 |
31 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
49 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
80 |
81 |
87 |
88 |
89 |
90 |
91 |
92 |
98 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/xcuserdata/quentindequelen.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SuppressBuildableAutocreation
6 |
7 | C6A0AC061AF641D800B79957
8 |
9 | primary
10 |
11 |
12 | C6A0AC111AF641D800B79957
13 |
14 | primary
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/xcuserdata/tannghiala.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ImageViewer.xcodeproj/xcuserdata/tannghiala.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ImageViewer.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C6A0AC061AF641D800B79957
16 |
17 | primary
18 |
19 |
20 | C6A0AC111AF641D800B79957
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ImageViewer/ImageViewer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageViewer.swift
3 | // ImageViewer
4 | //
5 | // Created by Tan Nghia La on 30.04.15.
6 | // Copyright (c) 2015 Tan Nghia La. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class ImageViewer: UIViewController {
12 | // MARK: - Properties
13 | fileprivate let kMinMaskViewAlpha: CGFloat = 0.3
14 | fileprivate let kMaxImageScale: CGFloat = 2.5
15 | fileprivate let kMinImageScale: CGFloat = 1.0
16 |
17 | fileprivate let senderView: UIImageView
18 | fileprivate var originalFrameRelativeToScreen: CGRect!
19 | fileprivate var rootViewController: UIViewController!
20 | fileprivate let imageView = UIImageView()
21 | fileprivate var panGesture: UIPanGestureRecognizer!
22 | fileprivate var panOrigin: CGPoint!
23 |
24 | fileprivate var isAnimating = false
25 | fileprivate var isLoaded = false
26 |
27 | fileprivate var closeButton = UIButton()
28 | fileprivate let windowBounds = UIScreen.main.bounds
29 | fileprivate let scrollView = UIScrollView()
30 | fileprivate let maskView = UIView()
31 |
32 | // MARK: - Lifecycle methods
33 | init(senderView: UIImageView, backgroundColor: UIColor) {
34 | self.senderView = senderView
35 |
36 | rootViewController = UIApplication.shared.keyWindow!.rootViewController!
37 | maskView.backgroundColor = backgroundColor
38 |
39 | super.init(nibName: nil, bundle: nil)
40 | }
41 |
42 | required init?(coder aDecoder: NSCoder) {
43 | fatalError("init(coder:) has not been implemented")
44 | }
45 |
46 | override func viewDidLoad() {
47 | super.viewDidLoad()
48 |
49 | configureView()
50 | configureMaskView()
51 | configureScrollView()
52 | configureCloseButton()
53 | configureImageView()
54 | configureConstraints()
55 | }
56 |
57 | // MARK: - View configuration
58 | fileprivate func configureScrollView() {
59 | scrollView.frame = windowBounds
60 | scrollView.delegate = self
61 | scrollView.minimumZoomScale = kMinImageScale
62 | scrollView.maximumZoomScale = kMaxImageScale
63 | scrollView.zoomScale = 1
64 |
65 | view.addSubview(scrollView)
66 | }
67 |
68 | fileprivate func configureMaskView() {
69 | maskView.frame = windowBounds
70 | maskView.alpha = 0.0
71 |
72 | view.insertSubview(maskView, at: 0)
73 | }
74 |
75 | fileprivate func configureCloseButton() {
76 | closeButton.alpha = 0.0
77 |
78 | let image = UIImage(named: "Close", in: Bundle(for: ImageViewer.self), compatibleWith: nil)
79 |
80 | closeButton.setImage(image, for: UIControlState())
81 | closeButton.translatesAutoresizingMaskIntoConstraints = false
82 | closeButton.addTarget(self, action: #selector(ImageViewer.closeButtonTapped(_:)), for: UIControlEvents.touchUpInside)
83 | view.addSubview(closeButton)
84 |
85 | view.setNeedsUpdateConstraints()
86 | }
87 |
88 | fileprivate func configureView() {
89 | var originalFrame = senderView.convert(windowBounds, to: nil)
90 | originalFrame.origin = CGPoint(x: originalFrame.origin.x, y: originalFrame.origin.y)
91 | originalFrame.size = senderView.frame.size
92 |
93 | originalFrameRelativeToScreen = originalFrame
94 |
95 | UIApplication.shared.setStatusBarHidden(true, with: UIStatusBarAnimation.slide)
96 | }
97 |
98 | fileprivate func configureImageView() {
99 | senderView.alpha = 0.0
100 |
101 | imageView.frame = originalFrameRelativeToScreen
102 | imageView.isUserInteractionEnabled = true
103 | imageView.contentMode = UIViewContentMode.scaleAspectFit
104 |
105 | imageView.image = senderView.image
106 |
107 | scrollView.addSubview(imageView)
108 |
109 | animateEntry()
110 | addPanGestureToView()
111 | addGestures()
112 |
113 | centerScrollViewContents()
114 | }
115 |
116 | fileprivate func configureConstraints() {
117 | var constraints: [NSLayoutConstraint] = []
118 |
119 | let views: [String: UIView] = [
120 | "closeButton": closeButton
121 | ]
122 |
123 | constraints.append(NSLayoutConstraint(item: closeButton, attribute: .centerX, relatedBy: .equal, toItem: closeButton.superview, attribute: .centerX, multiplier: 1.0, constant: 0))
124 | constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:[closeButton(==64)]-40-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
125 | constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:[closeButton(==64)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
126 |
127 | NSLayoutConstraint.activate(constraints)
128 | }
129 |
130 | // MARK: - Gestures
131 | fileprivate func addPanGestureToView() {
132 | panGesture = UIPanGestureRecognizer(target: self, action: #selector(ImageViewer.gestureRecognizerDidPan(_:)))
133 | panGesture.cancelsTouchesInView = false
134 | panGesture.delegate = self
135 |
136 | imageView.addGestureRecognizer(panGesture)
137 | }
138 |
139 | fileprivate func addGestures() {
140 | let singleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ImageViewer.didSingleTap(_:)))
141 | singleTapRecognizer.numberOfTapsRequired = 1
142 | singleTapRecognizer.numberOfTouchesRequired = 1
143 | scrollView.addGestureRecognizer(singleTapRecognizer)
144 |
145 | let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ImageViewer.didDoubleTap(_:)))
146 | doubleTapRecognizer.numberOfTapsRequired = 2
147 | doubleTapRecognizer.numberOfTouchesRequired = 1
148 | scrollView.addGestureRecognizer(doubleTapRecognizer)
149 |
150 | singleTapRecognizer.require(toFail: doubleTapRecognizer)
151 | }
152 |
153 | fileprivate func zoomInZoomOut(_ point: CGPoint) {
154 | let newZoomScale = scrollView.zoomScale > (scrollView.maximumZoomScale / 2) ? scrollView.minimumZoomScale : scrollView.maximumZoomScale
155 |
156 | let scrollViewSize = scrollView.bounds.size
157 | let w = scrollViewSize.width / newZoomScale
158 | let h = scrollViewSize.height / newZoomScale
159 | let x = point.x - (w / 2.0)
160 | let y = point.y - (h / 2.0)
161 |
162 | let rectToZoomTo = CGRect(x: x, y: y, width: w, height: h)
163 |
164 | scrollView.zoom(to: rectToZoomTo, animated: true)
165 | }
166 |
167 | // MARK: - Animation
168 | fileprivate func animateEntry() {
169 | guard let image = imageView.image else {
170 | fatalError("no image provided")
171 | }
172 |
173 | UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.6, options: UIViewAnimationOptions.beginFromCurrentState, animations: {() -> Void in
174 | self.imageView.frame = self.centerFrameFromImage(image)
175 | }, completion: nil)
176 |
177 | UIView.animate(withDuration: 0.4, delay: 0.03, options: UIViewAnimationOptions.beginFromCurrentState, animations: {() -> Void in
178 | self.closeButton.alpha = 1.0
179 | self.maskView.alpha = 1.0
180 | }, completion: nil)
181 |
182 | UIView.animate(withDuration: 0.4, delay: 0.1, options: UIViewAnimationOptions.beginFromCurrentState, animations: {() -> Void in
183 | self.view.transform = CGAffineTransform.identity.scaledBy(x: 1.1, y: 1.1)
184 | self.rootViewController.view.transform = CGAffineTransform.identity.scaledBy(x: 0.95, y: 0.95)
185 | }, completion: nil)
186 | }
187 |
188 | fileprivate func centerFrameFromImage(_ image: UIImage) -> CGRect {
189 | var newImageSize = imageResizeBaseOnWidth(windowBounds.size.width, oldWidth: image.size.width, oldHeight: image.size.height)
190 | newImageSize.height = min(windowBounds.size.height, newImageSize.height)
191 |
192 | return CGRect(x: 0, y: windowBounds.size.height / 2 - newImageSize.height / 2, width: newImageSize.width, height: newImageSize.height)
193 | }
194 |
195 | fileprivate func imageResizeBaseOnWidth(_ newWidth: CGFloat, oldWidth: CGFloat, oldHeight: CGFloat) -> CGSize {
196 | let scaleFactor = newWidth / oldWidth
197 | let newHeight = oldHeight * scaleFactor
198 |
199 | return CGSize(width: newWidth, height: newHeight)
200 | }
201 |
202 | // MARK: - Actions
203 | func gestureRecognizerDidPan(_ recognizer: UIPanGestureRecognizer) {
204 | if scrollView.zoomScale != 1.0 || isAnimating {
205 | return
206 | }
207 |
208 | senderView.alpha = 0.0
209 |
210 | scrollView.bounces = false
211 | let windowSize = maskView.bounds.size
212 | let currentPoint = panGesture.translation(in: scrollView)
213 | let y = currentPoint.y + panOrigin.y
214 |
215 | imageView.frame.origin = CGPoint(x: currentPoint.x + panOrigin.x, y: y)
216 |
217 | let yDiff = abs((y + imageView.frame.size.height / 2) - windowSize.height / 2)
218 | maskView.alpha = max(1 - yDiff / (windowSize.height / 0.95), kMinMaskViewAlpha)
219 | closeButton.alpha = max(1 - yDiff / (windowSize.height / 0.95), kMinMaskViewAlpha) / 2
220 |
221 | if (panGesture.state == UIGestureRecognizerState.ended || panGesture.state == UIGestureRecognizerState.cancelled)
222 | && scrollView.zoomScale == 1.0 {
223 | maskView.alpha < 0.85 ? dismissViewController() : rollbackViewController()
224 | }
225 | }
226 |
227 | func didSingleTap(_ recognizer: UITapGestureRecognizer) {
228 | scrollView.zoomScale == 1.0 ? dismissViewController() : scrollView.setZoomScale(1.0, animated: true)
229 | }
230 |
231 | func didDoubleTap(_ recognizer: UITapGestureRecognizer) {
232 | let pointInView = recognizer.location(in: imageView)
233 | zoomInZoomOut(pointInView)
234 | }
235 |
236 | func closeButtonTapped(_ sender: UIButton) {
237 | if scrollView.zoomScale != 1.0 {
238 | scrollView.setZoomScale(1.0, animated: true)
239 | }
240 | dismissViewController()
241 | }
242 |
243 | // MARK: - Misc.
244 | fileprivate func centerScrollViewContents() {
245 | let boundsSize = rootViewController.view.bounds.size
246 | var contentsFrame = imageView.frame
247 |
248 | if contentsFrame.size.width < boundsSize.width {
249 | contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0
250 | } else {
251 | contentsFrame.origin.x = 0.0
252 | }
253 |
254 | if contentsFrame.size.height < boundsSize.height {
255 | contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0
256 | } else {
257 | contentsFrame.origin.y = 0.0
258 | }
259 |
260 | imageView.frame = contentsFrame
261 | }
262 |
263 | fileprivate func rollbackViewController() {
264 | guard let image = imageView.image else {
265 | fatalError("no image provided")
266 | }
267 |
268 | isAnimating = true
269 | UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.6, options: UIViewAnimationOptions.beginFromCurrentState, animations: {() in
270 | self.imageView.frame = self.centerFrameFromImage(image)
271 | self.maskView.alpha = 1.0
272 | self.closeButton.alpha = 1.0
273 | }, completion: {(finished) in
274 | self.isAnimating = false
275 | })
276 | }
277 |
278 | fileprivate func dismissViewController() {
279 | isAnimating = true
280 | DispatchQueue.main.async(execute: {
281 | self.imageView.clipsToBounds = true
282 |
283 | UIView.animate(withDuration: 0.2, animations: {() in
284 | self.closeButton.alpha = 0.0
285 | })
286 |
287 | UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.6, options: UIViewAnimationOptions.beginFromCurrentState, animations: {() in
288 | self.imageView.frame = self.originalFrameRelativeToScreen
289 | self.rootViewController.view.transform = CGAffineTransform.identity.scaledBy(x: 1.0, y: 1.0)
290 | self.view.transform = CGAffineTransform.identity.scaledBy(x: 1.0, y: 1.0)
291 | self.maskView.alpha = 0.0
292 | UIApplication.shared.setStatusBarHidden(false, with: UIStatusBarAnimation.none)
293 | }, completion: {(finished) in
294 | self.willMove(toParentViewController: nil)
295 | self.view.removeFromSuperview()
296 | self.removeFromParentViewController()
297 | self.senderView.alpha = 1.0
298 | self.isAnimating = false
299 | })
300 | })
301 | }
302 |
303 | func presentFromRootViewController() {
304 | willMove(toParentViewController: rootViewController)
305 | rootViewController.view.addSubview(view)
306 | rootViewController.addChildViewController(self)
307 | didMove(toParentViewController: rootViewController)
308 | }
309 | }
310 |
311 | // MARK: - GestureRecognizer delegate
312 | extension ImageViewer: UIGestureRecognizerDelegate {
313 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
314 | panOrigin = imageView.frame.origin
315 | gestureRecognizer.isEnabled = true
316 | return !isAnimating
317 | }
318 | }
319 |
320 | // MARK: - ScrollView delegate
321 | extension ImageViewer: UIScrollViewDelegate {
322 | func viewForZooming(in scrollView: UIScrollView) -> UIView? {
323 | return imageView
324 | }
325 |
326 | func scrollViewDidZoom(_ scrollView: UIScrollView) {
327 | isAnimating = true
328 | centerScrollViewContents()
329 | }
330 |
331 | func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
332 | isAnimating = false
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/ImageViewer/Images.xcassets/Close.imageset/Close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/ImageViewer/Images.xcassets/Close.imageset/Close.png
--------------------------------------------------------------------------------
/ImageViewer/Images.xcassets/Close.imageset/Close@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/ImageViewer/Images.xcassets/Close.imageset/Close@2x.png
--------------------------------------------------------------------------------
/ImageViewer/Images.xcassets/Close.imageset/Close@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mollywoodnini/ImageViewer/8fda7eb185561c1d3c46a8b8426f0610e5b59272/ImageViewer/Images.xcassets/Close.imageset/Close@3x.png
--------------------------------------------------------------------------------
/ImageViewer/Images.xcassets/Close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "Close.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x",
11 | "filename" : "Close@2x.png"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x",
16 | "filename" : "Close@3x.png"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/ImageViewer/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 |
--------------------------------------------------------------------------------
/ImageViewer/UIImageView+ImageViewer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageView+ImageViewer.swift
3 | // ImageViewer
4 | //
5 | // Created by Tan Nghia La on 03.05.15.
6 | // Copyright (c) 2015 Tan Nghia La. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | public extension UIImageView {
13 |
14 | public func setupForImageViewer(_ backgroundColor: UIColor = UIColor.white) {
15 | isUserInteractionEnabled = true
16 | let gestureRecognizer = ImageViewerTapGestureRecognizer(target: self, action: #selector(UIImageView.didTap(_:)), backgroundColor: backgroundColor)
17 | addGestureRecognizer(gestureRecognizer)
18 | }
19 |
20 | internal func didTap(_ recognizer: ImageViewerTapGestureRecognizer) {
21 | let imageViewer = ImageViewer(senderView: self, backgroundColor: recognizer.backgroundColor)
22 | imageViewer.presentFromRootViewController()
23 | }
24 | }
25 |
26 | class ImageViewerTapGestureRecognizer: UITapGestureRecognizer {
27 | let backgroundColor: UIColor
28 |
29 | init(target: AnyObject, action: Selector, backgroundColor: UIColor) {
30 | self.backgroundColor = backgroundColor
31 |
32 | super.init(target: target, action: action)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ImageViewerTests/ImageViewerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageViewerTests.swift
3 | // ImageViewerTests
4 | //
5 | // Created by Tan Nghia La on 03.05.15.
6 | // Copyright (c) 2015 Tan Nghia La. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class ImageViewerTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | XCTAssert(true, "Pass")
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure() {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ImageViewerTests/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Tan Nghia La
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ImageViewer
2 |
3 | [](https://github.com/Carthage/Carthage)
4 |
5 | The ImageViewer is a very simple image viewer for iOS, which behaviour is inspired by the facebook app and completely written in Swift 2.0.
6 |
7 | ## Screenshots
8 |  
11 |
12 |
13 |
14 | ## Installation
15 | Using [Carthage](https://github.com/Carthage/Carthage):
16 |
17 | 1. Create a cartfile with following content within your project directory: `github "mollywoodnini/ImageViewer"`
18 | 2. run `carthage update`. Carthage builds the ImageViewer framework, which you'll have to import to your project.
19 | 3. On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop both frameworks from the Carthage/Build folder on disk.
20 | 4. On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following contents:
21 | ```
22 | /usr/local/bin/carthage copy-frameworks
23 | ```
24 | and add the paths to the frameworks under “Input Files”:
25 | ```
26 | $(SRCROOT)/Carthage/Build/iOS/ImageViewer.framework
27 | ```
28 |
29 | ## Usage
30 | Just create an ImageView and prepare it for the ImageViewer via:
31 | ```swift
32 | imageView.setupForImageViewer()
33 | ```
34 |
35 | You can specify the background color of the ImageViewer too:
36 | ```swift
37 | imageView.setupForImageViewer(UIColor.redColor())
38 | ```
39 |
40 | To hide the status bar add the "View controller-based status bar appearance" key to the Info.plist and set its value to NO.
41 |
42 | ## Requirements
43 | - iOS 8.0+
44 | - Xcode 7.0+
45 |
46 | ## License
47 |
48 | Released under the MIT license. See the LICENSE file for more info.
49 |
50 | ## Credits
51 | - Thanks to icons8.com for the close image!
52 | - Thanks to Michael Henry, whose ImageViewer was a huge help for me to get this here done.
53 |
--------------------------------------------------------------------------------