Я загружаю фотографии пользователей из фотоальбома на их телефоне в коллекцию 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!
}
}