UIImageView not displaying new image - ios

I have an image cache class, which downloads icons if necessary and caches them. In my view controller, in viewDidAppear, I have:
// (showIcon is a UIImageView from the storyboard)
showIcon.image = [[LRImageCache imageCache] imageForURL:iconURL completionTarget:self action:#selector(updateImage:)];
If the image is already cached, it is returned here, and is correctly displayed. Otherwise, a placeholder image is returned, and the cache class attempts to download the image. When (if) successful, it calls the target/action:
- (void)updateImage:(UIImage *)image
{
showIcon.image = image;
}
Setting a breakpoint on that line confirms that 'image' is the correct, downloaded image - but it doesn't display!
I can't help but think I'm missing something really obvious...

You are calling the updateImage: which is expecting an argument, with your code you call the method but you don't provide the argument.
Consider creating an image object first like so:
UIImage *myImage = [[LRImageCache imageCache] imageForURL:iconURL completionTarget:self action:#selector(updateImage)];
Remove the argument from the updateImage method (remember to remove the colon from the selector when setting the image.
- (void)updateImage{
showIcon.image = myImage;
}

Related

Calling an ImageView variable inside an if statement

I'm using Swift in Xcode and that's my issue:
I want to declare a variable where I want to store an UIImageView (Not an UIImage, but an UIImageView), so I tried something like that
var nameVariable = UIImageView()
Then I need to recall this nameVariable inside an if statement
if(\(nameVariable).center.y > something){..}
I tried using "(nameVariable)" but it doesn't work, can you help me?
Declare Variable
var img=UIImageView()
Then use it
if img.center.y > 50 {
//do stuff here
}

Swift keep pointer/reference to object or another a pattern?

I am converting a project from another tool and language:
Suppose I have a
singleimagecache: UIImage;
I now pass this to a structure which does
var myimage: UIImage = singleimagecache;
Now, this struct is passed to a function that does some work.
This function determines another image should be cached. In my original code, it would simply use myimage and assign its content some other image-bitmap data. Since the object-reference itself was not changed (only its content) singleimagecache would still point to valid fresh new cache data.
However, this is not possible in Swift since UIImage requires to be reconstructed like this:
myimage = UIImage(...)
But doing that would leave singleimagecache with wrong data
So that leaves me the following options:
Any support in Swift for keeping references in sync?
Any support in Swift for keeping pointers to objects (that themselves possibly can be nillable)
Wrap UIImage inside another object that is persistant and use that.
There is no built-in Swift support for what you wish to do. I would just make a wrapper class with 2 UIImage properties. One would be myimage and the other would be singleimagecache. You could then use the didSet property observer on myimage to achieve the desired synchronization so that singleimagecache will always be up to date with myimage. didSet will be called everytime a new value is stored/set in the property.
class imageCache
{
var myimage:
= UIImage() {
didSet {
singleimagecache = myimage
}
}
var singleimagecache = UIImage()
}

Using a variable to change the name of an image displayed in an UIImageView

I'm a completely newbie to iOS and have been focussing on learning Swift. However I'm also dabbling in Objective-C.
I can't seem to find a way of changing the image displayed on my app dependent on a data value I have in a variable. I have text value "5" for example that i want to put between a text prefix and suffix so that I can use that as the file name to display in a UIImageView.
Ultimately I want to achieve "Prefix"+ variable + "suffix" into something I can pass to my image view.
So far I have this.
NSString *fileName = #"fhrs_";
fileName = [fileName stringByAppendingFormat:#"%#", self.ratingVal];
fileName = [fileName stringByAppendingFormat:#"_en-gb.jpg"];
when the code gets here "fileName holds the correct value for the file I want to display in the UIImageView.
vwImgRating.image = [UIImage imageNamed:#"%#", fileName];
However I get a message when trying to compile that " Too many arguments to method call, expected 1, have 2.
Help!
Replace the last line with this
vwImgRating.image = [UIImage imageNamed:fileName];
or
if you want to use format specifier than u can use
vwImgRating.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#",fileName]];
Image needs a String and you are giving the String in the wrong format.

What's the best way to assert on a UIImage in a unit test?

Say I'm writing a unit test for a tableView:cellForRowAtIndexPath: delegate method on a view controller. This method could return a couple of different configurations of cells depending on the index path I pass in.
I can easily assert on the cell.textLabel.text property. But how can I assert that the cell.imageView.image property contains the correct image? Neither the image or the imageView have (public API) properties I can use to find out the image name or file name.
The best I've come up with is creating the smallest possible valid .png (using [UIImage imageWithData:] so I don't touch the disk in my unit tests) and asserting the byte array I get from cell.imageView.image is the one I expect. I've created an OCHamcrest matcher to make this a little nicer but it's an unsatisfying and inflexible approach.
Has anyone got a better idea?
If you're using [UIImage imagedNamed:], the images are cached. Excerpt from the UIImage class reference for imagedNamed::
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
This means you can assert on cell.imageView.image == [UIImage imagedName:#"my_image"] as this is a pointer comparison and since the images are cached multiple calls to imageNamed: with the same name will return the same pointer. Even if memory gets tight, you should still be fine, as the UIImage class reference explains:
In low-memory situations, image data may be purged from a UIImage object to free up memory on the system. This purging behavior affects only the image data stored internally by the UIImage object and not the object itself.
Converting the images to Data and then comparing the Data. Since the image is just a pointer to memory location.
guard let data1 = image1?.pngData(), let data2 = image2.pngData() else {
XCTFail("Data should not be nil")
return
}
XCTAssertEqual(data1, data2)
swift5
You can compare the contents of UIImage directly using the isEqual method on a UIImage which will compare that the two images are like for like.
So in your tests, you can do:
let expectedImage = UIImage(named: "my_picture")
let returnedImage = SomeImageReturnedFromFunction()
XCTAssertEqualObjects(expectedImage, returnedImage) // will return true if SomeImageReturnedFromFunction returns my_picture
Reference: https://developer.apple.com/documentation/uikit/uiimage#overview
You can indeed compare with the "isEqual" method but rather like this :
let expectedImage = UIImage(named: "an_image")
let returnedImage = myFunctionRet() // optional ?
XCTAssert(returnedImage?.isEqual(expectedImage) == true)

instantiate class method issue while trying to resize and image

I am quite new to iOS development and its my first time trying to resize and image and dealing with class methods.
I am following this post on how to resize an image. I placed the + (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize; in selectExerciseImageViewController.h and copied the relevant code to selectExerciseImageViewController.m.
Then trying to instantiate the class using
selectExerciseImageViewController * newC = [[selectExerciseImageViewController alloc] init];
UIImage* newImage = [newC imageWithImage:[info objectForKey:#"UIImagePickerControllerOriginalImage"]
scaledToSizeWithSameAspectRatio:CGSizeMake(40.0,40.0)];
but its throwing an error saying that there is not visible interface for that method. i tried putting self instead of newC but still throws the same error.
I would appreciate some guidance on why its behaving like this.
Your
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize;
method is a class method and you are trying to invoke it on an instance of selectExerciseImageViewController. Try:
[selectExerciseImageViewController imageWithImage:[info objectForKey:#"UIImagePickerControllerOriginalImage"]
scaledToSizeWithSameAspectRatio:CGSizeMake(40.0,40.0)];
or you can declare your method as an instance method, just change the + sign (class method) to a - sign (instance method)
- (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize;

Resources