Мой пользовательский просмотр коллекции фотоальбомов загружается очень медленно - PullRequest
0 голосов
/ 08 мая 2018

Я загружаю фотографии пользователей из фотоальбома на их телефоне в коллекцию ViewView, которая измеряет высоту строки в соответствии с соотношением сторон фотографий в строке. Когда программа запускается, кажется, что она вначале останавливается на несколько секунд, пока она выполняет все эти вычисления. Есть ли способ ускорить это?

Код пользовательского макета

import Foundation
import UIKit

protocol GreedoSizeCalculatorDataSource {
    func greedoSizeCalculator(_ layout: GreedoSizeCalculator?, originalImageSizeAt indexPath: IndexPath?) -> CGSize
}

class GreedoSizeCalculator {
     //private var _sizeCache = [AnyHashable: Any]()
    private var sizeCache = [IndexPath:CGSize]()
    private var leftOvers:[CGSize] = [CGSize]()
    var lastIndexPathAdded:IndexPath?


    var dataSource: GreedoSizeCalculatorDataSource?
    var rowMaximumHeight: CGFloat = 0.0
    var isFixedHeight = false
    var contentWidth: CGFloat = 0.0
    var interItemSpacing: CGFloat = 2.0


    func sizeForPhoto(at indexPath:IndexPath?)->CGSize{
        print("I made it rowHeight: \(rowMaximumHeight)")
        if let indexPath = indexPath{
            if sizeCache[indexPath] == nil{
                lastIndexPathAdded = indexPath
                computeSizes(at:indexPath)
            }
        }
        var size: CGSize? = sizeCache[indexPath!]
        if((size?.width)! < CGFloat(0.0) || (size?.height)! < CGFloat(0.0)){
            size = CGSize.zero
        }
        return size!
    }

    func clearCache(){
        self.sizeCache.removeAll()
    }

    func clearCache(after indexPath:IndexPath?){
        sizeCache.removeValue(forKey: indexPath!)

        for existingIndexPath in sizeCache.keys{
            if indexPath?.compare(existingIndexPath) == .orderedDescending{
                sizeCache.removeValue(forKey: existingIndexPath)
            }
        }

    }

    func computeSizes(at indexPath:IndexPath?){
        var photoSize = dataSource?.greedoSizeCalculator(self, originalImageSizeAt: indexPath)
        if (photoSize?.width)! < CGFloat(1) || (photoSize?.height)! < CGFloat(1) {
            // Photo with no height or width
            print("Photo has no height or width")
            photoSize?.width = rowMaximumHeight
            photoSize?.height = rowMaximumHeight
        }
        print("The photo size at index: \((indexPath?.item)!) is \nwidth: \((photoSize?.width)!), height: \((photoSize?.height)!)")
        leftOvers.append(photoSize!)
        print("Index:\(indexPath?.item) leftOvers.count: \(leftOvers.count)")
        var enoughContentForTheRow = false
        var rowHeight:CGFloat = rowMaximumHeight
        var widthMultiplier:CGFloat = 1.0
        //Calculations For Variable HeightGrid
        var totalAspectRatio:CGFloat = 0.0
        for leftOver in leftOvers{
            print("current aspect ratio: \(leftOver.width/leftOver.height)")
            totalAspectRatio += leftOver.width/leftOver.height
        }
        print("totalAspectRatio: \(totalAspectRatio)")
        //Aspect Ratio Calculation how to find the height of the row!!!!!!!!!!!!!!!!!!!!!!!
        rowHeight = CGFloat(contentWidth)/CGFloat(totalAspectRatio)
        print("rowHeight: \(rowHeight)")
        enoughContentForTheRow = rowHeight < rowMaximumHeight

        if enoughContentForTheRow {
            var availableSpace: CGFloat = contentWidth
            print("Available space: \(availableSpace)")
            var index: Int = 0
            for leftOver in leftOvers{
                var newWidth:CGFloat = floor((rowHeight*leftOver.width)/leftOver.height)
                print("new width: \(newWidth)")
                newWidth = min(availableSpace, newWidth)

            //add the size into the cahce
                self.sizeCache[lastIndexPathAdded!] = CGSize(width: newWidth, height: rowHeight)
                availableSpace -= newWidth
                availableSpace -= self.interItemSpacing
                // We need to keep track of the last index path added
                self.lastIndexPathAdded = IndexPath(item:(lastIndexPathAdded?.item)!+1,section:(lastIndexPathAdded?.section)!)
                index += 1
            }
            leftOvers.removeAll()
        }
        else{
            // The line is not full, let's ask the next photo and try to fill up the line
            computeSizes(at: IndexPath(item:(indexPath?.item)!+1,section:(indexPath?.section)!))
        }
    }


}

