2017-10-14 19 views
0

Je travaille sur une application qui donnera à l'utilisateur la possibilité de sélectionner une photo appliquer un filtre et l'enregistrer. Fondamentalement est une copie de la caméra de l'application instagram où l'utilisateur sélectionner une photo de la bibliothèque, appliquer un filtre, puis le poster.Swift CIfilter sélectionner à partir d'une collectionVoir

L'application fonctionne bien, mon seul problème est que lors du chargement de la collectionView avec les images filtrées, le processus est plutôt lent. J'ai lu un peu (je suis novice et c'est la première fois que j'ai travaillé avec l'image de base) et j'ai essayé d'appliquer tous les conseils que j'ai appris. Cependant, je crois que l'application est toujours lente.

Alors, voici ce que j'ai fait.

J'ai créé un modèle avec les filtres:

public protocol Filter { 
// reference to the core image filter 
var filter: CIFilter { get } 
// output of the filter. 
var outputImage: CIImage? { get } 
} 

// common to all filters. 
extension Filter { 
    public var outputImage: CIImage? { return self.filter.outputImage } 
} 

//Bloom 
public class Bloom: Filter { 

public let filter: CIFilter 

public init(inputImage: CIImage, inputRadius: CGFloat = 10.0, 
inputIntensity: CGFloat = 1.0) { 
    let parameters:[String : Any] = [ 
     "inputImage":inputImage, 
     "inputRadius":inputRadius, 
     "inputIntensity":inputIntensity  ] 
    guard let filter = CIFilter(name:"CIBloom", withInputParameters: parameters) else { fatalError() } 
    self.filter = filter 
    } 
} 


// Box Blur 
public class BoxBlur: Filter { 

public let filter: CIFilter 

public init(inputImage: CIImage, inputRadius: CGFloat = 10.0) { 
    let parameters:[String : Any] = [ 
     "inputImage":inputImage, 
     "inputRadius":inputRadius  ] 
    guard let filter = CIFilter(name:"CIBoxBlur", withInputParameters: parameters) else { fatalError() } 
    self.filter = filter 
    } 
} 
........... 

et ainsi de suite pour tous les filtres dont j'ai besoin.

Que j'ai créé une classe pour créer un tableau UIImage avec toutes les images filtrées:

class filteredImages { 
static var filterToApply: Filter! 
static var filterNames = [ 
    "Original","Sepia","Poster",.... 
] 

static var filteredImages = [UIImage]() 
static var filteredImage: UIImage! 

static func createImageArray(inputImage: CIImage, onSuccess: @escaping() ->()) { 
    filteredImages.removeAll() 
    for filter in filterNames { 
     switch filter { 
     case "Original": 
      filteredImage = UIImage(ciImage:inputImage) 
      filteredImages.append(filteredImage) 
     case "Sepia": 
      filterToApply = SepiaTone(inputImage: inputImage, inputIntensity: 0.8) 
      filteredImage = UIImage(ciImage: filterToApply.outputImage!) 
      filteredImages.append(filteredImage) 
............ 
     } 
     onSuccess() 
    } 
    } 
} 

Et enfin dans mon contrôleur:

var filteredImagesArray = [UIImage]() 
override func viewWillAppear(_ animated: Bool) { 
    super.viewWillAppear(true) 
    let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)! 
    filteredImages.createImageArray(inputImage: inputImage) { 
     self.filteredImagesArray = filteredImages.filteredImages 
    } 
} 
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCell", for: indexPath) as! FilterCell 

     cell.filteredImage.image = filteredImagesArray[indexPath.row] 
     cell.filterName.text = filteredImages.filterNames[indexPath.row] 
     cell.layer.borderWidth = 0.7 
     cell.layer.borderColor = UIColor.white.cgColor 
    return cell 
} 

J'ai aussi une extension pour réduire la " original "image:

extension UIImage { 
func resized(toWidth width: CGFloat) -> UIImage? { 
    let canvasSize = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height))) 
    UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale) 
    defer { UIGraphicsEndImageContext() } 
    draw(in: CGRect(origin: .zero, size: canvasSize)) 
    return UIGraphicsGetImageFromCurrentImageContext() 
    } 
} 

Y at-il un moyen que je puisse le rendre plus rapide? Ai-je raté quelque chose?

Merci.

----------- UNE MISE À JOUR PAR Ladislav DEMANDE

Voici le code que j'utilise pour obtenir des images de la bibliothèque:

enum Section: Int { 
    case allPhotos = 0 
    case smartAlbums 
    case userCollections 

