I'm trying to load an image from an iOS 8 framework that I'm writing (in Swift). I'm using Xcode 6 Beta 6
This code does not work (i.e. load image) if the image is stored in my framework's Images.xcassets:
let image = UIImage(named: "Background.png")
If the image is stored in an Images.xcassets of a host application (that uses the framework), then the image is loaded properly (from code inside the framework).
I can see that the framework's Images.xcassets is included in the Copy Bundle Resources phase.
I'm also using a storyboard file as a resource in the framework; and this loads properly.
I've tried renaming the Images.xcassets of the framework to avoid some kind of naming collision with the host application, but this doesn't work either.
While #Renatus answer is perfectly valid and addresses the core issue (bundle for framework needs to be specified), I wanted to post the solution I went with since it's slightly more direct:
Swift 3.0/4.0/5.0
let image = UIImage(named: "YourImage", in: Bundle(for: YOURFRAMEWORKCLASS.self), compatibleWith: nil)
Alternatively, you can use this pattern for non-class, aka non-"static", functions:
let image = UIImage(named: "YourImage", in: Bundle(for: type(of: self)), compatibleWith: nil)
or this pattern for class functions:
let image = UIImage(named: "YourImage", in: Bundle(for: self), compatibleWith: nil)
These alternatives are better for cutting and pasting.
UIImage(named: "Background.png") calls NSBundle.mainBundle() in the internals. So, your code is trying to find resource in your host app's bundle, not in the frameworks bundle. To load UIImage from your framework's bundle use this snippet:
let frameworkBundle = NSBundle(forClass: YOURFRAMEWORKCLASS.self)
let imagePath = frameworkBundle.pathForResource("yourImage.png", ofType: "")
if imagePath != nil {
result = UIImage(contentsOfFile: imagePath!)
}
Edited: added explanation (thx to milz)
In Swift 3.0:
let currentBundle = Bundle(for: YOURCLASS.self)
guard let path = currentBundle.path(forResource: imageName, ofType: "jpg") else { return defaultImage }
return UIImage(contentsOfFile: path) ?? defaultImage
Another option is assigning the bundle identifier, which makes more sense than assigning class when it comes to readability.
In Swift 3:
UIImage(named: "MyImage", in: Bundle(identifier: "bundleIdentifier"), compatibleWith: nil)
The accepted answer didn't work for me. Here's a fool proof way for loading an image embedded in a dynamic framework:
var bundle = NSBundle(forClass: self.classForCoder)
if let bundlePath = NSBundle(forClass: self.classForCoder).resourcePath?.stringByAppendingString("/MYLIB.bundle"), resourceBundle = NSBundle(path: bundlePath) {
bundle = resourceBundle
}
let image = UIImage(named: "my-embedded-image", inBundle: bundle, compatibleWithTraitCollection: nil)
Related
Whenever I try to use .addAttachmentURL, it does not attach anything. The ViewController is presented with nothing within the body of the text. The URL is a path to the pdf data (I don't know if that makes a difference) in my file defaults. Is there any way I can send a PDF through text like this? I have not found anything by looking through documentation or StackOverflow. Also, I haven't implemented it yet, but I was wondering if there was a way to also attach PNGs to this message I am sending along with the PDF.
func getFileManager() -> NSString {
let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString)
return filePath
}
func displayMessageInterface() {
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self
// Configure the fields of the interface.
composeVC.recipients = ["000000000"]
var url = URL(string: self.getFileManager() as String)!
url.appendPathComponent("my_report.pdf")
composeVC.addAttachmentURL(url, withAlternateFilename:
"this file")
// Present the view controller modally.
if MFMessageComposeViewController.canSendText() {
self.present(composeVC, animated: true, completion: nil)
} else {
print("Can't send messages.")
}
}
You are using the wrong URL initializer. URL(string:) initializer expects a scheme, in this case file://. You need to use URL(fileURLWithPath:) initializer or simply get the document directory URL using FileManager urls method:
extension URL {
static let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
let url = URL.documentDirectory.appendingPathComponent("my_report.pdf")
I am not sure what you mean when you say "The URL is a path to the pdf data in my file defaults". If you have included your file in your project Bundle you need to use its url(forResource:) method.
let url = Bundle.main.url(forResource: "my_report", withExtension: "pdf")!
I would like to get the subfolders structure in the Resources folder of a Swift Playground.
I've tried using this approach with no luck
let docsPath = Bundle.main.resourcePath! + "/Resources"
let directoryContents = try? fileManager.contentsOfDirectory(atPath: docsPath)
This results in nil and I can't get the tree structure of the folders, is this possible?
Solved it!
let docsPath = Bundle.main.resourcePath!
let urls = Bundle.main.urls(forResourcesWithExtension: nil, subdirectory: "dataset/train")
I would recommend to allow NSBundle to find your resources exactly where they are, so instead of
let docsPath = Bundle.main.resourcePath! + "/Resources"
you can use somewhere inside your class (moreover, such class will be transferable to real project)
let docsPath = Bundle(for: type(of: self)).path(forResource: "dataset", ofType: nil)
and you get exact path.
Btw, it can be something like
/var/folders/gs/hkhcv3rx5wd5lqrfvh1ll6g40000gn/T/com.apple.dt.Xcode.pg/resources/620432D5-9F12-4FC8-B271-17153FC9D797/dataset
that is why it's better to avoid hardcoding.
I'm updating my app with the new iOS 11 standards and a lot of stuff were deprecated and now I'm stacked with this error: "Argument labels '(contentsOfFile:)' do not match any available overloads.
Here you are the codes that was working:
//load plist file
var palermoip: NSArray?
if let path = Bundle.main.path(forResource: "palermoip", ofType: "plist") {
palermoip = NSArray(contentsOfFile: path)
}
Anyone knows how can I fix it? Thank you in advance !
I recommend to use PropertyListSerialization and the URL related API
let url = Bundle.main.url(forResource: "palermoip", withExtension: "plist")!
let data = try! Data(contentsOf:url)
let palermoip = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [[String:Any]] // or [Any] if the array does not contain dictionaries
and in Swift 4 even PropertyListDecoder
I am trying to load images that I have downloaded using Alamofire to the documents directory. I store the filenames in a Realm database. Once it is time to display the image I take the path to the documents directory and append the filename. This path doesn't seem to work for building a UIImage though.
if let name = playlist.RLMsongs[indexPath.row].name, let imageURL = playlist.RLMsongs[indexPath.row].album?.image?.getImageURL(.SmallImage), let fileName = playlist.RLMsongs[indexPath.row].album?.image?.smallLocalFileName {
cell.songName.text = name
let range = imageURL.rangeOfString("http")
if let theRange = range where theRange.startIndex == imageURL.startIndex { // imageURL starts with "http" (remote url)
cell.albumImage.sd_setImageWithURL(NSURL(string: imageURL), placeholderImage: UIColor.imageFromColor(UIColor.grayColor())) {
_ in
cell.albumImage.fadeIn(completion: nil)
}
} else {
cell.albumImage.image = UIImage(named: fileName) // images still do not load
// tried this first -> cell.albumImage.sd_setImageWithURL(NSURL(fileURLWithPath: imageURL), placeholderImage: UIColor.imageFromColor(UIColor.grayColor()))
}
}
Here is the bit that builds the path (imageURL above):
let documentsDir = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let path = documentsDir.URLByAppendingPathComponent(localFile).path
return path
As per TiM's recommendation I checked the value of imageURL on a breakpoint and it looks just fine:
imageURL: "/Users/danielnall/Library/Developer/CoreSimulator/Devices/0B8D64B5-9593-4F86-BBD3-E408682C5C0F/data/Containers/Data/Application/011E4805-40EB-4221-9D7D-1C1D64660186/Documents/75.9226ecd6893cb01a306c974d9d8ffd62803109c1.png"
This is the full path on the simulator and is only missing the file schema (file://) which for the use of NSURL fileURLWithPath should be just fine I think.
If you already have the PNG file in Documents then all you need to do is this.
(Assuming that cell.albumImage is UIImageView)
let imageFileName = "75.9226ecd6893cb01a306c974d9d8ffd62803109c1.png"
cell.albumImage.image = UIImage(named: imageFileName)
Check if file exists:
func fileExists(filePath: String) -> Bool {
let filemanager = NSFileManager.defaultManager()
if filemanager.fileExistsAtPath(filePath) {
return true
}
return false
}
I ended up finding the solution to the problem which was unrelated to the direction of this question. Turned out that the issue was in my override of the prepareForReuse method in my UITableViewCell class. Thank you for everyone's suggestions though.
I use the imagePickerController to choose an image from the camera roll. I would like to copy that image to an asset catalog like Images.xcassets. How can you do that?
Image xcassets are nothing more than a collection of images coupled with a specific file/directory organisation, and a JSON catalog, so there is nothing difficult in principle in creating them:
create an appropriate directory structure (I will refer to splash images, as they are the one I have more experience about):
myimages.xcassets/
myimages.xcassets/LaunchImage.launchimage
put your images in it
create an appropriate JSON catalog (spoiler: NSJSONSerialization) and again, my example is about splash images:
"images": [
{
"scale": "2x",
"orientation": "portrait",
"minimum-system-version": "7.0",
"filename": "launch_seed_ipad_portrait.png",
"idiom": "ipad",
"extent": "full-screen"
},
[... etc ...]
}
and that's it. Point is, as bundles, image catalogs are more a facility provided to ease image organisation from the Xcode project (for instance, I created them in a context of project automation), and not something meant to be compiled within the app, so your question looks a bit like asking:
how can I create a xib from code
how can I put together a bundle from an app
etc
all operations that can be carried on, if with some considerable effort, but are unlikely to do much good (either because the app cannot use the result, or there are more convenient alternatives).
Hope this helps
SWIFT:
No the app assets, but you could save the image in "Document":
let dirPath = dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let imagePath = (dirPath as NSString).stringByAppendingPathComponent("IMAGE.EXT")
let fileManager = NSFileManager.defaultManager()
do
{
try fileManager.createDirectoryAtPath(imagePath, withIntermediateDirectories: true, attributes: nil)
}
catch _
{
}
if let image: UIImage = UIImage(data: DATA)//OR YOUR SOURCE
{
var imageData: NSData! = UIImagePNGRepresentation(image)
if imageData == nil
{
imageData = UIImageJPEGRepresentation(image, 100)
}
if imageData == nil
{
//DO SOMETHING..
}
imageData!.writeToFile(imagePath, atomically: true)
}
And then load with:
if fileManager.fileExistsAtPath(imagePath)
{
return UIImage(contentsOfFile: imagePath)
}
That works for me, hope it helps!