Search code examples
iosswiftcs193p

Swift: Drag and Drop, save url of image from google images


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.

enter image description here

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

Here is my project on github


Solution

  • 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
        }
    }