Programmatically save an image to xcassets in Swift [duplicate] - image

This question already has an answer here:
Save image in asset catalog on runtime
(1 answer)
Closed 7 years ago.
Say my app downloads an image, is it possible to save this to Images.xcassets programmatically, so that the image doesn't have to be downloaded again?
Or would the best option be to keep retrieving it from the server?

You can't save it to the images.assets. However you can save it to the document directory buy doing this. (Code from here - How to convert code objective c to Swift to save image?)
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
if let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let readPath = dirPath.stringByAppendingPathComponent("Image.png")
let image = UIImage(named: readPath)
let writePath = dirPath.stringByAppendingPathComponent("Image2.png")
UIImagePNGRepresentation(image).writeToFile(writePath, atomically: true)
}
}
}

You can't save to Images.xcassets, however you can save it to a file and access it with imageWithContentsOfFile:.
You could also use a caching library, like JGAFImageCache or Haneke.

you can't write to the bundle at all you need to save the data to you documents/caches folder inside your sandbox

Related

File access permission in Swift on OSX

I'm using:
let dialog = NSOpenPanel()
to get a file URL.
I'm then reading in the contents of the text file with:
let content = try String( contentsOf: dialog.url)
This works!
I'm then trying to read in another text file in the same directory with a different extension:
let b = dialog.url?.deletingPathExtension()
// Add the new file extension
let c = b?.appendingPathExtension("TSN")
let content2 = try String( contentsOf: c)
With this I get:
"The file “FLO5.TSN” couldn’t be opened because you don’t have permission to view it."
If I try and open the .tsn file with a URL from a NSOpenPanel() dialog result it works. I need to open several data files from this same directory with different extensions. Is there a way to do this?
set off sandbox!!)
Xcode 9 and later enables sandboxing by default, which severely limits interactions with the system.
Select your target, then Capabilities and set Downloads Folder to Read/Write:
In my case, solve it's startAccessingSecurityScopedResource
Example:
let selectedFile = try result.get() // get path to file
do {
// get access to open file
if selectedFile.startAccessingSecurityScopedResource() {
let path = selectedFile.path
let data = NSData(contentsOfFile: path)
print(data) // array bytes
selectedFile.stopAccessingSecurityScopedResource()
}
} catch {
// Couldn't read the file.
print(error.localizedDescription)
}
Apple wiki about this feature https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso

Folder of images: create and fill SKShapeNodes, 1 of each

Is it possible to create a folder and have SpriteKit go through that folder, find each image (regardless of name), and create a SKShapeNode for each in that folder (regardless of how many there are)?
Assume all images are the same size, and all .png files ready to be used as unique images as textures for filling SKShapeNodes, and that all SKShapeNodes will be the same size.
I want to know, once done, how many shapes have been created, and have them uniquely named based on the name of the image of the texture used.
But all I'm reading seems to be about knowing how many images are there, and what their names are, before hand.
Clueless as to where to start searching about how to do this.
Update: no idea how to reference the "folder" with images:
I've dumped the images, with their very imaginative names [one, two, three, four, five etc. ], into a folder as "atlas", like this:
but as per Allessandro's wonderful answer, I have no idea how to address this "folder" and get at what's in it.
I think it's this line I'm getting completely wrong:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
I don't know what to replace "Images" with.
Update 2:
Found a much easier way to do this:
let myTextureAtlas = SKTextureAtlas(named: "demoArt")
print(myTextureAtlas)
print(myTextureAtlas.textureNames)
Too many requests , so I try to help you with some code:
Create a folder:
func createDir(folderName:String) {
let fileManager = FileManager.default
if let directory = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "APP_GROUP_IDENTIFIER") {
let newDirectory = directory.appendingPathComponent(folderName)
try! fileManager.createDirectory(at: newDirectory, withIntermediateDirectories: false, attributes: nil)
}
}
Now you have your folder so you can decide to save a file or to copy one file from documents to this new directory. I make an example for the last one.
Copy a file:
let fileManager = FileManager.default
do {
try fileManager.copyItem(atPath: "hero.png", toPath: "subfolder/hero.swift")
}
catch let error as NSError {
print("Something went wrong: \(error)")
}
Now you want to know how to extract all files from a directory.
Extract all files from a directory:
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = FileManager.default
if let enumerator = fileManager.enumerator(atPath: path) {
for file in enumerator {
if #available(iOS 9.0, *) {
if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path
, path.hasSuffix(".\(fileExtension)"){
allFiles.append(path)
}
} else {
// Fallback on earlier versions
}
}
}
return allFiles
}
Usage:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
let allPngFiles = extractAllFile(atPath: folderPath!, withExtension: "png") // returns file path of all the png files inside the folder
After that, you can create an array of SKShapeNode based from allPngFiles using fillTexture with a SKTexture created from each image and give to each node the name of the file used (so you know at the end also how many nodes you have created from array.count).
Slightly different answer here. I'm not sure why one needs to create a directory and copy files over. In your screenshot you have a demoArt.atlas with 7 files (one.png, two.png, etc). You indicate you want to generate an SKShapeNode. It still isn't clear to me why you want SKShapeNode, but ignoring that, you just need to know the folder name. You'll notice how the folder is blue, which means it is a reference. This means that in the image, the folder is retained.
This would get you all the files in that directory:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let texPath = NSURL(fileURLWithPath: documentsPath).appendingPathComponent("demoArt.atlas")
let filemanager:FileManager = FileManager()
// Error check/ensure path exists
let files = filemanager.enumerator(atPath:texPath!.path)
while let file = files?.nextObject() {
print(file)
// Create your SKShapeNode here, you can load file from this
// This is all relative path, so "file" would be one.png, two.png, etc. If you need the full path, you
// need to prepend texPath
}
Note the key diff here is not copying files over. Maybe I just don't understand the problem and why it would require the copy.
If you wanted to put that in an array you can. But here you'll have the file name of the texture which you would of course need to load.
Realistically, you want to load those textures asynchronously first, then construct your SKShapeNode.

