In Xcode 12.4 on macOS Big Sur, I'm developing an Objective-C plug-in of a third-party app running on macOS 10.12 and newer. I would like to load an image from an asset catalog.
I've added abc.xcassets into the project tree, added several PDF images. One of the image is named test.
I would like to show the test image as an alert icon. For this purpose, I'm trying to load the image into an NSImage instance in the main plug-in's class. I've tried all the alternatives I found but I still get nil instead of the image.
+ (void)initialize {
NSImage *test1 = [NSImage imageNamed:#"test"];
NSImage *test2 = [NSImage imageNamed:#"test" bundleForClass:[self class]];
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSImage *test3 = [[NSImage alloc] initWithContentsOfFile:
[testBundle pathForImageResource:#"test"]];
// Later used in
// [alert setIcon:test1];
}
However, all the test1, test2 and test3 are nil after running this code. What am I missing? How to do it properly?
PS: The test image is listed by Xcode's UI builder to select in buttons' Image property.
This worked:
[testBundle imageForResource:#"test"]
Related
I have a very weird issue. I have a watch app and I am trying to display an image programatically. When I debug the code, I can see the image is correctly loaded but on simulator or real device I cannot see the image. I have added that image in both watch and extension bundle resources.
Firstly I use the code below to open the view.
NSArray *objects =[NSArray arrayWithObjects:#"MainPageView",#"InterfaceController",#"PoseImageView",nil];
[self presentControllerWithNames:objects contexts:nil];
Inside PoseImageView I am trying to display an image with:
UIImage *imgTest = [UIImage imageNamed:[NSString stringWithFormat:#"1.jpg", strAngle]]; // I can see 1.jpg in debugger
[_imgPose setImage: imgTest];
The image is not there on watch or simulator. I am wondering how to fix the issue.
Note: From assets its not loading as well.
Note2: I can only display the image if the view is initial view.
You are trying to use imageNamed for .jpg which will not work.
Try using
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"imageName"] ofType:#"jpg"];
UIImage *theImage = [UIImage imageWithContentsOfFile:filePath];
As weird as it sounds...
App loaded through test flight or iTunes does not show image on the screen, but it does when it is loaded through Xcode. I believe the UIImageView is there, and the UIImage is loaded.
App loaded through the Xcode works.
If the device is connected to the Xcode and debugging, device connected with USB cable. App works despite the load method...
Core data seems to works in all cases.
Any idea, somebody has seen something similar. I am quite lost.
Also if you have any tip on how to debug this on the device I will appreciate it.
The few code I think it is involve.
I am loading the UIImage from disk using
UIImage * previewImage = [UIImage imageWithContentsOfFile:path];
The image is located in Documents Directory fileName is the input
NSURL *documentsFolderURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSString *documentsFolderPath = [documentsFolderURL path];
if (![_documentsDirectory isEqualToString:documentsFolderPath]) [self generateLocaFolderPath];
NSString *returnedPath = [_documentsDirectory stringByAppendingPathComponent:fileName];
return returnedPath;
and setting the image in the imageView with
previewView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frameImageThumbnail.size.width, frameImageThumbnail.size.height)];
[previewView setImage:previewImage];
Everything else seems to work well...
My first question!!! Thanks for reading!
_ edit _
I had the strong believe now that the problem is not in the UIImage, I have use the same methods in other parts of the app and it is working fine.
But now I believe the problem is in the animation I perform to the UIView that contains the UIImageView.
[_view setFrame:frameImageThumbnail];
[_view setClipsToBounds:YES];
[_view setAutoresizesSubviews:YES];
[_image setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[_view addSubview:_image];
[UIView animateWithDuration:0.4
delay:0.1
options:UIViewAnimationOptionCurveEaseInOut
animations:^(void){
CGRect destination1Frame = _view.frame;
//... some complicated calculations with the rect ...
[_view setFrame:destination1Frame];
[UIView transitionFromView:_customView toView:_view duration:0 options: UIViewAnimationOptionShowHideTransitionViews completion:^(BOOL finisehdCompletion){
//stop showing video in _customView
}];
}];
I am using NSAutolayout in the storyboard. Though my application only allows one landscape orientation. No idea if all this meshing up with the frames it is making.
dizzy to the poor auto layout guy...
Make sure you have / your image is set in Copy Bundle Resources. You can check if your image path is valid also. But I think the image is not set to copy at Build Phases.
I had the same problem.. Haven't find a correct solution.
But I deleted the image(check if it's deleted in ur Bundle resources), then put the image again, with ANOTHER name.
It worked for me. It's a temporary solution, idk if it's an apple bug or what.
Ok After more than 20 ipa files build and installed in my devices. In all this process to get rid of the problem I removed the _view from the storyboard, and initiallize the view programmatically. I remember that _view was originally set to (weak, nonatomic) property because was made with the storyboard, this super nice ctrl+.
That was I change I made, weak to Strong, and works now.
I don't know why it didn't work only when the program was install through iTunes with the ipa file or through the test flight app. That is still a mystery to me.
I am trying to make a custom control that updates live in Interface Builder using the new IB_DESIGNABLE option described here.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect myFrame = self.bounds;
CGContextSetLineWidth(context, 10);
CGRectInset(myFrame, 5,5);
[[UIColor redColor] set];
UIRectFrame(myFrame);
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath;
plistPath = [bundle pathForResource:#"fileExample" ofType:#"plist"];
UIImage *tsliderOff = [UIImage imageNamed:#"btn_slider_off.png"];
[tsliderOff drawInRect:self.bounds];
}
When I run in the simulator, I get a red box with my image in the center (as expected):
But when I try use the Interface Builder, it only shows up as a red box (no image in the middle):
When I debug it: Editor->Debug Selected Views, it reveals that anything loaded from the bundle is nil. The plistPath and tsliderOff both appear as nil.
I made sure that btn_slider_off.png was included in the Targets->myFrameWork->Build Phases->Copy Bundle Resources.
Any idea why Interface Builder doesn't see the png file during editing, but shows ok when running? "Creating a Custom View that Renders in Interface Builder" is a little limited if I can't load any images to render...
edit based on solution by rickster
rickster pointed me to the solution - the problem is that the files don't live in mainBundle, they live in [NSBundle bundleForClass:[self class]]; And it appears that [UIImage imageNamed:#"btn_slider_off.png"] automatically uses mainBundle.
The following code works!
#if !TARGET_INTERFACE_BUILDER
NSBundle *bundle = [NSBundle mainBundle];
#else
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
#endif
NSString *fileName = [bundle pathForResource:#"btn_slider_off" ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:fileName];
[image drawInRect:self.bounds];
As of when this question was first asked, creating an IB-designable control required packaging it in a framework target. You don't have to do that anymore — the shipping Xcode 6.0 (and later) will preview IB-designable controls from your app target, too. However, the problem and the solution are the same.
Why? [NSBundle mainBundle] returns the primary bundle of the currently running app.
When you call that from a framework, you're getting a different bundle returned based on which app is loading your framework. When you run your app, your app loads the framework. When you use the control in IB, a special Xcode helper app loads the framework. Even if your IB-designable control is in your app target, Xcode is creating a special helper app to run the control inside of IB.
The solution? Call +[NSBundle bundleForClass:] instead (or NSBundle(forClass:) in Swift). This gets you the bundle containing the executable code for whichever class you specify. (You can use [self class]/self.dynamicType there, but beware the result will change for subclasses defined in different bundles.)
If you're using the framework approach — which can be useful for some apps even though it's no longer required for IB-designable controls — it's best to put image resources in the same framework with the code that uses them. If your framework code expects to use resources provided at run time by whatever app loads the framework, the best thing to do for making it IB-designable is to fake it. Implement the prepareForInterfaceBuilder method in your control and have it load resources from a known place (like the framework bundle or a static path in your Xcode workspace).
I ran into a similar problem in Swift. As of Xcode 6 beta 3, you don't need to use a framework to get live rendering. However, you still have to deal with the bundle problem for Live View so that Xcode knows where to find the assets. Assuming "btn_slider_off" is an image set in Images.xcassets then you can do this in Swift for live rendering and it will work when the app is run normally as well.
let name = "btn_slider_off"
let myBundle = NSBundle(forClass: self.dynamicType)
// if you want to specify the class name you can do that instead
// assuming the class is named CustomView the code would be
// let myBundle = NSBundle(forClass: CustomView.self)
let image = UIImage(named: name, inBundle: myBundle, compatibleWithTraitCollection: self.traitCollection)
if let image = image {
image.drawInRect(self.bounds)
}
Warning about the above bundle/path resolution while in IB_DESIGNABLE:
XCode will not resolve a resource if the call contained in the initialization of the UIView. I could only get XCode to resolve a path while in drawRect:
I found a great / easy workaround to the above bundle resolution techniques, just simply designate a IBInspectable property to be a UIImage, then specify the image in Interface Builder. Ex:
#IBInspectable var mainImage: UIImage?
#IBInspectable var sideImage: UIImage?
I've fixed it using the following line of code:
let image = UIImage(named: "image_name", inBundle: NSBundle(forClass: self.dynamicType), compatibleWithTraitCollection: nil)
After implementing it this way the images show up correctly in the Interface Builder
IN SWIFT 3.0 the code of #rickster has changed to:
// DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS
let theBundle : Bundle = Bundle(for: type(of: self))
// OR ALTERNATEVLY BY PROVDING THE CONCRETE NAME OF DESIGNABLE CLASS
let theBundle : Bundle = Bundle(for: RH_DesignableView.self)
// AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE
let theImage : UIImage? = UIImage(named: "Logo", in: theBundle, compatibleWith: nil)
I also had the same problem : looking at this answer and following WWDC 2014 whats new in Interface Builder.
I solved it like that:
- (void)prepareForInterfaceBuilder{
NSArray *array = [[NSProcessInfo processInfo].environment[#"IB_PROJECT_SOURCE_DIRECTORIES"] componentsSeparatedByString:#":"];
if (array.count > 0) {
NSString *str = array[0];
NSString *newStr = [str stringByAppendingPathComponent:#"/MyImage.jpg"];
image = [UIImage imageWithContentsOfFile:newStr];
}
}
This problem has completely stumped me. This is for iOS 5.0 with Xcode 4.2
What's going on is that in my app I let user select images from their photo album and I save those images to apps document directory. Pretty straight forward.
What I do then is that in one of the viewController.m files I create multiple UIImageViews and I then set the image for the image view from one of the picture that user selected from apps dir. The problem is that after a certain number of UIImage sets I receive a "Received memory warning". It usually happens when there are 10 pictures. If lets say user selected 11 pictures then the app crashes with Error (GBC). NOTE: each of these images are at least 2.5 MB a piece.
After hours of testing I finally narrowed down the problem to this line of code
[button1AImgVw setImage:image];
If I comment out that code. All compiles fine and no memory errors happen. But if I don't comment out that code I receive memory errors and eventually a crash. Also note it does process the whole CreateViews IBAction but still crashes at the end. I cannot do release or dealloc since I am running this on iOS 5.0 with Xcode 4.2
Here is the code that I used. Can anyone tell me what did I do wrong?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self CreateViews];
}
-(IBAction) CreateViews
{
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask ,YES);
documentsPath = [paths objectAtIndex:0];
//here 15 is for testing purposes
for (int i = 0; i < 15; i++)
{
//Lets not get bogged down here. The problem is not here
UIImageView *button1AImgVw = [[UIImageView alloc] initWithFrame:CGRectMake(10*i, 10, 10, 10)];
[self.view addSubview:button1AImgVw];
NSMutableString *picStr1a = [[NSMutableString alloc] init];
NSString *dataFile1a = [[NSString alloc] init];
picStr1a = [NSMutableString stringWithFormat:#"%d.jpg", i];
dataFile1a = [documentsPath stringByAppendingPathComponent:picStr1a];
NSData *potraitImgData1a =[[NSData alloc] initWithContentsOfFile:dataFile1a];
UIImage *image = [[UIImage alloc] initWithData:potraitImgData1a];
// This is causing my app to crash if I load more than 10 images!
// [button1AImgVw setImage:image];
//If I change this code to a static image. That works too without any memory problem.
button1AImgVw.image = [UIImage imageNamed:#"mark-yes.png"]; // this image is less than 100KB
}
NSLog(#"It went to END!");
}
This is the error I get when 10 images are selected. App does launch and work
2012-10-07 17:12:51.483 ABC-APP[7548:707] It went to END!
2012-10-07 17:12:51.483 ABC-APP [7531:707] Received memory warning.
App crashes with this error when there are 11 images
2012-10-07 17:30:26.339 ABC-APP[7548:707] It went to END!
(gbc)
This situation (memory warnings and application quitting while attempting to load multiple full resolution UIImages into a view) has attempted to burn me a couple times in my iOS programming career.
You need to make a shrinked down copy of your original image before doing the "setImage" call.
For my own code, I use the "UIImage+Resize" category, the details for which can be found here.
Resize your image to something smaller before inserting into your view, then make sure the full resolution image is released (or set to nil if on ARC) and you should have a happier time of things.
Here is how I do it in my own code:
CGSize buttonSize = CGSizeMake(width, height);
// it'd be nice if UIImage took a file URL, huh?
UIImage * newImage = [[UIImage alloc] initWithContentsOfFile: pathToImage];
if(newImage)
{
// this "resizedimage" image is what you want to pass to setImage
UIImage * resizedImage = [newImage resizedImage: buttonSize interpolationQuality: kCGInterpolationLow];
}
So, I'm currently saving images in my UIImageView via this method:
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSString *image1 = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/image1.png"];
NSString *image2 = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/image2.png"];
}
The images save just fine, but I have no idea how to set the UIImageViews images in viewDidLoad. This is what I've been trying:
- (void)viewDidLoad
{
self.imageView.image =[UIImage imageNamed:[(NSHomeDirectory *)Documents objectForKey:#"image1"]];
self.imageView2.image =[UIImage imageNamed:[(NSHomeDirectory *)Documents objectForKey:#"image1"]];
}
But, obviously that is not working. I'm having trouble understanding the basics here. Any help would be great, thanks!
The -imageNamed: method looks in the application's main bundle if the named image hasn't been cached yet. From the docs:
The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the application’s main bundle.
(See UIImage class reference)
You want -imageWithContentsOfFile: instead.