Is there a way to get the file size on disk of a PHAsset without doing requestImageDataForAsset or converting it to ALAsset? I am loading in previews of a user's photo library - potentially many thousands of them - and it's imperative to my app that they know the size before import.
You can grab the fileSize of a PHAsset and convert it to human readable form like this:
let resources = PHAssetResource.assetResources(for: yourAsset) // your PHAsset
var sizeOnDisk: Int64? = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
}
Then use your sizeOnDisk variable and pass it into a method like this...
func converByteToHumanReadable(_ bytes:Int64) -> String {
let formatter:ByteCountFormatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: Int64(bytes))
}
A safer solution:
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSNumber *fileSize = nil;
NSError *error = nil;
[contentEditingInput.fullSizeImageURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:&error];
NSLog(#"file size: %#\nerror: %#", fileSize, error);
}];
Swift version:
asset.requestContentEditingInput(with: nil) { (contentEditingInput, _) in
do {
let fileSize = try contentEditingInput?.fullSizeImageURL?.resourceValues(forKeys: [URLResourceKey.fileSizeKey]).fileSize
print("file size: \(String(describing: fileSize))")
} catch let error {
fatalError("error: \(error)")
}
}
Inspired by How to get an ALAsset URL from a PHAsset?
Please try this.
let resources = PHAssetResource.assetResources(for: YourAsset)
var sizeOnDisk: Int64 = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
totalSize.text = String(format: "%.2f", Double(sizeOnDisk) / (1024.0*1024.0))+" MB"
}
SWIFT 5.0
Light & Easy:
private static let bcf = ByteCountFormatter()
func getSize(asset: PHAsset) -> String {
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return "Unknown"
}
let sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64))
Self.bcf.allowedUnits = [.useMB]
Self.bcf.countStyle = .file
return Self.bcf.string(fromByteCount: sizeOnDisk)
}
Related
this is the code how I save images in CoreData:
lazy var dataImage = profileIcon.jpegData(compressionQuality: 1.0)
//MARK: - Saving the Workout Image
func savePUValues() {
// Kontext Identifiziern
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "PushUps" //Tabellenname im DatenModell
//Neuen Datensatz anlegen:
guard let newEntity = NSEntityDescription.entity(forEntityName: entityName, in: context)
else {
return
}
let newSet = NSManagedObject(entity: newEntity, insertInto: context)
let savingImage = dataImage
newSet.setValue(savingImage, forKey: "image")
// Datensatz speichern
do {
try context.save()
} catch {
print("An error appeared")
}
}
Now I want to fetch the image data and load it into a UITableView:
With this code:
func loadValues() -> [CellData] {
//Kontext identifizieren
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//Anfrage stellen
let context = appDelegate.persistentContainer.viewContext
let entityName = "PushUps"
//MARK: - Request
// Use a specific fetch request
let request = NSFetchRequest<PushUps>(entityName: entityName)
// add a sort descriptor to sort the items by highScore descending
request.sortDescriptors = [NSSortDescriptor(key: "count", ascending: false)]
do {
// results is an array of PushUps instances, no type cast needed
let results = try context.fetch(request)
for r in results {
if let result = r as? NSManagedObject {
let titletext = "\(count!) Push-Ups"
var cellTitleLabel = UILabel()
cellTitleLabel.text = titletext
let workoutImage = result.value(forKey: "image") as? UIImageView
print("Image was loaded into the array")
var Data1 = CellData(imageData: workoutImage ?? oImageView, titleData: cellTitleLabel)
print("Image: \(String(describing: workoutImage)), TitLe: \(cellTitleLabel) ")
cellDataArray.append(Data1)
}
}
} catch {
print(error)
}
return cellDataArray
}
The problem is that the it's loading the image as nil.
The console is giving me this back: "Image: nil, TitLe: <UILabel: 0x115f17980; frame = (0 0; 0 0); text = '0.0 Push-Ups'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28249cfa0>> "
So where is the problem? What is wrong with my Code?
Thanks for your help!
The problem is that you are saving Data and trying to retrieve it as a UIImageView, which is doomed to failure:
lazy var dataImage = profileIcon.jpegData(compressionQuality: 1.0)
// Data
let savingImage = dataImage
newSet.setValue(savingImage, forKey: "image")
// still Data
// ---------------------
let workoutImage = result.value(forKey: "image") as? UIImageView
// UIImageView?????
However, don't save image data into Core Data in the first place. Save to disk and store the partial path (e.g. the file name).
this is my respone
{
activeFromDate = "01/01/2017";
terminateDate = “02/05/2019”;
}
{
activeFromDate = "01/01/2013";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2018";
}
{
activeFromDate = "07/01/2012";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2019";
}
this is my code
let answerArray = NSMutableArray()
for i in 0..<arr.count
{
let dict = Dictionary[i] as! NSDictionary
let name1 = dict.value(forKey:"terminateDate") as! String
if(name1 == "")
{
print (answerArray.add(dict))
}
}
this is my nsdictionary response
how to get array count if terminatedDate is null and terminatedDate is greater than today date?
I have created a sample to read json file placed in your project. and have integrated the code as per your requirement.
Json File
{"Status":"success","Data":[
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"},
{"activeFromDate":"01/01/2013","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2018"},
{"activeFromDate":"07/01/2012","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2016"},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"}
]
}
In your View Controller
import UIKit
class HomeViewController: UIViewController {
let answerArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
jsonParsingFromFile()
}
func jsonParsingFromFile()
{
let path: NSString = Bundle.main.path(forResource: "tjw", ofType: "json")! as NSString
let data : NSData = try! NSData(contentsOfFile: path as String, options: NSData.ReadingOptions.dataReadingMapped)
self.startParsing(data: data)
}
func startParsing(data :NSData)
{
let dict: NSDictionary!=(try! JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary
//print(dict)
guard let dataArr:NSArray = dict.value(forKey: "Data") as? NSArray else {
return
}
print(dataArr.count)
for i in 0..<dataArr.count{
guard let currentDict:NSDictionary = dataArr[i] as? NSDictionary else {
return
}
let activeFromDateStr:String = currentDict.value(forKey: "activeFromDate") as? String ?? ""
let terminateDateStr:String = currentDict.value(forKey: "terminateDate") as? String ?? ""
print(activeFromDateStr, terminateDateStr)
if terminateDateStr != ""{
let date2 = convertToDate(dateStr: terminateDateStr)
let today = Date()
if date2>today{
answerArray.add(currentDict)
}
}else{
answerArray.add(currentDict)
}
}
print(answerArray)
}
func convertToDate(dateStr:String)->Date{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
dateFormatter.locale = Locale(identifier: "en_IN")
let date = dateFormatter.date(from: dateStr)!
return date
}
}
I have the below code that worked fine, up until the release of swift 3.1.
func loadImage() {
id = userPhotoModel.id
let fileManager = FileManager.default
let imagePath = (self.getDirectoryPath() as NSString).appendingPathComponent(photoName)
if fileManager.fileExists(atPath: imagePath){
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
print("name of photo retrieved: \(photoName)")
self.userPhoto.image = imageFromPath
}else{
print("No Image")
}
}
Now, swift 3.1 wants to add as! String to:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath) as! String)
However, when I run the app, it crashes in this location with no error message as per the below image.
What is causing this?
EDIT: Here is the resizeImage func
fileprivate func resizeImage(named name: String) -> UIImage
{
var image = UIImage(named: name)
if image!.size.height > image!.size.width
{
self.userPhoto.contentMode = .scaleAspectFit
}
else
{
image = image!.resizeTo(self.userPhoto.bounds)
}
return image!
}
The issue is the confusing syntax in the line:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
That should simply be:
let imageFromPath = resizeImage(named: imagePath)
No cast required and correct in any Swift 3.x.
I have code like below. For some file I got metadata like artist, title and other without any problem. For other files metadata list is nil but when I check metadata in editor like Tagger - title and other metadata exists. Furthermore when I change metadata in external editor for at least one key - my code starts work properly.
Could someone explain me where I make mistake ?
static func getBookInCatalog(url: URL) -> Book {
let book = Book(url: url)
let isDir: ObjCBool = false
var directoryContents = [URL]()
var totalTime: CMTime?
var size: UInt64 = 0
var chapters:Int = 0
do {
directoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
} catch let error as NSError {
print(error.localizedDescription)
return book
}
for item in directoryContents {
if !isDir.boolValue {
let result = appDelegate.fileTypes.filter { $0==item.pathExtension.lowercased() }
if !result.isEmpty {
chapters += 1
let fileSize = (try! FileManager.default.attributesOfItem(atPath: item.path)[FileAttributeKey.size] as! NSNumber).uint64Value
size += fileSize
let playerItem = AVPlayerItem(url: item)
let metadataList = playerItem.asset.commonMetadata
let asset = AVURLAsset(url: item, options: nil)
let audioDuration = asset.duration
if let _ = totalTime {
totalTime = totalTime! + audioDuration
} else {
totalTime = audioDuration
}
for metadata in metadataList {
guard let key = metadata.commonKey, let value = metadata.value else{
continue
}
switch key {
case "albumName":
if book.title == nil || book.title == "" {
book.title = (value as? String)!
}
case "artist":
if book.author == nil || book.author == "" {
book.author = (value as? String)!
}
case "artwork" where value is NSData:
if book.image == nil {
book.image = UIImage(data: (value as! NSData) as Data)
}
default:
continue
}
}
}
}
}
if let imageInsideCatalog = getImageFromFolder(url: url){
book.image = imageInsideCatalog
}
if book.title == nil {
book.title = url.deletingPathExtension().lastPathComponent
}
book.chapters = chapters
book.totalTime = totalTime
book.size = size
return book
}
MP3 meta data "standards" have gone through several major iterations over the years (see http://id3.org) . Your editor may be able to read older formats (that AVURLAsset may not support) and save them using the latest/current standard which would make them compatible after any change.
I have a program, where the user "creates" an image, and then the program takes a screenshot of the screen. I would then like to save this screenshot to a database, prefferebly nsuserdefaults, since I am accessing it later in a table view. Any other suggestions on how to approach this, are more than welcome :)
The code is like this
let screenshot = getScreenshot() // saves the screenshot
var imagePaths = [String]()
// get the array of previous screenshots
if let _ = NSUserDefaults.standardUserDefaults().objectForKey(theKey)
{
imagePaths = NSUserDefaults.standardDefaults().objectForKey(theKey) as! [String]
}
// then I want to get a path to the image something like
let imagePath = screenshot.getPath() // although this is not a valid method, this is basically what I want
// add the imagePath
imagePaths.append(imagePath)
// finally I save the image
NSUserDefaults.standardUserDefaults().setObject(imagePaths, forKey: theKey)
You can create directory in Documents and save there screenshots as usual files. Filename can be generated from date and time for uniqueness.
func saveImage(imageData: NSData)
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy hh.mm.ss"
let filename = "\(dateFormatter.stringFromDate(NSDate())).png"
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
let filePath = imagesDirectory.stringByAppendingPathComponent(filename)
if !NSFileManager.defaultManager().fileExistsAtPath(imagesDirectory)
{
var error: NSError?
NSFileManager.defaultManager().createDirectoryAtPath(imagesDirectory, withIntermediateDirectories: false, attributes: nil, error: &error)
if error != nil
{
println("\(error!.localizedDescription)")
return
}
}
imageData.writeToFile(filePath, atomically: true)
}
func getImagesPaths() -> [String]?
{
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
if let filenames = NSFileManager.defaultManager().contentsOfDirectoryAtPath(imagesDirectory, error: nil)
{
let imagePaths = filenames.map{"\(imagesDirectory)/\($0)"}.filter(){$0.pathExtension == "png"}
return imagePaths.count > 0 ? imagePaths : nil
}
return nil
}
To save image simply use saveImage(data). To get images paths use getImagesPaths().
If you need array of UIImage, you can get it by follow way:
var images : [UIImage] = [ ]
if let imagePaths = getImagesPaths()
{
for path in imagePaths
{
if let image = UIImage(contentsOfFile: path)
{
images.append(image)
}
}
}