Reputation: 101
I had an app crash from a test flight user. I cannot replicate the issue and was having trouble narrowing it down. Any help or suggestions is appreciated! Here is where the issue is according to xcode: HomeViewController.actnShare()
@objc func actnShare(){
let asset = self.allImages[kolodaView.currentCardIndex]
let image : UIImage = convertImageFromAsset(asset: asset)!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [image], applicationActivities: nil)
My HomeViewController code:
import UIKit
import Photos
import PhotosUI
import Koloda
import SimpleImageViewer
import AssetsPickerViewController
import MediaBrowser
import SVProgressHUD
enum SortOrder{
case Random
case Ascending
case Descending
}
class HomeViewController: UIViewController {
@IBOutlet weak var mainKoladaView: UIView!{
didSet{
mainKoladaView.layer.cornerRadius = 10.0
}
}
@IBOutlet weak var mainCardView: UIView!{
didSet{
mainCardView.layer.cornerRadius = 10.0
}
}
@IBOutlet weak var submainCardView: UIView!{
didSet{
submainCardView.layer.cornerRadius = 20.0
}
}
@IBOutlet weak var imageDateLabel: UILabel!
@IBOutlet weak var imageCountLabel: UILabel!
@IBOutlet weak var kolodaView: KolodaView!{
didSet{
kolodaView.layer.cornerRadius = 10.0
}
}
@IBOutlet weak var imageCountView: UIView!{
didSet{
imageCountView.layer.cornerRadius = imageCountView.frame.height / 2
}
}
@IBOutlet weak var sortOrderLabel: UILabel!
@IBOutlet weak var sortView: UIView!{
didSet{
sortView.layer.cornerRadius = sortView.frame.height / 2
}
}
@IBOutlet weak var dateView: UIView!{
didSet{
dateView.layer.cornerRadius = dateView.frame.height / 2
}
}
@IBOutlet weak var lblCount: UILabel!
@IBOutlet weak var sortButton: UIButton!
var allImagesModels = [PhotoModel]()
var allImages = [PHAsset]()
var assets = [PHAsset]()
var sortOrder : SortOrder = .Random
var mediaArray = [Media]()
var selectionMedia = [PHAsset]()
var selections = [Bool]()
var maximumCountToDelete = 20//100
var imageCount = 1
var maximumCountToKeep = 20//100
var tempDeleteCount = 0
var tempKeepCount = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getAllImages()
selectionMedia = ImageManager.shared.deleteAssetList
self.setUpCount()
kolodaView.dataSource = self
kolodaView.delegate = self
tempDeleteCount = AppManager.shared.userClick
tempKeepCount = AppManager.shared.keepCount
self.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
}
func showUpgradeScreen(assetVC : AssetsPickerViewController?){
let upgradeVc = self.storyboard?.instantiateViewController(withIdentifier: "UpgradeViewController") as! UpgradeViewController
upgradeVc.modalPresentationStyle = .overCurrentContext
upgradeVc.modalTransitionStyle = .crossDissolve
upgradeVc.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0)
DispatchQueue.main.async {
if assetVC != nil{
assetVC?.present(upgradeVc, animated: true, completion: nil)
}else{
self.present(upgradeVc, animated: true, completion: nil)
}
}
}
func setUpCount(){
self.lblCount.adjustsFontSizeToFitWidth = true
if selectionMedia.count > 0 {
self.lblCount.text = "\(selectionMedia.count)"
}else{
self.lblCount.isHidden = true
}
NotificationCenter.default.addObserver(self, selector: #selector(actnCountChange), name: NSNotification.Name(DELETE_PHOTOS_CHANGES), object: nil)
}
@objc func actnCountChange(){
if ImageManager.shared.deleteAssetList.count > 0 {
DispatchQueue.main.async {
self.lblCount.isHidden = false
self.lblCount.text = "\(ImageManager.shared.deleteAssetList.count)"
}
}else{
self.lblCount.isHidden = true
}
}
func getAllImages(){
SVProgressHUD.show()
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
print("Found \(allPhotos.count) assets")
print("all Photos \(allPhotos)")
self.addImagetoArray(photos: allPhotos)
SVProgressHUD.dismiss()
case .denied, .restricted:
print("Not allowed")
SVProgressHUD.dismiss()
case .notDetermined:
// Should not see this when requesting
print("Not determined yet")
SVProgressHUD.dismiss()
case .limited:
<#code#>
@unknown default:
fatalError()
}
}
}
func convertImageFromAsset(asset: PHAsset) -> UIImage? {
var img: UIImage?
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.version = .original
options.isSynchronous = true
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
if let data = data {
img = UIImage(data: data)
}
}
return img
}
func addImagetoArray(photos : PHFetchResult<PHAsset>){
self.allImages.removeAll()
for i in 0...photos.count - 1 {
let photoAsset = photos.object(at: i)
self.allImages.append(photoAsset)
}
self.allImages = allImages.shuffled()
DispatchQueue.main.async {
self.imageCountLabel.text = "1/\(self.allImages.count)"
self.imageDateLabel.text = self.allImages.first?.creationDate?.string(withFormat: "MMM d, yyyy")
self.kolodaView.reloadData()
}
}
//MARK: Actions
@IBAction func sortAction(_ sender: UIButton) {
let sheet = UIAlertController.init(title: "", message: "Please sort select options", preferredStyle: UIAlertController.Style.actionSheet)
sheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: { _ in
//Cancel Action
}))
sheet.addAction(UIAlertAction(title: "Random",
style: UIAlertAction.Style.default,
handler: {(_: UIAlertAction!) in
self.sortOrder = .Random
self.sortOrderLabel.text = "Random"
self.allImages = self.allImages.shuffled()
DispatchQueue.main.async {
self.imageDateLabel.text = self.allImages.first?.creationDate?.string(withFormat: "MMM d, yyyy")
self.kolodaView.reloadData()
}
}))
sheet.addAction(UIAlertAction(title: "Newest First",
style: UIAlertAction.Style.default,
handler: {(_: UIAlertAction!) in
self.sortOrder = .Ascending
self.sortOrderLabel.text = "Newest First"
self.allImages = self.allImages.sorted(by: { $0.creationDate?.compare($1.creationDate ?? Date()) == .orderedDescending })
DispatchQueue.main.async {
self.imageDateLabel.text = self.allImages.first?.creationDate?.string(withFormat: "MMM d, yyyy")
self.kolodaView.reloadData()
}
}))
sheet.addAction(UIAlertAction(title: "Oldest First",
style: UIAlertAction.Style.default,
handler: {(_: UIAlertAction!) in
self.sortOrder = .Descending
self.sortOrderLabel.text = "Oldest First"
self.allImages = self.allImages.sorted(by: { $0.creationDate?.compare($1.creationDate ?? Date()) == .orderedAscending})
DispatchQueue.main.async {
self.imageDateLabel.text = self.allImages.first?.creationDate?.string(withFormat: "MMM d, yyyy")
self.kolodaView.reloadData()
}
}))
self.showSheet(sheet: sheet, sender: UIButton(), owner: self)
// if sortOrder == .Random{
// self.sortOrder = .Descending
// self.sortButton.setTitle("Sort by: Newest First", for: .normal)
// self.allImages = allImages.shuffled()
// }else if sortOrder == .Descending{
// self.sortOrder = .Ascending
// self.sortButton.setTitle("Sort by: Oldest First", for: .normal)
// self.allImages = allImages.sorted(by: { $0.creationDate?.compare($1.creationDate ?? Date()) == .orderedDescending })
// }else{
// self.sortOrder = .Random
// self.sortButton.setTitle("Sort by: Random", for: .normal)
// self.allImages = allImages.sorted(by: { $0.creationDate?.compare($1.creationDate ?? Date()) == .orderedAscending })
// }
// DispatchQueue.main.async {
// self.imageDateLabel.text = self.allImages.first?.creationDate?.string(withFormat: "MMM d, yyyy")
// self.kolodaView.reloadData()
// }
}
func showSheet(sheet: UIAlertController, sender: UIView, owner: UIViewController) {
switch UIDevice.current.userInterfaceIdiom {
case .phone:
owner.present(sheet, animated: true, completion: nil)
case .pad:
let popOver = sheet.popoverPresentationController
popOver?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
popOver?.sourceView = sender
popOver?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
owner.present(sheet, animated: true, completion: nil)
case .unspecified:
owner.present(sheet, animated: true, completion: nil)
default:
print("")
}
}
@IBAction func actnReload(_ sender: Any) {
kolodaView?.revertAction()
// showUpgradeScreen()
}
@IBAction func actnRemove(_ sender: Any) {
DispatchQueue.main.async {
self.kolodaView?.swipe(.left)
}
}
@IBAction func actnLike(_ sender: Any) {
DispatchQueue.main.async {
self.kolodaView?.swipe(.right)
}
}
@IBAction func actnGallery(_ sender: Any) {
// DispatchQueue.main.async {
// self.kolodaView?.swipe(.right)
// }
let vc = storyboard?.instantiateViewController(withIdentifier: "AlbumVC") as! AlbumViewController
vc.albumDelegate = self
self.navigationController?.pushViewController(vc, animated: true)
//
// let options = PHFetchOptions()
// options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
// options.sortDescriptors = [NSSortDescriptor(key: "duration", ascending: true)]
//
//
//
// let pickerConfig = AssetsPickerConfig()
// pickerConfig.selectedAssets = ImageManager.shared.deleteAssetList
// pickerConfig.assetFetchOptions = [
// .smartAlbum: options,
// .album: options
// ]
//
// let picker = AssetsPickerViewController()
// picker.pickerConfig = pickerConfig
// picker.pickerDelegate = self
// present(picker, animated: true, completion: nil)
}
@IBAction func actnTrash(_ sender: Any) {
if ImageManager.shared.deleteAssetList.count == 0 {
return
}
mediaArray = ImageManager.shared.getDeleteMediaList()
let browser = MediaBrowser(delegate: self)
browser.displayActionButton = true
browser.displayMediaNavigationArrows = true
browser.displaySelectionButtons = true
browser.alwaysShowControls = true
browser.zoomPhotosToFill = true
browser.enableGrid = false
browser.startOnGrid = true
browser.enableSwipeToDismiss = true
browser.autoPlayOnAppear = false
selections.removeAll()
for index in 0..<mediaArray.count {
selections.append(true)
selectionMedia.append(mediaArray[index].asset!)
}
self.navigationController?.pushViewController(browser, animated: true)
}
@IBAction func actnSettings(_ sender: Any) {
let setting = self.storyboard?.instantiateViewController(withIdentifier: "SettingViewController") as! SettingViewController
self.navigationController?.pushViewController(setting, animated: true)
}
@objc func actnShare(){
let asset = self.allImages[kolodaView.currentCardIndex]
let image : UIImage = convertImageFromAsset(asset: asset)!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [image], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0)
// Anything you want to exclude
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.postToWeibo,
UIActivity.ActivityType.print,
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.addToReadingList,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo
]
self.present(activityViewController, animated: true, completion: nil)
}
@IBAction func actnOpenGallery(_ sender: Any) {
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
options.sortDescriptors = [NSSortDescriptor(key: "duration", ascending: true)]
let pickerConfig = AssetsPickerConfig()
pickerConfig.selectedAssets = ImageManager.shared.deleteAssetList
pickerConfig.assetFetchOptions = [
.smartAlbum: options,
.album: options
]
let picker = AssetsPickerViewController()
picker.pickerConfig = pickerConfig
picker.pickerDelegate = self
present(picker, animated: true, completion: nil)
}
}
// MARK: KolodaViewDelegate
extension HomeViewController: KolodaViewDelegate {
func kolodaDidRunOutOfCards(_ koloda: KolodaView) {
// let position = kolodaView.currentCardIndex
// for i in 1...4 {
// dataSource.append(UIImage(named: "Card_like_\(i)")!)
// }
// kolodaView.insertCardAtIndexRange(position..<position + 4, animated: true)
}
func koloda(_ koloda: KolodaView, didSelectCardAt index: Int) {
let photoModel = allImages[index]
let configuration = ImageViewerConfiguration { config in
config.image = convertImageFromAsset(asset: photoModel)
}
let imageViewerController = ImageViewerController(configuration: configuration)
self.present(imageViewerController, animated: true, completion: nil)
}
func koloda(_ koloda: KolodaView, didSwipeCardAt index: Int, in direction: SwipeResultDirection){
self.selectionMedia = ImageManager.shared.deleteAssetList
if self.imageCount != self.allImages.count{
self.imageCount += 1
self.imageCountLabel.text = "\(self.imageCount)/\(self.allImages.count)"
if self.allImages.count > index + 1 {
self.imageDateLabel.text = self.allImages[index + 1].creationDate?.string(withFormat: "MMM d, yyyy")
}
}
if direction == .left{
if tempDeleteCount > maximumCountToDelete && !AppManager.shared.isUpgraded {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.kolodaView?.revertAction()
}
showUpgradeScreen(assetVC: nil)
return
}
let photoModel = self.allImages[index]
self.imageCountLabel.text = "\(self.imageCount)/\(self.allImages.count)"
// AppManager.shared.userClick += 1
tempDeleteCount += 1
let filter = self.selectionMedia.filter({$0.localIdentifier == photoModel.localIdentifier})
if filter.count == 0{
ImageManager.shared.deleteAssetList.append(photoModel)
}
}else if direction == .right{
let photoModel = self.allImages[index]
if tempKeepCount > maximumCountToKeep && !AppManager.shared.isUpgraded {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.kolodaView?.revertAction()
}
showUpgradeScreen(assetVC: nil)
return
}
tempKeepCount += 1
let filter = self.selectionMedia.filter({$0.localIdentifier == photoModel.localIdentifier})
if filter.count > 0,let objectIndex = self.selectionMedia.firstIndex(of: filter[0]){
ImageManager.shared.deleteAssetList.remove(at: objectIndex)
tempDeleteCount -= 1
}
}
// else if direction == .topLeft || direction == .topRight{
//
// let photoModel = self.allImages[index]
//
// if AppManager.shared.favCount >= maximumCountToFav && !AppManager.shared.isUpgraded {
// DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
// self.kolodaView?.revertAction()
// }
// showUpgradeScreen(assetVC: nil)
// return
// }
// AppManager.shared.favCount += 1
// PHPhotoLibrary.shared().performChanges({
// let request = PHAssetChangeRequest(for: photoModel)
// request.isFavorite = true
// }) { (is_success, error) in
// print("Success")
// }
//
//
// }
}
func koloda(_ koloda: KolodaView, allowedDirectionsForIndex index: Int) -> [SwipeResultDirection]{
return [.right, .left]
}
func kolodaSwipeThresholdRatioMargin(_ koloda: KolodaView) -> CGFloat? {
return 0.5
}
}
// MARK: KolodaViewDataSource
extension HomeViewController: KolodaViewDataSource {
func kolodaNumberOfCards(_ koloda: KolodaView) -> Int {
return allImages.count
}
func kolodaSpeedThatCardShouldDrag(_ koloda: KolodaView) -> DragSpeed {
return .fast
}
func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
// let photoModel = allImagesModels[index]
// let imageView = UIImageView(image: photoModel.image)
// imageView.contentMode = .scaleAspectFill
// imageView.clipsToBounds = true
// return imageView
let shareBtn = UIButton(frame: CGRect(x: kolodaView.frame.width - 70, y: 10, width: 50, height: 50))
shareBtn.setBackgroundImage(UIImage(named: "Upload"), for: .normal)
shareBtn.addTarget(self, action: #selector(actnShare), for: .touchUpInside)
let photo = allImages[index]
let imageView = UIImageView(image: convertImageFromAsset(asset: photo) ?? UIImage())
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.isUserInteractionEnabled = true
imageView.addSubview(shareBtn)
return imageView
}
func koloda(_ koloda: KolodaView, viewForCardOverlayAt index: Int) -> OverlayView? {
return Bundle.main.loadNibNamed("OverlayView", owner: self, options: nil)?[0] as? OverlayView
}
}
extension HomeViewController: AssetsPickerViewControllerDelegate {
func assetsPickerCannotAccessPhotoLibrary(controller: AssetsPickerViewController) {
logw("Need permission to access photo library.")
}
func assetsPickerDidCancel(controller: AssetsPickerViewController) {
logi("Cancelled.")
}
func assetsPicker(controller: AssetsPickerViewController, selected assets: [PHAsset]) {
self.assets = assets
ImageManager.shared.deleteAssetList = assets
// self.assets.forEach { (photo) in
//
//
// let filter = selectionMedia.filter({$0.localIdentifier == photo.localIdentifier})
// if filter.count == 0{
// ImageManager.shared.deleteAssetList.append(photo)
// }
// }
// ImageManager.shared.deleteAssetList.append(contentsOf: self.assets)
}
func assetsPicker(controller: AssetsPickerViewController, shouldSelect asset: PHAsset, at indexPath: IndexPath) -> Bool {
logi("shouldSelect: \(indexPath.row)")
// can limit selection count
let totalCount = controller.selectedAssets.count + AppManager.shared.userClick
if totalCount >= maximumCountToDelete && !AppManager.shared.isUpgraded {
showUpgradeScreen(assetVC: controller)
return false
}
// if controller.selectedAssets.count > 3 {
// // do your job here
// }
tempDeleteCount += 1
return true
}
func assetsPicker(controller: AssetsPickerViewController, didSelect asset: PHAsset, at indexPath: IndexPath) {
logi("didSelect: \(indexPath.row)")
}
func assetsPicker(controller: AssetsPickerViewController, shouldDeselect asset: PHAsset, at indexPath: IndexPath) -> Bool {
logi("shouldDeselect: \(indexPath.row)")
tempDeleteCount -= 1
return true
}
func assetsPicker(controller: AssetsPickerViewController, didDeselect asset: PHAsset, at indexPath: IndexPath) {
logi("didDeselect: \(indexPath.row)")
}
func assetsPicker(controller: AssetsPickerViewController, didDismissByCancelling byCancel: Bool) {
logi("dismiss completed - byCancel: \(byCancel)")
}
}
//MARK: MediaBrowserDelegate
extension HomeViewController: MediaBrowserDelegate {
func thumbnail(for mediaBrowser: MediaBrowser, at index: Int) -> Media {
if index < mediaArray.count {
return mediaArray[index]
}
return Media.init(image: UIImage(named: "SplashScreen")!, caption: "No image selected")
}
func media(for mediaBrowser: MediaBrowser, at index: Int) -> Media {
if index < mediaArray.count {
return mediaArray[index]
}
return Media.init(image: UIImage(named: "SplashScreen")!, caption: "No image selected")
}
func numberOfMedia(in mediaBrowser: MediaBrowser) -> Int {
return mediaArray.count
}
func isMediaSelected(at index: Int, in mediaBrowser: MediaBrowser) -> Bool {
return selections[index]
}
func didDisplayMedia(at index: Int, in mediaBrowser: MediaBrowser) {
print("Did start viewing photo at index \(index)")
}
func mediaDid(selected: Bool, at index: Int, in mediaBrowser: MediaBrowser) {
selections[index] = selected
if selected {
let media = mediaArray[index]
let filter = selectionMedia.filter({$0.localIdentifier == media.asset?.localIdentifier})
if filter.count == 0{
selectionMedia.append(mediaArray[index].asset!)
}
}else{
let media = mediaArray[index]
if selectionMedia.contains(media.asset!), let indexItem = selectionMedia.firstIndex(of: media.asset!){
selectionMedia.remove(at: indexItem)
}
}
}
func actionButtonPressed(at photoIndex: Int, in mediaBrowser: MediaBrowser, sender: Any? = nil) {
print("delete")
// let assetList = ImageManager.assetFrom(mediaList: selectionMedia)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets(ImageManager.shared.deleteAssetList as NSFastEnumeration)
}) { (is_success, error) in
if is_success {
DispatchQueue.main.async {
ImageManager.shared.deleteAssetList.removeAll()
AppManager.shared.userClick += ImageManager.shared.deleteAssetList.count
mediaBrowser.navigationController?.popViewController(animated: true)
}
}
}
}
func gridCellSize() -> CGSize {
return CGSize(width: (self.view.frame.width/3 - 5), height: (self.view.frame.width/3 - 5))
}
}
extension HomeViewController: AlbumProtocol{
func selectedAlbum(photos: PHFetchResult<PHAsset>) {
self.imageCountLabel.text = "1/\(photos.count)"
self.kolodaView.currentCardIndex = 0
self.imageCount = 1
self.addImagetoArray(photos: photos)
}
}
Upvotes: 0
Views: 41