Testing that a particular image was copied to pasteboard - ios

I'm writing a test to verify a feature which copies an image to the pasteboard.
Here's the test, as I would prefer to write it:
// reset the paste board
UIPasteboard.generalPasteboard.image = nil; //<-- this explodes
XCTAssertNil(UIPasteboard.generalPasteboard.image);
// Grab some random existing image
UIImage *image = [UIImage imageNamed:#"some-image"];
MJKMyClass *myInstance = [[myInstance alloc] initWithImage:image];
[myInstance doSomethingThatCopiesImageToPasteboard]
XCTAssertNotNil(UIPasteboard.generalPasteboard.image);
This fails with:
failed: caught "NSInvalidArgumentException", "-[UIPasteboard setImage:]: Argument is not an object of type UIImage [(null)]"
Which is surprising, because, according to the UIPasteboard header, image is a nullable field.
#interface UIPasteboard(UIPasteboardDataExtensions)
<...>
#property(nullable,nonatomic,copy) UIImage *image __TVOS_PROHIBITED;
<...>
#end
I presume this means they are doing a runtime check on the argument, even though it is nullable.
Things I have tried:
comparing objects by id doesn't work because UIImage's are copied by the generalPastboard.image (every time you call UIPasteboard.generalPasteboard.image you can a different instance)
comparing by PNG representation might work, but seems gnarly.
comparing by image size has been my closest bet so far, but also seems roundabout.

You can clear the pasteboard without having to pass nil by using the items property:
UIPasteboard.generalPasteboard.items = #[];
Or in Swift:
UIPasteboard.generalPasteboard().items = []
For comparing UIImages, you might want to look at some other questions:
How to compare two UIImage objects
https://stackoverflow.com/search?q=uiimage+compare

Related

Objective-C: Uncast element of NSMutableArray returns unexpected type

If am having trouble understanding the following. Consider the following code:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:#"car.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:imageView];
UIImage *image2 = [imageView image];
UIImage *image3 = [[array objectAtIndex:0] image];
UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];
The instructions with image2 and image4 work as expected. The line with image3 on the other hand displays an issue,
Incompatible pointer types initializing 'UIImage *' with an expression
of type 'CIImage *'
I understand not type-casting the object retrieved from NSMutableArray might cause a problem. However, I have a hard time understanding why the compiler would think this expression to be of type CIImage.
If this question has been asked in more general terms, I apologize, I couldn't find it...
Let's pull this apart:
UIImage *image3 = [[array objectAtIndex:0] image];
Just look at this part:
[array objectAtIndex:0]
Objective-C has no type information for the object returned from this method call, so it types it as an id. Now you are asking to send the image message to an id. But an id is not a typed object. What is Objective-C to make of this?
Basically, it has to guess what message you are sending. So it looks thru its known repertory of image methods, and just picks one. It knows of six such methods:
You meant the fourth one. But it happens that the compiler picks the first one, which returns a CIImage. So by your standards, it guessed "wrong". But you made it guess, and it had to guess something.
This is exactly the problem that the cast in your last example fixes:
UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];
So, the moral: don't do what you did in the image3 line. Do what you did in the image4 line! You have type information that the compiler doesn't have: tell the compiler what you know.
(Observe that no harm is actually done by the way you wrote the code. The compiler warning is just that: a warning. In actual fact, when the image message is sent to the UIImageView, a UIImage will be returned and all will be well. But the compiler is warning you that it doesn't know this. By not casting you have given up all static typing and forced the compiler to take its hands off. The compiler doesn't know what will happen, so it warns you that this will have to be resolved at runtime. When you cast, you resolve the matter at compile time.)

SpriteKit SKTextureAtlas atlasWithDictionary method

