diff --git a/Example/Demo.xcodeproj/project.pbxproj b/Example/Demo.xcodeproj/project.pbxproj index ce8c76d5..7484db15 100644 --- a/Example/Demo.xcodeproj/project.pbxproj +++ b/Example/Demo.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + A68EAF90239634A000158120 /* ExampleListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A68EAF8F239634A000158120 /* ExampleListViewController.swift */; }; + A68EAF922396361C00158120 /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A68EAF912396361C00158120 /* BasicViewController.swift */; }; + A68EAF9423963A9100158120 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A68EAF9323963A9100158120 /* Extensions.swift */; }; + A68EAF9623963B3600158120 /* WithURLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A68EAF9523963B3600158120 /* WithURLViewController.swift */; }; + A6AB18E4239752C800BF0681 /* WithURLsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AB18E3239752C800BF0681 /* WithURLsViewController.swift */; }; A6BC0BC8239139F8004A4E46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BC7239139F8004A4E46 /* AppDelegate.swift */; }; A6BC0BD1239139F9004A4E46 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A6BC0BD0239139F9004A4E46 /* Assets.xcassets */; }; A6BC0BD4239139F9004A4E46 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A6BC0BD2239139F9004A4E46 /* LaunchScreen.storyboard */; }; @@ -20,6 +25,11 @@ /* Begin PBXFileReference section */ 10710A5B07A80798DED8269E /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 20E424F2DB4A27D25989365C /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; }; + A68EAF8F239634A000158120 /* ExampleListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleListViewController.swift; sourceTree = ""; }; + A68EAF912396361C00158120 /* BasicViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicViewController.swift; sourceTree = ""; }; + A68EAF9323963A9100158120 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + A68EAF9523963B3600158120 /* WithURLViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithURLViewController.swift; sourceTree = ""; }; + A6AB18E3239752C800BF0681 /* WithURLsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithURLsViewController.swift; sourceTree = ""; }; A6BC0BC4239139F8004A4E46 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; A6BC0BC7239139F8004A4E46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A6BC0BD0239139F9004A4E46 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -78,9 +88,14 @@ A6BC0BD2239139F9004A4E46 /* LaunchScreen.storyboard */, A6BC0BD5239139F9004A4E46 /* Info.plist */, A6BC0BDD23913C30004A4E46 /* GalleryFlowLayout.swift */, - A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */, A6BC0BE123914239004A4E46 /* ThumbCell.swift */, A6BC0BE723915420004A4E46 /* Data.swift */, + A68EAF9323963A9100158120 /* Extensions.swift */, + A68EAF8F239634A000158120 /* ExampleListViewController.swift */, + A68EAF912396361C00158120 /* BasicViewController.swift */, + A68EAF9523963B3600158120 /* WithURLViewController.swift */, + A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */, + A6AB18E3239752C800BF0681 /* WithURLsViewController.swift */, ); path = Demo; sourceTree = ""; @@ -208,11 +223,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A68EAF9623963B3600158120 /* WithURLViewController.swift in Sources */, A6BC0BE023913C66004A4E46 /* GalleryViewController.swift in Sources */, + A6AB18E4239752C800BF0681 /* WithURLsViewController.swift in Sources */, A6BC0BE823915420004A4E46 /* Data.swift in Sources */, + A68EAF9423963A9100158120 /* Extensions.swift in Sources */, A6BC0BDE23913C30004A4E46 /* GalleryFlowLayout.swift in Sources */, + A68EAF90239634A000158120 /* ExampleListViewController.swift in Sources */, A6BC0BE223914239004A4E46 /* ThumbCell.swift in Sources */, A6BC0BC8239139F8004A4E46 /* AppDelegate.swift in Sources */, + A68EAF922396361C00158120 /* BasicViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/Demo/AppDelegate.swift b/Example/Demo/AppDelegate.swift index fbe19159..241ee520 100644 --- a/Example/Demo/AppDelegate.swift +++ b/Example/Demo/AppDelegate.swift @@ -1,11 +1,3 @@ -// -// AppDelegate.swift -// Demo -// -// Created by Michael Henry Pantaleon on 2019/11/29. -// Copyright © 2019 Michael Henry Pantaleon. All rights reserved. -// - import UIKit @UIApplicationMain @@ -16,8 +8,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) - let galleryVC = GalleryViewController() - window?.rootViewController = UINavigationController(rootViewController: galleryVC) + let exampleListVC = ExampleListViewController() + window?.rootViewController = UINavigationController(rootViewController: exampleListVC) window?.makeKeyAndVisible() return true } diff --git a/Example/Demo/BasicViewController.swift b/Example/Demo/BasicViewController.swift new file mode 100644 index 00000000..cb504b6e --- /dev/null +++ b/Example/Demo/BasicViewController.swift @@ -0,0 +1,31 @@ +import UIKit +import MHFacebookImageViewer + +class BasicViewController:UIViewController { + + lazy var imageView:UIImageView = { + let iv = UIImageView() + iv.image = Data.images[0].resize(targetSize: .thumbnail) + + // Setup Image Viewer + iv.setupImageViewer() + return iv + }() + + override func loadView() { + super.loadView() + view = UIView() + view.backgroundColor = .white + view.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20).isActive = true + imageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true + imageView.widthAnchor.constraint(equalToConstant: 100).isActive = true + imageView.heightAnchor.constraint(equalToConstant: 100).isActive = true + } + + override func viewDidLoad() { + super.viewDidLoad() + + } +} diff --git a/Example/Demo/Data.swift b/Example/Demo/Data.swift index 8318d48a..a2802e67 100644 --- a/Example/Demo/Data.swift +++ b/Example/Demo/Data.swift @@ -1,36 +1,17 @@ import UIKit struct Data { - static let images:[UIImage] = [ - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, - UIImage(named: "cat1")!, - UIImage(named: "cat2")!, - UIImage(named: "cat3")!, - UIImage(named: "cat4")!, - UIImage(named: "cat5")!, + + static let imageNames:[String] = [ + "cat1", + "cat2", + "cat3", + "cat4", + "cat5" ] + + static let images:[UIImage] = Self.imageNames.compactMap { UIImage(named: $0)! } + + static let imageUrls:[URL] = Self.imageNames.compactMap { + URL(string: "https://raw.githubusercontent.com/michaelhenry/MHFacebookImageViewer/master/Example/Demo/Assets.xcassets/\($0).imageset/\($0).jpg")! } } diff --git a/Example/Demo/ExampleListViewController.swift b/Example/Demo/ExampleListViewController.swift new file mode 100644 index 00000000..bc312d1f --- /dev/null +++ b/Example/Demo/ExampleListViewController.swift @@ -0,0 +1,72 @@ +import UIKit + +enum ExampleType:CaseIterable, CustomStringConvertible { + + case basic + case withURL + case withUIImages + case withURLs + + var description: String { + switch self { + case .basic: + return "Basic" + case .withURL: + return "With URL" + case .withUIImages: + return "With [UIImage]" + case .withURLs: + return "With [URL]" + } + } + + var viewController:UIViewController { + switch self { + case .basic: + return BasicViewController() + case .withURL: + return WithURLViewController() + case .withUIImages: + return WithImagesViewController() + case .withURLs: + return WithURLsViewController() + } + } +} + +class ExampleListViewController:UITableViewController { + + var items:[ExampleType] = ExampleType.allCases + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView( + _ tableView: UITableView, numberOfRowsInSection section: Int) + -> Int { + return items.count + } + + override func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let itemReuseId = "item_reuse_identifier" + var cell = tableView.dequeueReusableCell(withIdentifier: itemReuseId) + if cell == nil { + cell = UITableViewCell(style: .default, reuseIdentifier: itemReuseId) + } + cell?.textLabel?.text = items[indexPath.row].description + return cell! + } + + + override func tableView( + _ tableView: UITableView, + didSelectRowAt indexPath: IndexPath) { + + navigationController?.pushViewController( + items[indexPath.row].viewController, animated: true) + } +} diff --git a/Example/Demo/Extensions.swift b/Example/Demo/Extensions.swift new file mode 100644 index 00000000..145e72f2 --- /dev/null +++ b/Example/Demo/Extensions.swift @@ -0,0 +1,34 @@ +import UIKit + +extension UIImage { + func resize(targetSize: CGSize) -> UIImage { + + let widthRatio = targetSize.width / size.width + let heightRatio = targetSize.height / size.height + + var newSize: CGSize + if widthRatio > heightRatio { + newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) + } else { + newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) + } + + let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) + + UIGraphicsBeginImageContextWithOptions(newSize, false, 0) + draw(in: rect) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage! + } + + func scale(by scale: CGFloat) -> UIImage? { + let scaledSize = CGSize(width: size.width * scale, height: size.height * scale) + return resize(targetSize: scaledSize) + } +} + +extension CGSize { + static let thumbnail:CGSize = CGSize(width: 50, height:50) +} diff --git a/Example/Demo/GalleryViewController.swift b/Example/Demo/GalleryViewController.swift index 7648db36..57921ee5 100644 --- a/Example/Demo/GalleryViewController.swift +++ b/Example/Demo/GalleryViewController.swift @@ -1,7 +1,7 @@ import UIKit import MHFacebookImageViewer -class GalleryViewController:UIViewController { +class WithImagesViewController:UIViewController { var images:[UIImage] = Data.images @@ -56,12 +56,14 @@ class GalleryViewController:UIViewController { } } - override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + override func viewWillTransition( + to size: CGSize, + with coordinator: UIViewControllerTransitionCoordinator) { updateLayout(size) } } -extension GalleryViewController:UICollectionViewDataSource { +extension WithImagesViewController:UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 @@ -73,16 +75,20 @@ extension GalleryViewController:UICollectionViewDataSource { return images.count } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView( + _ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell:ThumbCell = collectionView .dequeueReusableCell(withReuseIdentifier: ThumbCell.reuseIdentifier, for: indexPath) as! ThumbCell cell.imageView.image = images[indexPath.item] - // Setup Image Viewer + // Setup Image Viewer with [UIImage] cell.imageView.setupImageViewer( images: images, initialIndex: indexPath.item) + return cell } } diff --git a/Example/Demo/WithURLViewController.swift b/Example/Demo/WithURLViewController.swift new file mode 100644 index 00000000..885bf820 --- /dev/null +++ b/Example/Demo/WithURLViewController.swift @@ -0,0 +1,36 @@ +import UIKit +import MHFacebookImageViewer +import SDWebImage + +class WithURLViewController:UIViewController { + + lazy var imageView:UIImageView = { + let iv = UIImageView() + + // Set an image with low resolution. + iv.image = Data.images[0].resize(targetSize: .thumbnail) + + // Setup Image Viewer With URL + iv.setupImageViewer(url: Data.imageUrls[0]) + return iv + }() + + override func loadView() { + super.loadView() + view = UIView() + view.backgroundColor = .white + view.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20).isActive = true + imageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true + imageView.widthAnchor.constraint(equalToConstant: 100).isActive = true + imageView.heightAnchor.constraint(equalToConstant: 100).isActive = true + } + + override func viewDidLoad() { + super.viewDidLoad() + + // for debugging purposes, just clear the download images + SDImageCache.shared.clear(with: .all, completion: nil) + } +} diff --git a/Example/Demo/WithURLsViewController.swift b/Example/Demo/WithURLsViewController.swift new file mode 100644 index 00000000..37d22450 --- /dev/null +++ b/Example/Demo/WithURLsViewController.swift @@ -0,0 +1,99 @@ +import UIKit +import MHFacebookImageViewer +import SDWebImage + +class WithURLsViewController:UIViewController { + + // load a lower resolution images + var images:[UIImage] = Data.images.compactMap { + $0.resize(targetSize: .thumbnail) + } + + lazy var layout = GalleryFlowLayout() + + lazy var collectionView:UICollectionView = { + // Flow layout setup + let cv = UICollectionView( + frame: .zero, collectionViewLayout: layout) + cv.register( + ThumbCell.self, + forCellWithReuseIdentifier: ThumbCell.reuseIdentifier) + cv.dataSource = self + return cv + }() + + override func loadView() { + super.loadView() + view = UIView() + view.backgroundColor = .white + view.addSubview(collectionView) + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.topAnchor + .constraint(equalTo: view.topAnchor) + .isActive = true + collectionView.leadingAnchor + .constraint(equalTo: view.leadingAnchor) + .isActive = true + collectionView.trailingAnchor + .constraint(equalTo: view.trailingAnchor) + .isActive = true + collectionView.bottomAnchor + .constraint(equalTo: view.bottomAnchor) + .isActive = true + } + + override func viewDidLoad() { + super.viewDidLoad() + title = "Gallery" + SDImageCache.shared.clear(with: .all, completion: nil) + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + updateLayout(view.frame.size) + } + + private func updateLayout(_ size:CGSize) { + if size.width > size.height { + layout.columns = 4 + } else { + layout.columns = 3 + } + } + + override func viewWillTransition( + to size: CGSize, + with coordinator: UIViewControllerTransitionCoordinator) { + updateLayout(size) + } +} + +extension WithURLsViewController:UICollectionViewDataSource { + + func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + func collectionView( + _ collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int { + return images.count + } + + func collectionView( + _ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + + let cell:ThumbCell = collectionView + .dequeueReusableCell(withReuseIdentifier: ThumbCell.reuseIdentifier, + for: indexPath) as! ThumbCell + cell.imageView.image = images[indexPath.item] + + // Setup Image Viewer with [URL] + cell.imageView.setupImageViewer( + urls: Data.imageUrls, + initialIndex: indexPath.item) + + return cell + } +} diff --git a/Sources/UIImageView_Extensions.swift b/Sources/UIImageView_Extensions.swift index 6ccea22c..7c477814 100644 --- a/Sources/UIImageView_Extensions.swift +++ b/Sources/UIImageView_Extensions.swift @@ -91,6 +91,7 @@ extension UIImageView { isUserInteractionEnabled = true contentMode = .scaleAspectFill + clipsToBounds = true if _tapRecognizer == nil { _tapRecognizer = TapWithDataRecognizer(