NSOPeration загружает одни и те же изображения в обе мои коллекции. - PullRequest
0 голосов
/ 31 октября 2018

Я создаю приложение, которое использует NSOperation для извлечения изображений из Flickr API в мои collectionViews. У меня есть два collectionViews, поэтому я делаю 2 выборки одновременно. Operation1 извлекает изображения из API, Operation 2 кэширует изображения, а Operation 3 загружает изображения в их collectionView.

Это я говорю mainViewController:

импорт UIKit

класс ShudderViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

var places = ["1","2","3","4","5","6","7","8","9"]
var moviesImage = [#imageLiteral(resourceName: "HorrorMovie4"), #imageLiteral(resourceName: "HorrorMovie1"),#imageLiteral(resourceName: "HorrorMovie3"),#imageLiteral(resourceName: "HorrorMovie2"),#imageLiteral(resourceName: "HorrorMovie1"),]
let infinite =  100000
var collectionView2Photos = [Photo]() {
    didSet {
        print("2 Photo ID = \(collectionView2Photos[0].id)")
    }
}
var collectionView3Photos = [Photo]() {
    didSet {
        print("3 Photo ID = \(collectionView3Photos[0].id)")
        print("Collection view 3 got pics")
    }
}

@IBOutlet weak var mainCollectionView: UICollectionView!

@IBOutlet weak var newlyAddedCollectionView: UICollectionView!

@IBOutlet weak var curatorsChoiseCollectionView: UICollectionView!


override func viewDidLoad() {
    super.viewDidLoad()
    mainCollectionView.layer.masksToBounds = false
    mainCollectionView.delegate = self
    mainCollectionView.dataSource = self

    newlyAddedCollectionView.layer.masksToBounds = false
    newlyAddedCollectionView.delegate = self
    newlyAddedCollectionView.dataSource = self

    curatorsChoiseCollectionView.layer.masksToBounds = false
    curatorsChoiseCollectionView.delegate = self
    curatorsChoiseCollectionView.dataSource = self

    photoController.searchForFlicker(withSearchterm: "Jesus") { (sucess, photos) in
        if sucess {
            guard let photos = photos else {return}
            DispatchQueue.main.async {
                self.collectionView2Photos = photos
                self.mainCollectionView.reloadData()
                self.newlyAddedCollectionView.reloadData()
            }
        }
    }
    photoController.searchForFlicker(withSearchterm: "Basketball") { (sucess, photos) in
        if sucess {
            guard let photos = photos else {return}
            DispatchQueue.main.async {
                self.collectionView3Photos = photos
                self.curatorsChoiseCollectionView.reloadData()
            }
        }
    }

}

override func viewDidAppear(_ animated: Bool) {
    let midIndexPath = IndexPath(row: infinite / 2, section: 0)
    mainCollectionView.scrollToItem(at: midIndexPath,
                                    at: .centeredHorizontally,
                                    animated: false)
}



// Make collection view cells fill as much available width as possible
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    var totalUsableWidth = collectionView.frame.width
    let inset = self.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: indexPath.section)
    totalUsableWidth -= inset.left + inset.right

    let minWidth: CGFloat = 150.0
    let numberOfItemsInOneRow = Int(totalUsableWidth / minWidth)
    totalUsableWidth -= CGFloat(numberOfItemsInOneRow - 1) * flowLayout.minimumInteritemSpacing
    let width = totalUsableWidth / CGFloat(numberOfItemsInOneRow)
    return CGSize(width: width, height: width)
}

// Add margins to the left and right side
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 0, left: 10.0, bottom: 0, right: 10.0)
}




func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    if collectionView == self.newlyAddedCollectionView {
        //print(photoController.photoDetail.count)
        return collectionView2Photos.count

    } else if collectionView == curatorsChoiseCollectionView {
        return collectionView3Photos.count
    }


    return infinite
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    if collectionView == self.mainCollectionView {


        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: "mainCell", for: indexPath) as! MainCollectionViewcell


        cell.imageView.image = moviesImage[indexPath.row % moviesImage.count]
        cell.layer.cornerRadius = 12
        return cell
    }

    else if collectionView == self.newlyAddedCollectionView{
        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: "newAddedCell", for: indexPath) as! newlyAddedCollectionViewCell


        loadImage(forCell: cell, forItemAt: indexPath, arrayOfPhotos: collectionView2Photos, collectionView: newlyAddedCollectionView)
        return cell
    }

    else {
        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: "curatorsCollectionViewCell", for: indexPath) as! CuratorsChoiceCollectionViewCell

        loadImage(forCell: cell, forItemAt: indexPath, arrayOfPhotos: collectionView3Photos, collectionView: curatorsChoiseCollectionView)
        return cell
    }
}


