In Xcode 5.0.2
I create a blank single view app for iPhone,
then add a "male.png" image to the project,
drag a UIImageView to the storyboard
and finally add the following code to the viewDidLoad:
_imageView.image = [UIImage imageNamed:#"male.png"];
This works well:
Then I add the 4 files from JMImageCache project and change the ViewController.m to:
#import "ViewController.h"
#import "JMImageCache.h"
static NSString* const kAvatar = #"http://gravatar.com/avatar/55b3816622d935e50098bb44c17663bc.png";
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[_imageView setImageWithURL:[NSURL URLWithString:kAvatar]
placeholder:[UIImage imageNamed:#"male.png"]];
}
#end
Unfortunately, this results in app crash with the error message Thread 1: EXC_BAD_ACCESS:
At his webpage Jake Marsh (the author of JMImageCache) notes:
JMImageCache purposefully uses NSString objects instead of NSURL's to make things easier and cut down on [NSURL URLWithString:#"..."] bits everywhere. Just something to notice in case you see any strange EXC_BAD_ACCESS exceptions, make sure you're passing in NSString's and not NSURL's.
But (as an iOS programming newbie) I don't understand, what exactly does Mr. Marsh mean - since his file UIImageView+JMImageCache.m declares the 1st argument for the public method as NSURL:
- (void) setImageWithURL:(NSURL *)url placeholder:(UIImage *)placeholderImage {
[self setImageWithURL:url key:nil placeholder:placeholderImage];
}
Is the note maybe outdated and how could I fix my app?
That's a bug in JMImageCache. setImageWithURL:key:placeholder:completionBlock: calls itself, exhausting the stack.
To work around the bug, call the longer form of the method:
[_imageView setImageWithURL:[NSURL URLWithString:kAvatar]
key:nil
placeholder:[UIImage imageNamed:#"male.png"]
completionBlock:nil
failureBlock:nil];
Or, use an older version of the library (e.g. 0.4.0). Looks like the bug was introduced in 1af09be78a.
Related
I'm using following category UIImageView+AFNetworking.h from AFNetworking in my app, its working fine, its caching photos for me and loads images smoothly.
At one point, I want to get an image which is already there in my cache.
So I dig up into above category class where I found following code, which I think – can be helpful.
Here's the snippet from it:
#implementation AFImageCache
- (UIImage *)cachedImageForRequest:(NSURLRequest *)request {
switch ([request cachePolicy]) {
case NSURLRequestReloadIgnoringCacheData:
case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
return nil;
default:
break;
}
return [self objectForKey:AFImageCacheKeyFromURLRequest(request)];
}
- (void)cacheImage:(UIImage *)image
forRequest:(NSURLRequest *)request
{
if (image && request) {
[self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)];
}
}
#end
If you want me to add web version of this, it's already here.
I think, - (UIImage *)cachedImageForRequest:(NSURLRequest *)request is the method which can return me my cached image.
But I'm not sure, how can I use it?
If I know Objective-C a bit, it's a kinda a "protocol"?
What I have tried so far is to use it like a protocol in one of my view controller. Like this:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController<AFImageCache>
#end
But then it is giving me following warnings:
I'm not sure how to resolve those warnings and get the image from cache? Or any other more appropriate way?
I assume you import UIImageView+AFNetworking.h header.
If you want to access to the cached image. You should have a NSURLRequest object. Then it is like this:
UIImage *image = [[UIImageView sharedImageCache] cachedImageForRequest:request];
Done!
So this one has me stumped - probably something simple, but I'm clueless.
I'm defining a custom class, containing one method that receives one message (an integer). When calling that method, the compiler refuses to recognize the message I'm trying to send along with the call. ("No known class method for selector 'sendMessage:'. Removing the message from both the call and the definition - i.e. removing the :(int)mode from the definition, and the :1 from the call - allows it to compile fine (but then of course I lose the functionality).
I've tried defining it as an instance method, and as a class method - neither one works.
Many thanks in advance for your collective wisdom!
custom class "Communications.h":
#interface Communications : NSString
+(NSString*)sendMessage:(int)mode;
#end
Communications.m:
#import "Communications.h"
#interface Communications ()
#end
#implementation Communications
+(NSString*)sendMessage:(int)mode {
// Do something important
}
ViewController.h:
#import "Communications.h"
- (void) tapPanic:(UITapGestureRecognizer*)sender;
ViewController.m:
- (void) tapPanic:(UITapGestureRecognizer *)sender {
[Animations animatePanic:self.view type:0];
panicactive = 1;
NSString* tmpResponse = [Communications sendMessage:1];
UILabel* tmpServerResponsePanic = [self.view viewWithTag:10002];
tmpServerResponsePanic.text = tmpResponse;
[[self serverResponsePanic] setNeedsDisplay];
}
So, chalk it up to weirdness with Xcode... copy / pasting the contents of Communications .h and .m into new files, with a new class definition (Comms), did the trick. I think the compiler got confused and was remembering an old definition of the method.
I'm going to have objects, read from a .csv (Comma seperated file) which contains file names, later. Thats why i'm trying to do it like this.
What I would like to accomplish is to have the button get the image from the filename in the csv.
What i've tried so far:
The Object:
#interface question : NSObject
{
#private
UIImage *imageName;
}
#property (nonatomic, retain)UIImage *imageName;
(I have tried with NSString aswell as UIImage, neither works like I want them)
ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
question *question1 = [[question alloc]init];
[question1.imageName initWithContentsOfFile:#"Default.png"];//Will be read from a csv later on, just typing it in manually for a test
testImage = question1.imageName;
}
-(void)imageshow
{
//(UIImage *)testImage;(NSString *)imageNameString; Didnt work
//[self.testImage initWithContentsOfFile:#"%#",questiont.imageName]; - Doesnt work either
[testButton setImage:testImage forState:UIControlStateNormal];
}
-(IBAction)test:(id)sender
{
[self imageshow];
}
Where this code fails is when I press the test button, nothing happens (not crash)
How can I do this properly?
(Add an image with a text filename to a NSObject and then show it in a button/image view)
question1.img = [UIImage imageNamed:#"Default.png"];
[testButton setImage:question1.img forState:UIControlStateNormal];
and yes you can try the following for dynamic images
question1.img = [UIImage imageNamed:[NSString stringWithFormat:"%#",imgName]];
imgName => is the name of the image not the image
example: "abc.png" or "123.png" ,etc
if your property is named imageName the accessor variable is automaticly named _imageName.
You are using two different variables in your code.
#interface question : NSObject
{
#private
UIImage *_imageName;
}
#property (nonatomic, retain)UIImage *imageName;
imageName is not a great name for an property that contains an image.. it would better fit to a string.
There are some things to change.
For mere conventional reasons write all class names with an upper case at the beginning. It eases reading, exchange in forums as SO and avoids mistakes.
Second name variables either speaking or abstract, preferrably speaking. But to not choose a mislieading name such imageName. There should be a name in that variable, not an image, or maybe the graphic that displays a name. :)
imageWithContentsOfFile expects a fully qualified file name. If "Default.png" is part of your boundle, as I guess by its name, then imageNamed:#"Default.png" would do.
Your setImage looks nearly good. It cannot work of course while your image is (null). However, it is not good business practis accessing the iVar direct at this point. You shoudl use its setter/getter. Use instead:
[self.testButton setImage:self.testImage forState:UIControlStateNormal];
In your case it does not make much of a difference. But similar to the other conventions it helps avoiding errors when ever you mix your code with some code that you found on the web or when you work in a team.
I'm using SDWebImage for image caching at UICollectionView in my iOS app.
Everything is fine, however, when user scrolls fast over the collection view there is always little pause before placeholder is replaced with the cached image. I believe this is due to cache checks. For better user experience I would like cells to show the proper image instead of placeholder once an image is actually cached. This would be easy if I could get local (device) filesystem's path of the cached image and store it at the Show instance, and use it (if exists) instead of my placeholder.
There is the possibility to pass success block to the setImageWithURL method However, the only argument it gets is UIImage instance (actually in master branch there is also BOOL variable called cached being passed too). So - is there a possibility to get image's filesystem path straight from the UIImage instance? Or should I modify SDWebImage so it pass that information along to cached instance? Or is there any better way achieve a goal I described earlier?
My Show class has imageURL and here is how show cell use SDWebImage:
#import "ShowCell.h"
#import <SDWebImage/UIImageView+WebCache.h>
#implementation ShowCell
#synthesize imageView = _imageView;
#synthesize label = _label;
#synthesize show = _show;
- (void)setShow:(Show *)show
{
if (_show != show) {
self.label.text = show.title;
if (show.imageURL) {
[self.imageView setImageWithURL:[NSURL URLWithString:show.imageURL]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
_show = show;
}
}
#end
There are private SDImageCache methods to get filesystem path to cached image. We can make these methods public with a category. Just put the code below into the, say, SDImageCache+Private.h and add it to your project:
#import "SDImageCache.h"
#interface SDImageCache (PrivateMethods)
- (NSString *)defaultCachePathForKey:(NSString *)key;
- (NSString *)cachedFileNameForKey:(NSString *)key;
#end
I had a similar problem, see: SDWebImage showing placeholder for images in cache
Basically I forked the project to resolve this.
Update:
You can just do this to avoid the flickering (this works with the master branch of the project):
[imageView setImageWithURL:url placeholderImage:imageView.image options:0
progress:^(NSUInteger receivedSize, long long expectedSize) {
imageView.image = [UIImage imageNamed:#"QuestionMarkFace.png"];
} completed:nil];
I'm writing an iPhone app, and I'm surprised that there seem to be no NSQueue or NSStack classes in Apple's Foundation Framework. I see that it would be quite easy to roll my own, starting with an NSMutableArray, so I'll do that unless I've missed something. Have I missed something?
Here's my Stack class, in case it's useful to those who come after me. As you can see, the pop method involves enough code that you'd want to factor it out.
Stack.h:
#import <Foundation/Foundation.h>
#interface Stack : NSObject {
NSMutableArray *contents;
}
- (void)push:(id)object;
- (id)pop;
#end
Stack.m
#import "Stack.h"
#implementation Stack
// superclass overrides
- (id)init {
if (self = [super init]) {
contents = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[contents release];
[super dealloc];
}
// Stack methods
- (void)push:(id)object {
[contents addObject:object];
}
- (id)pop {
id returnObject = [[contents lastObject] retain];
if (returnObject) {
[contents removeLastObject];
}
return [returnObject autorelease];
}
#end
as far as I know there is no generic class avaialbe. Try using the NSMutableArray, add via addObject and get first/last via objectAtIndex and removeObjectAtIndex.
Another easy way would be to extend NSMutableArray's capabilities by making use of Objective C's categories. You can do that by adding two files to your project:
NSMutableArray+Stack.h
#interface NSMutableArray (StackExtension)
- (void)push:(id)object;
- (id)pop;
#end
NSMutableArray+Stack.m
#import "NSMutableArray+Stack.h"
#implementation NSMutableArray (StackExtension)
- (void)push:(id)object {
[self addObject:object];
}
- (id)pop {
id lastObject = [self lastObject];
[self removeLastObject];
return lastObject;
}
#end
Now you can use a regular NSMutableArray in every other file of your project like a stack and call push or pop on that object. Don't forget to #import NSMutableArray+Stack.h in those files. Here is some sample code how you can use your new NSMutableArray as a stack:
NSMutableArray *myStack = [[NSMutableArray alloc] init]; // stack size = 0
NSString *aString = #"hello world";
[myStack push:myString]; // stack size = 1
NSString *anotherString = #"hello universe";
[myStack push:anotherString]; // stack size = 2
NSString *topMostStackObject;
topMostStackObject = [myStack pop]; // stack size = 1
NSLog("%#",topMostStackObject);
topMostStackObject = [myStack pop]; // stack size = 0
NSLog("%#",topMostStackObject);
The log output will be:
hello universe
hello world
I'm a bit late to this party, but are you aware of CHDataStructures?
http://cocoaheads.byu.edu/code/CHDataStructures
I have put a working iOS Objective C queue object on GitHub. The code was taken from various posts and by no means is owned by me.
https://github.com/esromneb/ios-queue-object/
If you see any problems please fork, and make a pull request!
Yes, an NSMutableArray doubles as a stack or queue. (It would be slightly inefficient as a queue.)
You could also use C++'s stack and queue adapter, but it makes memory management a bit messy if you want to store Objective-C objects with it.
ObjectiveSugar is a very popular CocoaPod that provides, among a bunch of other great stuff, push and pop API calls on NSMutableArray. Sure, it's not in the iOS SDK, but I'm sharing it here because I was looking for the same thing, and this was the solution I went with (and it certainly didn't hurt that we were already using this CocoaPod in our codebase).
No. You missed nothing. That's all. Objective-C is higher level language look like C. Low level control is not required.
Cocoa classes are designed for easier use than efficiency. If you want to deal with performance, you have an option of raw C (or C++) implementation. Otherwise, just use easy way. Of course, early-optimization is evil.
If you want a kind of encapsulation, just make a new class which contains NSMutableArray within it. Hide inner NSMutableArray and just expose what you want. But you'll realize this is unnecessary.