Photo changing every day(Xcode, Swift)

I haven't write code for this But I want some help that can it be possible to code so that it shows new photo everyday. for example I have 10 images and I want to display each image for like September 16,17,18, and so on ?
Here you go...
Call method getImageForToday() where ever you want to fetch image for today (This solution will work specific to your case, i.e. as you mentioned you have 10 images)
func getImageForToday() -> UIImage
{
let arrImages = ["image1.png", "image2.png" ... "image10.png"]
var imageName = arrImages[getLastCharacterFromTodayDate()]
return UIImage(named: imageName)
}
func getLastCharacterFromTodayDate() -> Int
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let component = calendar.components(NSCalendarUnit.CalendarUnitDay, fromDate: NSDate())
return component.day % 10
}
You can use the object NSDate to know what day is it and after that doing something.
First you have your dictionnary of image like that :
let images = [ UIImage(named: "image1.png")!, UIImage(named: "image2.png")!, UIImage(named: "image3.png")]
so now, you must know what day is it for the first app openning in exemple.
So you can do that :
let date = NSDate()
You put the element date in UserDefaults. (in order to save it)
You can now display a image. You make a random number and you take UIImage at this random position.
The day after, or the next app opening you recheck the NSDate with the NSDate saved and if it's different you take a other photo.

Cocoa: take screenshot of desktop wallpaper (without icons and windows)

Is is possible to capture the Mac OS X desktop without desktop items and any windows that may be open (i.e. just the wallpaper)?
I've experimented with CGWindowListCreateImage, CGWindowListCreateImageFromArray, and CGDisplayCreateImage, but no luck.
Essentially I'm trying to capture the desktop wallpaper without using [NSWorkspace desktopImageURLForScreen:] (it's a sandboxed app without access to the file system).
You'll need to be careful to test that this is still correct, but the desktop window sits below the Finder (it's drawn by the Dock). Passing the kCGWindowListOptionOnScreenBelowWindow CGWindowListOption to CGWindowListCreateImage should get you what you want (unless something else is drawing below that level).
Otherwise, you'll need to use CGWindowListCreate and iterate through the response excluding anything that isn't drawn by the dock at the window level kCGMinimumWindowLevel + 19.
It gets kind of tricky when there are multiple screens, but hopefully this information is enough for you to do what you need.
I know this is a super old question, and Tony Arnold's question is right, and what I used to build my own "grab the desktop" code.
I have some example code that shows how to do all these things (it's a wonderful thing walking in parts of Cocoa that are barely documented... )
I've put that sample code up in a bitbucket repository. Specifically the code sample to take a picture. (There's more interesting Cocoa code in my learning Cocoa repository, where that sample code is from )
Swift version:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}

Getting Desktop Background in Cocoa

I'm needing to do something full-screen app, which would usually not be the problem. The problem now is that I need to have the user's desktop, but without icons, as the background of my full screen window, much like Launchpad in 10.7. I've gotten a reference to the desktop background in AppleScript:
tell application "Finder"
set a to desktop picture
end tell
This gives me something like this: document file "100930-F-7910D-001.jpg" of folder "Pictures" of folder "Fighter Jet Stuff" of folder "Desktop" of folder "tristan" of folder "Users" of startup disk of application "Finder" which I just could not figure out to get into a regular path.
I tried doing set a to desktop picture as POSIX path but that throws up on me. Any idea of how I could do this in Cocoa, using the above Applescript to get the path, or even better, without an Applescript? I'd like to not rely on the specific format of any plist that might store this info, as it has the potential to break later on. I'm thinking there might be a framework that I just don't know about...
The methods you are looking for are available in NSWorkspace.
– desktopImageURLForScreen:
– setDesktopImageURL:forScreen:options:error:
– desktopImageOptionsForScreen:
Please take a look at the documentation here: NSWorkspace Class Reference
If you needs just the current wallpaper, you can take a screenshot of it:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}

Resources