private func loadImage(forCell cell: UICollectionViewCell, forItemAt indexPath: IndexPath, arrayOfPhotos: [Photo], collectionView: UICollectionView) {

    if collectionView == curatorsChoiseCollectionView {
        let cell = cell as! CuratorsChoiceCollectionViewCell
        let photo = arrayOfPhotos[indexPath.item]
        if let image = cache[photo.id]{
            cell.imageView.image = image
        }

        else {

            let op1 = FetchPhotoOperation(photoRef: photo)

            let op2 = BlockOperation {
                guard let image = op1.image else { return }

                self.cache.cache(value: image, for: photo.id)
            }

            let op3 = BlockOperation {

                guard let image = op1.image else { print("Something went wrong PEREZ");return }
                if indexPath == collectionView.indexPath(for: cell) {

                    cell.imageView.image = image

                }
                else {
                    self.fetchRequests[photo.id]?.cancel()

                }
            }
            op3.addDependency(op1)
            op2.addDependency(op1)
            OperationQueue.main.addOperation(op3)
            photoFetchQueue.addOperations([op1, op2], waitUntilFinished: false)

            fetchRequests[photo.id] = op1
        }
    } else if collectionView == newlyAddedCollectionView {
        let cell = cell as! newlyAddedCollectionViewCell
        let photo = arrayOfPhotos[indexPath.item]
        if let image = cache[photo.id]{
            cell.imageView.image = image
        }

        else {

            let op1 = FetchPhotoOperation(photoRef: photo)


            let op2 = BlockOperation {
                guard let image = op1.image else { return }
                self.cache.cache(value: image, for: photo.id)
            }

            let op3 = BlockOperation {
                print("testing")
                guard let image = op1.image else { print("Something went wrong PEREZ");return }
                if indexPath == collectionView.indexPath(for: cell) {

                    cell.imageView.image = image


                }
                else {
                    self.fetchRequests[photo.id]?.cancel()

                }
            }
            op3.addDependency(op1)
            op2.addDependency(op1)
            OperationQueue.main.addOperation(op3)
            photoFetchQueue.addOperations([op1, op2], waitUntilFinished: false)

            fetchRequests[photo.id] = op1
        }
    }
}

@IBOutlet weak var scaryImage: UIImageView!
private  let photoController = PhotoController()
private var cache: Cache<String, UIImage> = Cache()
private var photoFetchQueue = OperationQueue()
private var fetchRequests: [String: FetchPhotoOperation] = [:] {
    didSet{
        print("Hello there")
    }
}

}

Это мой кеш-файл:

class Cache<Key, Value> where Key: Hashable {

    private var queue = DispatchQueue(label: "CSG.ShudderAppInterview.CacheSerialQueue")
    private var cachedItems: [Key: Value] = [:]

    subscript(_ key: Key) -> Value? {
        get {
            // waits for synchronous task to complete before going on to other tasks
            return queue.sync { cachedItems[key] ?? nil }
        }
    }

    func cache(value: Value, for key: Key) {
        queue.async { self.cachedItems[key] = value }
    }
}

и это моя операция FetchPhotos:

import Foundation
import UIKit

class ConcurrentOperation: Operation {

    enum State: String {
        case isReady, isExecuting, isFinished
    }

    private var _state = State.isReady

    private let stateQueue = DispatchQueue(label: "CSG.ShudderAppInterview.ConcurrentOperationStateQueue")
    var state: State {
        get {
            var result: State?
            let queue = self.stateQueue
            queue.sync {
                result = _state
            }
            return result!
        }

        set {
            let oldValue = state
            willChangeValue(forKey: newValue.rawValue)
            willChangeValue(forKey: oldValue.rawValue)

            stateQueue.sync { self._state = newValue }

            didChangeValue(forKey: oldValue.rawValue)
            didChangeValue(forKey: newValue.rawValue)
        }

    }

    override  dynamic var isReady: Bool {
        return super.isReady && state == .isReady
    }

    override dynamic var isExecuting: Bool {
         return state == .isExecuting
    }

    override dynamic var  isFinished: Bool {
        return state == .isFinished
    }

    override  var isAsynchronous: Bool {
    return true 
    }

}
//NSOperation subclass

class FetchPhotoOperation : ConcurrentOperation {

    init(photoRef: Photo) {
        self.photo = photoRef
    }

    var photo: Photo
    var image : UIImage?
    private var dataTask: URLSessionDataTask?

    override func start() {
        state = .isExecuting

        guard let url = URL(string: "https://farm\(photo.farm).staticflickr.com/\(photo.server)/\(photo.id)_\(photo.secret).jpg")     else { return }
        print(url)
        dataTask = URLSession.shared.dataTask(with: url) { (data, _, error) in
            defer { self.state = .isFinished }
            if let error = error {
                NSLog("Error retrieving image from url: \(error), Refer to your dataTast in FetchOperation")
                return
            }
            guard let data = data else { return }
            if let downloadedImage = UIImage(data: data) {
                self.image = downloadedImage
            }
        }

        dataTask?.resume()
    }

    override func cancel() {
        dataTask?.cancel()
    }

}
...