diff --git a/PhotoCollectionView.xcodeproj/project.pbxproj b/PhotoCollectionView.xcodeproj/project.pbxproj index 80ad7db..ff69e6f 100644 --- a/PhotoCollectionView.xcodeproj/project.pbxproj +++ b/PhotoCollectionView.xcodeproj/project.pbxproj @@ -7,13 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 09142C005C1E806898B8B37A /* Pods_PhotoCollectionView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1A0A4A9CDFDB3F5E2E64CBF /* Pods_PhotoCollectionView.framework */; }; 0F3BD7481F1E5B880003A218 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F3BD7471F1E5B880003A218 /* AppDelegate.swift */; }; 0F3BD74A1F1E5B880003A218 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F3BD7491F1E5B880003A218 /* ViewController.swift */; }; 0F3BD74D1F1E5B880003A218 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0F3BD74B1F1E5B880003A218 /* Main.storyboard */; }; 0F3BD74F1F1E5B880003A218 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0F3BD74E1F1E5B880003A218 /* Assets.xcassets */; }; 0F3BD7521F1E5B880003A218 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0F3BD7501F1E5B880003A218 /* LaunchScreen.storyboard */; }; 0F3BD7571F1E5B9D0003A218 /* PhotoCollectionView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* PhotoCollectionView.framework */; }; - OBJ_21 /* PhotoCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* PhotoCache.swift */; }; + E5BC86CEA6D51902F094871C /* Pods_PhotoCollectionViewDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B231CCF5164F7C89A4B1A77B /* Pods_PhotoCollectionViewDemo.framework */; }; OBJ_22 /* PhotoCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* PhotoCollectionView.swift */; }; OBJ_23 /* PhotoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* PhotoView.swift */; }; /* End PBXBuildFile section */ @@ -26,11 +27,16 @@ 0F3BD74E1F1E5B880003A218 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0F3BD7511F1E5B880003A218 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 0F3BD7531F1E5B880003A218 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 756687D273583F5ABF5B5AA3 /* Pods-PhotoCollectionViewDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhotoCollectionViewDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-PhotoCollectionViewDemo/Pods-PhotoCollectionViewDemo.release.xcconfig"; sourceTree = ""; }; + 8AFFE575DCA5992B0ABFF7F2 /* Pods-PhotoCollectionView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhotoCollectionView.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PhotoCollectionView/Pods-PhotoCollectionView.debug.xcconfig"; sourceTree = ""; }; + A59E36DFC3CD24C195800490 /* Pods-PhotoCollectionView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhotoCollectionView.release.xcconfig"; path = "Pods/Target Support Files/Pods-PhotoCollectionView/Pods-PhotoCollectionView.release.xcconfig"; sourceTree = ""; }; + B231CCF5164F7C89A4B1A77B /* Pods_PhotoCollectionViewDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PhotoCollectionViewDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F1A0A4A9CDFDB3F5E2E64CBF /* Pods_PhotoCollectionView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PhotoCollectionView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F2238AC62168DAAC5CB3B3E2 /* Pods-PhotoCollectionViewDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhotoCollectionViewDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PhotoCollectionViewDemo/Pods-PhotoCollectionViewDemo.debug.xcconfig"; sourceTree = ""; }; OBJ_10 /* PhotoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoView.swift; sourceTree = ""; }; OBJ_12 /* Example */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Example; sourceTree = SOURCE_ROOT; }; OBJ_15 /* PhotoCollectionView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PhotoCollectionView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - OBJ_8 /* PhotoCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCache.swift; sourceTree = ""; }; OBJ_9 /* PhotoCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCollectionView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -40,6 +46,7 @@ buildActionMask = 2147483647; files = ( 0F3BD7571F1E5B9D0003A218 /* PhotoCollectionView.framework in Frameworks */, + E5BC86CEA6D51902F094871C /* Pods_PhotoCollectionViewDemo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -47,6 +54,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( + 09142C005C1E806898B8B37A /* Pods_PhotoCollectionView.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -66,6 +74,26 @@ path = PhotoCollectionViewDemo; sourceTree = ""; }; + 1EF6FCFAA49083CAFF391576 /* Pods */ = { + isa = PBXGroup; + children = ( + 8AFFE575DCA5992B0ABFF7F2 /* Pods-PhotoCollectionView.debug.xcconfig */, + A59E36DFC3CD24C195800490 /* Pods-PhotoCollectionView.release.xcconfig */, + F2238AC62168DAAC5CB3B3E2 /* Pods-PhotoCollectionViewDemo.debug.xcconfig */, + 756687D273583F5ABF5B5AA3 /* Pods-PhotoCollectionViewDemo.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 515FB1A048F523B8021092FD /* Frameworks */ = { + isa = PBXGroup; + children = ( + F1A0A4A9CDFDB3F5E2E64CBF /* Pods_PhotoCollectionView.framework */, + B231CCF5164F7C89A4B1A77B /* Pods_PhotoCollectionViewDemo.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; OBJ_11 /* Tests */ = { isa = PBXGroup; children = ( @@ -82,7 +110,7 @@ name = Products; sourceTree = BUILT_PRODUCTS_DIR; }; - OBJ_5 /* */ = { + OBJ_5 = { isa = PBXGroup; children = ( OBJ_6 /* Package.swift */, @@ -91,14 +119,14 @@ OBJ_12 /* Example */, 0F3BD7461F1E5B880003A218 /* PhotoCollectionViewDemo */, OBJ_14 /* Products */, + 1EF6FCFAA49083CAFF391576 /* Pods */, + 515FB1A048F523B8021092FD /* Frameworks */, ); - name = ""; sourceTree = ""; }; OBJ_7 /* Sources */ = { isa = PBXGroup; children = ( - OBJ_8 /* PhotoCache.swift */, OBJ_9 /* PhotoCollectionView.swift */, OBJ_10 /* PhotoView.swift */, ); @@ -112,9 +140,12 @@ isa = PBXNativeTarget; buildConfigurationList = 0F3BD7561F1E5B880003A218 /* Build configuration list for PBXNativeTarget "PhotoCollectionViewDemo" */; buildPhases = ( + AC4046D9271E07C9338FA604 /* [CP] Check Pods Manifest.lock */, 0F3BD7411F1E5B870003A218 /* Sources */, 0F3BD7421F1E5B870003A218 /* Frameworks */, 0F3BD7431F1E5B870003A218 /* Resources */, + 7600BE75BA5849080E8FDFA7 /* [CP] Embed Pods Frameworks */, + C09A58A52992ABD0D50AC028 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -129,8 +160,10 @@ isa = PBXNativeTarget; buildConfigurationList = OBJ_17 /* Build configuration list for PBXNativeTarget "PhotoCollectionView" */; buildPhases = ( + 13E805DDBEBC697E8ADC0865 /* [CP] Check Pods Manifest.lock */, OBJ_20 /* Sources */, OBJ_24 /* Frameworks */, + 4137C8305D60DC5D9FF33BEE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -164,7 +197,7 @@ en, Base, ); - mainGroup = OBJ_5 /* */; + mainGroup = OBJ_5; productRefGroup = OBJ_14 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -188,6 +221,84 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 13E805DDBEBC697E8ADC0865 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 4137C8305D60DC5D9FF33BEE /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PhotoCollectionView/Pods-PhotoCollectionView-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 7600BE75BA5849080E8FDFA7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PhotoCollectionViewDemo/Pods-PhotoCollectionViewDemo-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AC4046D9271E07C9338FA604 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + C09A58A52992ABD0D50AC028 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PhotoCollectionViewDemo/Pods-PhotoCollectionViewDemo-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 0F3BD7411F1E5B870003A218 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -202,7 +313,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 0; files = ( - OBJ_21 /* PhotoCache.swift in Sources */, OBJ_22 /* PhotoCollectionView.swift in Sources */, OBJ_23 /* PhotoView.swift in Sources */, ); @@ -232,6 +342,7 @@ /* Begin XCBuildConfiguration section */ 0F3BD7541F1E5B880003A218 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F2238AC62168DAAC5CB3B3E2 /* Pods-PhotoCollectionViewDemo.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -282,6 +393,7 @@ }; 0F3BD7551F1E5B880003A218 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 756687D273583F5ABF5B5AA3 /* Pods-PhotoCollectionViewDemo.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -328,6 +440,7 @@ }; OBJ_18 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 8AFFE575DCA5992B0ABFF7F2 /* Pods-PhotoCollectionView.debug.xcconfig */; buildSettings = { ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -349,6 +462,7 @@ }; OBJ_19 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A59E36DFC3CD24C195800490 /* Pods-PhotoCollectionView.release.xcconfig */; buildSettings = { ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -422,6 +536,7 @@ 0F3BD7551F1E5B880003A218 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; }; OBJ_17 /* Build configuration list for PBXNativeTarget "PhotoCollectionView" */ = { isa = XCConfigurationList; diff --git a/PhotoCollectionView.xcworkspace/contents.xcworkspacedata b/PhotoCollectionView.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3dc81da --- /dev/null +++ b/PhotoCollectionView.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/PhotoCollectionViewDemo/ViewController.swift b/PhotoCollectionViewDemo/ViewController.swift index c3e4c83..e00de93 100644 --- a/PhotoCollectionViewDemo/ViewController.swift +++ b/PhotoCollectionViewDemo/ViewController.swift @@ -33,6 +33,11 @@ extension ViewController: PhotoCollectionViewDataSource { func photoColletionView(_ photoCollectionView: PhotoCollectionView, imageAt index: Int) -> UIImage? { return UIImage(named: "image\(index + 1)") } + + /* can use URL for data + func photoCollectionView(_ photoCollectionView: PhotoCollectionView, urlImageAt index: Int) -> URL? { + return URL(string: "https://i.ytimg.com/vi/SfLV8hD7zX4/maxresdefault.jpg") + }*/ } extension ViewController: PhotoCollectionViewDelegate { diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..116ec15 --- /dev/null +++ b/Podfile @@ -0,0 +1,18 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '8.0' + +target 'PhotoCollectionView' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + pod 'SwiftyImageCache', '~> 1.0' + +end + +target 'PhotoCollectionViewDemo' do + + use_frameworks! + + pod 'SwiftyImageCache', '~> 1.0' + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..baa178e --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,12 @@ +PODS: + - SwiftyImageCache (1.0) + +DEPENDENCIES: + - SwiftyImageCache (~> 1.0) + +SPEC CHECKSUMS: + SwiftyImageCache: ff7e6fe1c25ecf1c8da0ec03042465011d83a43b + +PODFILE CHECKSUM: ee4e7baf314733488bb31f6f17189c62641ca799 + +COCOAPODS: 1.2.1 diff --git a/Sources/PhotoCache.swift b/Sources/PhotoCache.swift deleted file mode 100644 index 41ce4dc..0000000 --- a/Sources/PhotoCache.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// PhotoCache.swift -// PhotoCollectionDemo -// -// Created by Minh Luan Tran on 7/13/17. -// Copyright © 2017 luantran. All rights reserved. -// - -import UIKit - -open class PhotoCache { - - open static let `default` = PhotoCache() - - var queue = DispatchQueue(label: "PhotoCache") - var workItems = NSCache() - var images = NSCache() - - public init() { - } - - open func loadImage(atUrl url: URL, completion: @escaping (UIImage?) -> Void) { - let urlString = url.absoluteString - if let image = images.object(forKey: urlString as NSString) { - completion(image) - return - } - if let workItem = workItems.object(forKey: urlString as NSString) { - workItem.notify(queue: queue, execute: { [weak self] in - if let image = self?.images.object(forKey: urlString as NSString) { - DispatchQueue.main.async { - completion(image) - } - } - }) - return - } - let workItem = DispatchWorkItem { [weak self] in - do { - let data = try Data(contentsOf: url) - if let image = UIImage(data: data) { - self?.images.setObject(image, forKey: urlString as NSString) - DispatchQueue.main.async { - completion(image) - } - } else { - DispatchQueue.main.async { - completion(nil) - } - } - } catch ( _) { - DispatchQueue.main.async { - completion(nil) - } - } - } - workItems.setObject(workItem, forKey: urlString as NSString) - queue.async(execute: workItem) - } - - open func clear() { - workItems.removeAllObjects() - images.removeAllObjects() - } -} diff --git a/Sources/PhotoCollectionView.swift b/Sources/PhotoCollectionView.swift index e85c8ef..737ac6f 100644 --- a/Sources/PhotoCollectionView.swift +++ b/Sources/PhotoCollectionView.swift @@ -17,6 +17,7 @@ import UIKit @objc public protocol PhotoCollectionViewDelegate: NSObjectProtocol { @objc optional func photoCollectionView(_ photoCollectionView: PhotoCollectionView, didSelectImageAt index: Int) + @objc optional func photoCollectionView(_ photoCollectionView: PhotoCollectionView, didCreated photoView: PhotoView, at index: Int) -> Void } @IBDesignable @@ -31,7 +32,6 @@ open class PhotoCollectionView: UIView { @IBInspectable open var moreTextColor: UIColor! = UIColor.white @IBInspectable open var moreTextBackgroundColor: UIColor! = UIColor(white: 0.2, alpha: 0.6) open var moreTextFont: UIFont! = UIFont.systemFont(ofSize: 17) - open var photoCache = PhotoCache.default override open var bounds: CGRect { didSet { @@ -108,7 +108,7 @@ open class PhotoCollectionView: UIView { if let image = image { photoView.setImage(image) } else if let url = dataSource.photoCollectionView?(self, urlImageAt: i) { - photoView.setUrl(url: url, photoCache: photoCache) + photoView.setUrl(url: url) } if numImage > maxImage && i == numShow - 1 { addMoreLabel(in: photoView, numMore: numImage - numShow) @@ -118,6 +118,8 @@ open class PhotoCollectionView: UIView { photoView.addGestureRecognizer(tapGesture) photoViews.append(photoView) addSubview(photoView) + + delegate?.photoCollectionView?(self, didCreated: photoView, at: i) } } diff --git a/Sources/PhotoView.swift b/Sources/PhotoView.swift index 3ac7286..f3810f5 100644 --- a/Sources/PhotoView.swift +++ b/Sources/PhotoView.swift @@ -7,10 +7,11 @@ // import UIKit +import SwiftyImageCache -class PhotoView: UIView { +public class PhotoView: UIView { - lazy var imageView: UIImageView! = { + public lazy var imageView: UIImageView! = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleAspectFill @@ -18,7 +19,7 @@ class PhotoView: UIView { imageView.backgroundColor = .black return imageView }() - lazy var loadingView: UIActivityIndicatorView! = { + public lazy var loadingView: UIActivityIndicatorView! = { let loadingView = UIActivityIndicatorView(activityIndicatorStyle: .white) loadingView.hidesWhenStopped = true loadingView.translatesAutoresizingMaskIntoConstraints = false @@ -33,7 +34,7 @@ class PhotoView: UIView { }() - required init?(coder aDecoder: NSCoder) { + required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -63,14 +64,17 @@ class PhotoView: UIView { imageView.image = image } - func setUrl(url: URL, photoCache: PhotoCache) { + func setUrl(url: URL, cache: ImageCache = ImageCache.default) { loadingView.startAnimating() - photoCache.loadImage(atUrl: url, completion: { [weak self] image in + let urlString = url.absoluteString + cache.loadImage(atUrl: url, completion: { [weak self] (urlStr, image) in guard let sSelf = self else { return } - sSelf.imageView.image = image - sSelf.loadingView.stopAnimating() + if urlString == urlStr { + sSelf.imageView.image = image + sSelf.loadingView.stopAnimating() + } }) } }