    static let count = 3 
} 
var allPhotos: PHFetchResult<PHAsset>! 
var allPhotosFromAlbum = PHFetchResult<PHAsset>() 
var smartAlbums: [PHAssetCollection] = [] 
var userCollections: PHFetchResult<PHAssetCollection>! 
let subtypes:[PHAssetCollectionSubtype] = [ 
    .smartAlbumFavorites, 
    .smartAlbumPanoramas, 
    .smartAlbumScreenshots, 
    .smartAlbumSelfPortraits, 
    .smartAlbumVideos, 
    .smartAlbumRecentlyAdded, 
    .smartAlbumSelfPortraits 
] 
override func viewDidLoad() { 
    super.viewDidLoad() 
    // Create a PHFetchResult object for each section in the table view. 
    let allPhotosOptions = PHFetchOptions() 
    allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] 
    allPhotos = PHAsset.fetchAssets(with: allPhotosOptions) 
    let options = PHFetchOptions() 
    options.predicate = NSPredicate(format: "estimatedAssetCount > 0") 
    options.sortDescriptors = [NSSortDescriptor(key: "localizedTitle", ascending: false)] 
    smartAlbums = fetchSmartCollections(with: .smartAlbum, subtypes: subtypes) 
    userCollections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: options) 
} 

ici est la fonction i utiliser pour se PHAssetCollection avec Sous-types:

private func fetchSmartCollections(with: PHAssetCollectionType, subtypes: [PHAssetCollectionSubtype]) -> [PHAssetCollection] { 
    var collections:[PHAssetCollection] = [] 
    let options = PHFetchOptions() 
    options.includeHiddenAssets = false 

    for subtype in subtypes { 
     if let collection = PHAssetCollection.fetchAssetCollections(with: with, subtype: subtype, options: options).firstObject, collection.photosCount > 0 { 
      collections.append(collection) 
     } 
    } 

    return collections 
} 

I afficher les résultats dans un tableView puis lors de la sélection je passe toutes les images de l'album à un autre contrôleur.

Voici le code du contrôleur (appelons-le mMainVC) où i afficher réellement les images pour sélectionner:

fileprivate let imageManager = PHCachingImageManager() 
var fetchResult: PHFetchResult<PHAsset>! 
var assetCollection: PHAssetCollection! 

override func viewDidLoad() { 
    super.viewDidLoad() 
if fetchResult == nil { 
     let allPhotosOptions = PHFetchOptions() 
     allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] 
     fetchResult = PHAsset.fetchAssets(with: allPhotosOptions) 
    } 
} 

et ici la fonction pour obtenir l'image du fetchResult:

func selectImageFromAssetAtIndex(index: Int) { 
    let asset = fetchResult.object(at: index) 
    let size = scrollViewImage.frame.size.width 

    PHImageManager.default().requestImage(for: asset, targetSize: CGSize(width: size, height: size), contentMode: .aspectFill, options: nil, resultHandler: { (image, info) in 
     DispatchQueue.main.async { 
      self.displayImageInScrollView(image: image!) 
     } 
    }) 
} 

--------------------- ET VIDEO SCREENSHOT

enter image description here enter image description here

est ici le lien pour la vidéo: https://drive.google.com/file/d/0B6U8olIA_ZS8U0tpN0hKOWdrVm8/view?usp=sharing

+0

êtes-vous ge tation de photos depuis Camera roll? – Ladislav

+0

@Ladislav oui j'obtiens la photo de la photothèque ... j'implémenterai plus tard la possibilité de filmer une photo puis d'appliquer le filtre – Marco

+0

Pouvez-vous ajouter l'extrait de code comment vous obtenez actuellement les images de PhotoKit .. ? – Ladislav

Répondre

1

Tout ce que vous faites est bon, je pense, le seul problème est que

override func viewWillAppear(_ animated: Bool) { 
    super.viewWillAppear(true) 
    let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)! 
    filteredImages.createImageArray(inputImage: inputImage) { 
     self.filteredImagesArray = filteredImages.filteredImages 
    } 
} 

createImageArray est appelé sur le thread principal et qui est la raison pour laquelle votre l'application se bloque.

Je voudrais ajouter un état loading avec activityIndicator à chaque collectionViewcell et les cellules seraient en loading état jusqu'à ce que vous obtenir le résultat de retour de l'appel createImageArray, alors vous remplir les cellules avec des images filtrées, quelque chose comme:

override func viewWillAppear(_ animated: Bool) { 
    super.viewWillAppear(true) 
    DispatchQueue.global(qos: .userInitiated).async {[weak self] in 
     let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)! 
     filteredImages.createImageArray(inputImage: inputImage) { 
      self.filteredImagesArray = filteredImages.filteredImages 
      DispatchQueue.main.async { 
       let indexPaths = collectionView.indexPathsForVisibleItems 

       collectionView.reloadItems(at: indexPaths) 
      } 
     } 
    } 
} 

donc, au moment où vous rechargez les éléments visibles self.filteredImagesArray seront fixés et vous serez en mesure de désactiver l'état loading et montrer les images de la filteredImagesArray

+0

Merci pour le répondre ... mon problème est que lorsque le défilement scrollview à un certain point sera lent ... ce que vous avez suggéré pourrait aider dans ce domaine? désolé mais vu que je suis si nouveau à swift je ne suis pas sûr que je le comprends à 100% ... Dois-je appliquer l'état de chargement dans le cellForItemAt? – Marco

+0

Lorsque la vue de collection est chargée et que vous avez déjà filtré toutes les images que vous stockez dans 'self.filteredImagesArray' il devrait être très rapide à partir de maintenant ... – Ladislav

+0

S'il vous plaît également poster peut-être une capture d'écran ou deux de l'application, peut-être que je suis manque quelque chose ici – Ladislav