I am trying to create an app that consists of a collection view which has images dragged onto it.
I am opening google images in a dual display, searching "seals" and dragging the first image of a seal onto my app's viewcontroller. My app is supposed to save the url of the image. Each cell in the collection view is given a url from the saved urls. After the url is set, the cell retrieves the image from the url and places the image inside itself.
The problem is that the wrong url is being taken. The url taken can be converted into data, but that data cannot be converted into a UIImage. The url saved from the drag and drop is not the url for the image, it seems to be the url for a google search or something.
I need to be able to save the correct url for the image so that the ImageCell's can load the image from the url.
When I enter the url into my web browser I receive a page like this.
ImageViewController
import UIKit
class ImageViewController: UIViewController{
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
var gallery = [URL]()
}
extension ImageViewController: UICollectionViewDelegate, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return gallery.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCell
cell.url = gallery[indexPath.item]
return cell
}
}
extension ImageViewController: UICollectionViewDropDelegate{
func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
return session.canLoadObjects(ofClass: NSURL.self) && session.canLoadObjects(ofClass: UIImage.self)
}
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
return UICollectionViewDropProposal(operation: .copy)
}
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
// Get the destion index path, if there's none set, just insert at the beginning
let destinationIndexPath = coordinator.destinationIndexPath ?? IndexPath(item: 0, section: 0)
// Process all items being dragged
for item in coordinator.items {
// External drops must provide url and an image that we'll use to get an aspect-ratio
var url: URL?
// Get the URL
item.dragItem.itemProvider.loadObject(ofClass: NSURL.self) { [weak self] (provider, error) in
url = provider as? URL //Check that url is valid
// If both url and ratio were provided, perform the actual drop
if url != nil{
DispatchQueue.main.async {
collectionView.performBatchUpdates({
// Update model
self?.gallery.insert(url!, at: destinationIndexPath.item)
// Update view
collectionView.insertItems(at: [destinationIndexPath])
// Animates the item to the specified index path in the collection view.
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
})
}
}
}
}
}
}
ImageCell
class ImageCell: UICollectionViewCell{
var url: URL?{
didSet{
self.fetchImage(withURL: url!)
}
}
@IBOutlet var imageView: UIImageView!
private func fetchImage(withURL url: URL) {
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let data = try? Data(contentsOf: url) else {
return
}
guard let image = UIImage(data: data) else {
print("Couldn't convert data to UIImage")
print("The url is")
print(url)
return
}
// If by the time the async. fetch finishes, the imageURL is still the same, update the UI (in the main queue)
if self?.url == url {
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
}
}
GITHUB
To extract the image url from the URL that is provided from a drag and drop from a google image, you can add these extensions to URL. Then on the variable that the url is saved to(url
), you can access the image url using url!.imageURL
rather than just url!
extension URL {
var imageURL: URL {
if let url = UIImage.urlToStoreLocallyAsJPEG(named: self.path) {
// this was created using UIImage.storeLocallyAsJPEG
return url
} else {
// check to see if there is an embedded imgurl reference
for query in query?.components(separatedBy: "&") ?? [] {
let queryComponents = query.components(separatedBy: "=")
if queryComponents.count == 2 {
if queryComponents[0] == "imgurl", let url = URL(string: queryComponents[1].removingPercentEncoding ?? "") {
return url
}
}
}
return self.baseURL ?? self
}
}
}
extension UIImage
{
private static let localImagesDirectory = "UIImage.storeLocallyAsJPEG"
static func urlToStoreLocallyAsJPEG(named: String) -> URL? {
var name = named
let pathComponents = named.components(separatedBy: "/")
if pathComponents.count > 1 {
if pathComponents[pathComponents.count-2] == localImagesDirectory {
name = pathComponents.last!
} else {
return nil
}
}
if var url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
url = url.appendingPathComponent(localImagesDirectory)
do {
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
url = url.appendingPathComponent(name)
if url.pathExtension != "jpg" {
url = url.appendingPathExtension("jpg")
}
return url
} catch let error {
print("UIImage.urlToStoreLocallyAsJPEG \(error)")
}
}
return nil
}
func storeLocallyAsJPEG(named name: String) -> URL? {
if let imageData = self.jpegData(compressionQuality: 1.0) {
if let url = UIImage.urlToStoreLocallyAsJPEG(named: name) {
do {
try imageData.write(to: url)
return url
} catch let error {
print("UIImage.storeLocallyAsJPEG \(error)")
}
}
}
return nil
}
}