protocol GreedoCollectionViewLayoutDataSource {
    func greedoCollectionViewLayout(_ layout: GreedoCollectionViewLayout?, originalImageSizeAt indexPath: IndexPath?) -> CGSize
}

class GreedoCollectionViewLayout:GreedoSizeCalculatorDataSource {
    var dataSource: GreedoCollectionViewLayoutDataSource?
    var rowMaximumHeight: CGFloat = 0.0
    var isFixedHeight = false

    var collectionView:UICollectionView?
    private var _greedo: GreedoSizeCalculator?
    var greedo: GreedoSizeCalculator? {
        if _greedo == nil {
            _greedo = GreedoSizeCalculator()
            _greedo?.rowMaximumHeight = 200
            _greedo?.isFixedHeight = false
            _greedo?.dataSource = self
        }
        return _greedo
    }

    init(collectionView: UICollectionView?) {
        self.collectionView = collectionView
    }

    func sizeForPhoto(at indexPath: IndexPath?) -> CGSize {
        var contentWidth:CGFloat = (self.collectionView?.bounds.size.width)!
        var interitemspacing:CGFloat = 2.0
        let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout

        if layout != nil {
            contentWidth -= (layout?.sectionInset.left ?? 0.0) + (layout?.sectionInset.right ?? 0.0)
            interitemspacing = (layout?.minimumInteritemSpacing)!
            layout?.minimumLineSpacing = 5.0
            layout?.minimumInteritemSpacing = 5.0
        }
        greedo?.contentWidth = contentWidth
        greedo?.interItemSpacing = interitemspacing
        return (greedo?.sizeForPhoto(at: indexPath))!
    }

    func clearCache() {
        greedo?.clearCache()
    }

    func clearCache(after indexPath: IndexPath?) {
        greedo?.clearCache(after: indexPath)
    }

    func rowmaximumHeight() -> CGFloat {
        return (greedo?.rowMaximumHeight)!
    }

    func setRowMaximumHeight(_ rowMaximumHeight: CGFloat) {
        greedo?.rowMaximumHeight = rowMaximumHeight
    }

    func greedoSizeCalculator(_ layout: GreedoSizeCalculator?, originalImageSizeAt indexPath: IndexPath?) -> CGSize {
        return dataSource!.greedoCollectionViewLayout(self, originalImageSizeAt: indexPath)
    }


}

Главный вид Код контроллера