I'm not sure if I'm doing this wrong or if there's something broken with the "atlasWithDictionary" method.
This is how I used it:
NSArray* imageNames = #[ #"image1", #"image2", #"image3", #"image4"];
NSMutableDictionary* tempDict = [NSMutableDictionary dictionary];
for (int i = 0; i < imageNames.count; i++) {
UIImage* texture = [UIImage imageNamed:imageNames[i]];
[tempDict setObject:texture forKey:imageNames[i]];
}
SKTextureAtlas* atlasFullOfTextures = [SKTextureAtlas atlasWithDictionary:tempDict];
and then later in my code, whenever I would do
SKTexture* tex = [atlasFullOfTextures textureNamed:#"image1"];
I just get a nil object. I did a bit of troubleshooting and found that
NSArray* arrayOfNames = [atlasFullOfTextures textureNames];
returns an empty array.
Also, I know that the images are loading fine as I temporarily made the dictionary public and successfully made SKTextures from the UIImage objects.
Does anyone have any idea what's happening?
From the documentation:
The keys in the dictionary represent the names of the individual
textures. The associated object for each key can be:
An NSString object that contains a file system path to a file that contains the texture
An NSURL object that contains a file system path to a file that contains the texture
A UIImage object
An NSImage object
You are not providing a path/url to the image, you just use image names with no way of telling where they might be. You will probably also have to specify the file extension as Sprite Kit probably won't try to guess it. If these are textures obtained or created at runtime, they will not be in the bundle so you have to specify what the path to each texture is as well (usually in the appdata or documents folder).
If these images are in the bundle, you probably have to specify the bundle directory. But there's little reason not to have the atlas be created by Xcode at compile time in this case and then using atlasNamed:.

Need to release a CGImage contained in a CALayer's contents property

I have a CALayer object called sublayer. I use it as self.sublayer throughout my view controller because I have made it a property in my view controller's header file.
I set the sublayer's contents property equal to a UIImage object that is created using a CGImageRef object called imageRef:
self.subLayer.contents = (id)[UIImage imageWithCGImage:imageRef].CGImage;
I then release the imageRef object right away now that it has been used to create the sublayer contents and it is no longer needed:
CGImageRelease(imageRef);
However, here is what is bothering me. Later on in the code I will no longer need self.sublayer.contents and I want to make sure I release the CGImage it contains properly.
If I NSLog self.sublayer.contents it will print this to the console: <CGImage 0x146537c0>
So I need to be able to release this CGImage as well.
I tried using this to release the CGImage, but the NSLog still prints the same to the console:
CGImageRelease((__bridge CGImageRef)(self.subLayer.contents));
If I use this, the NSLog will print to the console as (null), but I am worried that this is technically not releasing the CGImage:
self.subLayer.contents = nil;
Does setting the sublayer's contents property to nil properly release the CGImage, or am I correct in thinking that it is not technically releasing the CGImage?
I am experiencing memory problems right now in my app so I need to make sure that I am releasing this CGImage properly.
The contents property on CALayer is a retaining property, meaning that it's setter implementation more or less does this:
- (void)setContents:(id)contents
{
if (contents == _contents) return; // Same as existing value
[_contents release];
_contents = [contents retain];
}
So, when you set nil as the new contents, the old contents is released.

Error: CUICatalog: Invalid asset name supplied: (null), or invalid scale factor : 2.000000

TableViewApplication[1458:70b] CUICatalog: Invalid asset name supplied: (null), or invalid scale factor: 2.000000
Getting this warning while working with TableViewController. How to rectify this error and which block is affected?
This one appears when someone is trying to put nil in [UIImage imageNamed:]
Add symbolic breakpoint for [UIImage imageNamed:]
Add $arg3 == nil condition on Simulator, $r0 == nil condition on 32-bit iPhone, or $x2 == nil on 64-bit iPhone.
Run your application and see where debugger will stop.
P.S. Keep in mind this also happens if image name is empty string. You can check this by adding [(NSString*)$x2 length] == 0 to the condition.
This error (usually) happens when you try to load an image with [UIImage imageNamed:myImage] but iOS is not sure if myImage is really a NSString and then you have this warning.
You can fix this using:
[UIImage imageNamed:[NSString stringWithFormat:#"%#", myImage]]
Or you can simply check for the length of the name of the UIImage:
if (myImage && [myImage length]) {
[UIImage imageNamed:myImage];
}
Since the error is complaining that the name you gave is (null), this is most likely caused by calling [UIImage imageNamed:nil]. Or more specifically, passing in a variable hasn't been set, so it's equal to nil. While using stringWithFormat: would get rid of the error, I think there's a good chance it's not actually doing what you want. If the name you supply is a nil value, then using stringWithFormat: would result in it looking for an image that is literally named "(null)", as if you were calling [UIImage imageNamed:#"(null)"].
Something like this is probably a better option:
if (name) {
UIImage *image = [UIImage imageNamed:name];
} else {
// Do something else
}
You might want to set a breakpoint in Xcode on that "Do something else" line, to help you figure out why this code is getting called with a nil value in the first place.
In Xcode 6.4, this seems to occur when using "Selected Image" for a tab bar item in the storyboard, even if it's a valid image.
This doesn't actually seem to set the selected state image anyway, so it needs to be defined in User Defined Runtime Attributes, and removed from the SelectedImage attribute of the Tab Bar Item
In my case i was passing [UIImage imageNamed:#""] which caused me to show the warning. Just add breakpoints to all the lines where you have used imageNamed and the debug the line where you find the warning.
This happened to me after a storyboard was split into several (the actual change happened when I was on holidays, so I don't know exactly how it was done).
After inspecting the XML of the storyboards, I found that an image reference which previously had pointed to "bottomBar" in the assets catalogue instead pointed to imageView:fFo-1g-jzs:image.
At the end of the XML file under the <resources> tag was tag named <image name="imageView:fFo-1g-jzs:image"> containing a big mutableData blob.
After resetting the image reference in the storyboard and removing the blob, the error went away.
You are supplying some invalid image name, so need to validate before putting it you can do any of the above ways whichever make sense for your code or like
if (iconImageName && [iconImageName length])
{
[UIImage imageNamed:iconImageName];
}
else
{
iconImageName.hidden = YES;
}
Hope it will help!!!!
I have just fixed this error. Just check the usage of the [UIImage imageNamed:(NSString*) imageName] function. If the imageName is nil, then error occurs.
The Reason to the error is passing a nil value to the "imageNamed:" method. To avoid this, you can hide your imageView when you try to pass the nil value. The chances may occur in reusing the imageViews in UITableView or may be in scrollViews.
I avoided the warning with the below check :
UIImageView *your_image_view;
NSString *imageName;
if(imageName && imageName.length){
your_image_view.hidden = NO;
your_image_view.image = [UIImage imageNamed:imageName];
}
else {
your_image_view.hidden = YES;
}
I got this warning when I load image use [[UIImage imageNamed:normalStr] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal], than, I change to [UIImage imageNamed:normalStr], warning was gone.
One approach is do class method swizzling to replace [UIImage imageNamed:] with your own implementation, then check the image name in your implementation. See the following:
How to swizzle a class method on iOS?
I implemented this in my UIImage(Debug) category:
+ (UIImage *)db_imageNamed:(NSString *)imageName {
if ([imageName length] == 0) {
NSLog(#"breakpoint here");
}
return [self db_imageNamed:imageName]; // not a recursive call here after swizzling
}
You might want to swizzle [UIImage imageNamed:inBundle:compatibleWithTraitCollection:] as well.
In my case I have implemented cell with UILabel, UIImageView and UITextFied, sometimes I don't want to display image, in that case I'm sending empty string "" to the imageView. Now the same error/warning is getting.
Error: [framework] CUICatalog: Invalid asset name supplied: ''
Solution is we have to pass nil instead of "" empty string.
Code:
if imgName.count > 0 {
selectedIconView.image = UIImage(named: imgName)
} else {
selectedIconView.image = nil
//You can also use
//selectedIconView.isHidden = true
}
Maybe you can use this to confirm the "imageName" is not a "nil";Then you will leave away this warning.
if (imageName) {
self.imageView.image = [UIImage imageNamed:imageName];
}

Change Back And Forth UIImages

Hi I’m trying to get an image to change on a method call and if the method is recalled the original image comes back
-(void)change
{
if ((player.image = [UIImage imageNamed:#"first.png"]))
{
player.image = [UIImage imageNamed:#"second.png"]));
}
else
{
player.image = [UIImage imageNamed:#"first.png"]));
}
}
This works change the "first.png" image to "second.png" but when called again it doesn’t.
Where am I going wrong?
As pointed out by Michael Dautermann, the way you're comparing two UIImages is wrong. Can't you simply keep an NSString property that tells you the image you're showing?
-(void)change
{
if ([self.displayedImageTitle isEqualToString:#"first.png"]){
player.image = [UIImage imageNamed:#"second.png"]));
self.displayedImageTitle = #"second.png";
}
else{
player.image = [UIImage imageNamed:#"first.png"]));
self.displayedImageTitle = #"first.png";
}
}
In your code above, you're making an invalid assumption that "[UIImage imageNamed: #"first.png"]" will always be the same UIImage object when you do a comparison. That isn't true. Every time you call the "imageNamed" API, you may be creating a brand new UIImage object.
Now, since UIImage object doesn't know anything about file names or image titles, you need to keep track of which image is being displayed and then toggle on that.
I'd suggest creating a subclassed UIImage object that has a filename or title property (that you would also need to set), or have your two UIImages as properties or ivars loaded into memory (that is, if these aren't giant memory hogging images) and then do your toggling.

Resources