import UIKit
import Photos

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout,UICollectionViewDataSource,GreedoCollectionViewLayoutDataSource {

    var imageArray:[UIImage] = [UIImage]()
     var collectionViewSizeCalculator:GreedoCollectionViewLayout?

    var collectionView:UICollectionView! = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 1
        layout.minimumLineSpacing = 2.0
        layout.scrollDirection = .vertical
        let CV = UICollectionView(frame: UIScreen.main.bounds, collectionViewLayout: layout)
        CV.translatesAutoresizingMaskIntoConstraints = false
        return CV
    }()


    var fetchResult: PHFetchResult<PHAsset> = PHFetchResult()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.collectionViewSizeCalculator = GreedoCollectionViewLayout(collectionView: self.collectionView)
        self.collectionViewSizeCalculator?.dataSource = self
        self.collectionViewSizeCalculator?.rowMaximumHeight = self.collectionView.bounds.height / 3;
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 5.0
        layout.minimumLineSpacing = 5.0
        layout.sectionInset = UIEdgeInsetsMake(10.0, 5.0, 5.0, 5.0)
        self.collectionView.collectionViewLayout = layout
        //End Of new Greedo Stuff in ViewDidLoad()



        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        collectionView.register(customPhotoCell.self, forCellWithReuseIdentifier: "Cell")
        self.collectionView.backgroundColor = .white
        self.view.addSubview(collectionView)


        setUpLayout()
        fetchAssets()

        let photos = PHPhotoLibrary.authorizationStatus()
        if photos == .notDetermined {
            PHPhotoLibrary.requestAuthorization({status in
                if status == .authorized{
                    print("permission granted")
                    DispatchQueue.main.async {
                        self.fetchAssets()
                        self.collectionView.reloadData()
                    }
                } else {
                    print("permission not granted")
                }
            })
        }


    }

    func setUpLayout(){
        collectionView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true

    }

    func fetchAssets(){
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
    }



    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! customPhotoCell

        let imageView = cell.imgView
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true
        requestOptions.deliveryMode = .highQualityFormat
        let scale = min(2.0, UIScreen.main.scale)
        let requestImageSize = CGSize(width: cell.bounds.width * scale, height: cell.bounds.height * scale)
        PHImageManager.default().requestImage(for: fetchResult.object(at: indexPath.item) , targetSize: requestImageSize, contentMode: .aspectFill, options: requestOptions, resultHandler: {
            image,error in
            if let error = error{
                print(error)
            }
            self.imageArray.append(image!)
            imageView.image = image!
            imageView.clipsToBounds = false
            imageView.contentMode = .scaleAspectFill
        })
        cell.layer.masksToBounds = true
        cell.layer.cornerRadius = 10
        cell.backgroundColor = .white
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return fetchResult.count
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 5.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 5.0
    }



    //Greedo Size for itemAt
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return self.collectionViewSizeCalculator!.sizeForPhoto(at: indexPath)
    }

    func greedoCollectionViewLayout(_ layout: GreedoCollectionViewLayout?, originalImageSizeAt indexPath: IndexPath?) -> CGSize {
        if (indexPath?.item)! < fetchResult.count{
            let asset = fetchResult[(indexPath?.item)!]
            return CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
        }
        return CGSize(width: 0.1, height: 0.1)
    }

    //New stuff
    var lastOffsetY:CGFloat = 0
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        lastOffsetY = scrollView.contentOffset.y
    }
    func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
        if(scrollView.contentOffset.y > self.lastOffsetY){
            UIView.animate(withDuration: 1.0, animations: {
                self.collectionView.transform = CGAffineTransform(translationX: 0, y: -50)
            }, completion: nil)
        }
    }
}



class customPhotoCell:UICollectionViewCell{

    var imgView:UIImageView = {
        let imgV = UIImageView()
        imgV.backgroundColor = .orange
        imgV.translatesAutoresizingMaskIntoConstraints = false
        return imgV
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        print("Inside of init")
        self.addSubview(imgView)
        self.layer.cornerRadius = 10.0
        imgView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        imgView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        imgView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
        imgView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


}

extension UIImageView{
    func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) {
        let options = PHImageRequestOptions()
        options.version = .original
        PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in
            guard let image = image else { return }
            switch contentMode {
            case .aspectFill:
                self.contentMode = .scaleAspectFill
            case .aspectFit:
                self.contentMode = .scaleAspectFit
            }
            self.image = image
        }
    }
}

extension UIImage{
    func resizeImageWith() -> UIImage {
        let aspectRatio = CGFloat(self.size.width / self.size.height)
        let newWidth = UIScreen.main.bounds.width
        let newSize = CGSize(width: newWidth, height: newWidth/aspectRatio)
        UIGraphicsBeginImageContextWithOptions(newSize, true, 0)
        self.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
        let  newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}
...