I set an image using an url like this:
[self.imageView setMyImageWithURL:[NSURL URLWithString:[trip.destination getHeaderImageUrl] ]];
- (void)setMYImageWithURL:(NSURL *)url {
[self setBackground];
if(url.absoluteString.length >0){
__block UIImageView *safeSelf = self;
UIImage *placeholderImage = [UIImage imageNamed:[NSString stringWithFormat:#"placeholderpic_%#.png", NSLocalizedString(#"LanguageCode" , #"")]];
[self setImageWithURLRequest:[NSURLRequest requestWithURL:url]
placeholderImage:placeholderImage
success:nil
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
safeSelf.image = [UIImage imageNamed:[NSString stringWithFormat:#"placeholderpic_%#_missing.png", NSLocalizedString(#"LanguageCode" , #"")]];
}
];
}else{
self.image = [self missingImage];
}
}
Now, is there a way to check the imageView url after it has been set. Like, NSLog(#"%#", self.imageView.usingURL). I would like to check if it's the same as another url using a conditional. Something like this:
if(self.imageView.usingURL == [NSURL URLWithString:[trip.destination getHeaderImageUrl]])
Use this category to add property in UIImageView
#interface UIImageView (URLImageView)
#property NSURL* usingURL;
#end
#implementation UIImageView (URLImageView)]
#end
Now when image downloading and set to image view assign url to image view like
self.imageView.usingURL = yourURL
Now you can access this url to compare.
Related
UIImageView corner radius not working inside cellForRowAtIndexPath method
and and i am using is prototype cell.
I also tried layoutIfNeeded method unfortunately doesn't work.
Here is my code:
[cell layoutIfNeeded];
client_image=[cell viewWithTag:3];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
// client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
// [client_image layoutIfNeeded];
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
This works fine when I set it manually like:
client_image.layer.cornerRadius = 50;
But it's not working when i upgrade Xcode 7 to 8 with below code:
client_image.layer.cornerRadius = client_image.frame.size.height/2;
Any idea how why i am getting this wired issue?
Update: it is issue in Xcode 8 when I print client_image.frame.size.height it's return 1000 first time. after scrolling it's return right value.
call layoutIfneed and LayoutSubviews both before using frame of Imageview. Try following..Working for me.
client_image=[cell viewWithTag:3];
[cell layoutIfNeeded];
[cell layoutSubviews];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
// client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
// [client_image layoutIfNeeded];
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
In your cellForRow, Try this.
NSURL *url = [NSURL URLWithString:#"http:url..."];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
UIImage *placeholderImage = [UIImage imageNamed:#"your_placeholder"];
__weak UITableViewCell *weakCell = cell;
[cell.imageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.imageView.image = image;
// Write your code here for Rounding corner
[weakCell setNeedsLayout];
} failure:nil];
Below is the method which will return the image and you can set this in YourImageView.
Try to give round corner to YourImageView in this method's sucessblock.
From AFNetworking Documentation :
/**
Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied.
#param urlRequest The URL request used for the image request.
#param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
#param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
#param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(nullable UIImage *)placeholderImage
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Try to give corner radius in willDisplayCell of tableView delegate and check:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *) cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
client_image=[cell viewWithTag:3];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
//client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
//[client_image layoutIfNeeded];
client_image.clipsToBounds = YES;
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
[cell layoutSubviews];
[cell layoutIfNeeded];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YOURCELLINDETIFIER";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIImageView *profile = (UIImageView*)[cell viewWithTag:90]; // your imageview tag
profile.layer.cornerRadius = profile.frame.size.width / 2;
profile.layer.masksToBounds = YES;
profile.layer.borderWidth = 1.0f;
profile.layer.borderColor = [UIColor grayColor].CGColor;
NSURL *url = [NSURL URLWithString:"YOUR URL HERE"; // your URL
[profile sd_setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (!error) {
profile.image=image;
}
}];
return cell;
}
Size of the client_image frame is reported incorrect thats why you need to call layoutIfNeeded method. Just try calling [self layoutIfNeeded]; inside viewDidLoad method before doing any cornerRadius stuff.
This should work.
#import <QuartzCore/QuartzCore.h>
cell.previewImage.clipsToBounds = YES;
cell.previewImage.layer.cornerRadius = 20;//asper your requirement give the value of radius.
my replaceObjectatIndex:withObject: is not being called when I put it inside a block. I know this because when I NSLog in the outer block the value doesn't change. why is the method inside the inner block not being called while the method in the outer block does? what's the difference?
this is the code:
if (cell.selected) {
[[SDImageCache sharedImageCache] queryDiskCacheForKey:imageID
done:^(UIImage *image, SDImageCacheType cacheType)
{
// image is not nil if image was found
if (image == nil) {
//image is not found
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:[NSURL URLWithString:link]
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
// image is finished being downloaded
// resize image
UIImage *resizedImage = [self imageWithImage:image forRowAtIndexPath:indexPath];
// store resized image in cache
[[SDImageCache sharedImageCache] storeImage:resizedImage forKey:imageID];
//set image view to resized image
[textCell.testImage setImage:resizedImage];
[self.heightArray replaceObjectAtIndex:indexPath.row
withObject:[NSNumber numberWithFloat:image.size.height]];
}
//delete original sized image
image = nil;
}];
} else {
//image is found
[textCell.testImage setImage:image];
NSLog(#"image found %#", [self.heightArray objectAtIndex:indexPath.row]);
}
}];
} else {
//cell is not selected
textCell.testImage.image = nil;
}
By the way, the setImage: method works perfectly but not replaceObjectatIndex:withObject:
I don't know what you are doing but I found an issue in your code: you are using the image even after you set it to nil.
Correction:
//delete original sized image -
//??image = nil;
[self.heightArray replaceObjectAtIndex:indexPath.row
withObject:[NSNumber numberWithFloat:image.size.height]];
image = nil;
I'm picking images with UIPicker and when user picks, the download will start. I'm downloading 78 images from my server to create an animation. How can I make it while it's downloading images to hide current image(imageview), and when download finishes to show image(imageview). I tried this but it's not working.
- (void)viewDidLoad
{
[super viewDidLoad]
// Load starting image, otherwise screen is blank
self.radar_1 = [[UIImageView alloc]initWithFrame:CGRectMake(0, 65, self.view.frame.size.width, self.view.frame.size.width-70)];
radar_1.image = [UIImage animatedImageWithAnimatedGIFURL:[NSURL URLWithString:#"<|SERVER LINK|>"]];
[self.view addSubview:radar_1];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (row == 2) {
self.radar_1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 65, self.view.frame.size.width, self.view.frame.size.width-70)];
self.radar_1.hidden = YES;
radar_1.animationImages = [NSArray arrayWithObjects:
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"<|SERVER LINK|> "]]],
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"<|SERVER LINK|> "]]],
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"<|SERVER LINK|>"]]],
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"<|SERVER LINK|>"]]],
{...} // Doing the same 77 times...
radar_1.hidden = NO;
radar_1.animationDuration = 20.0f;
radar_1.animationRepeatCount = 0;
[radar_1 startAnimating];
[self.view addSubview: radar_1];
}
Downloading 70 images from a NSURL synchronously is probably not the best idea, from a user experience standpoint. Is there a reason you cannot put all the images together in a Sprite Sheet and download a single image, and then process it on the client end to display your animation?
If you really do need to go grab 70 images, it would be best to do that asynchronously so the UI remains responsive. Here is an example of simple Async Image load that could be used:
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
You can hold a counter variable and iterate through your image requests calling the method above. When each is complete you can tick off another successful image request. When you have them all, then you can construct that animation you are making.
While that is occurring, you can add an ActivityIndicator to your view to let the user know stuff is happening while they are waiting.
Hope that helps some.
I am using SDWebImage for fetching images from server to my table view app in IOS.
But the problem is that when I scroll down in table view instead of waiting for the images to load it put the images downloaded in the first few rows of table view and repeat those images till the end row and when it downloads the images it changes those repeated images to the actual image for that row.
NSURL * url = [NSURL URLWithString:string];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:url
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished,NSURL * url)
{
if (finished && image )
{
NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
if ([visibleIndexPaths containsObject:indexPath]) {
cell.myImage.image = image;
}
}
}];
Actually, it is not a bug with SDWebImage, but rather it's the nature of how UITableView works. downloadImageWithURL, is an async process,so when your tableView delegate/datasource methods are called, the image isn't downloaded yet, therefore cellForRow doesn't have an image to display.
To overcome this issue you should first check image from cache as
[[SDWebImageManager sharedManager] diskImageExistsForURL:[NSURL URLWithString:ImageUrl]]
if yes then set image to UIImageView otherwise use downloadImageWithURL to download image and add cell tag(To display image to correct row) as
cell.tag = indexPath.row;
on successfull download first check correct row as
if(cell.tag == indexPath.row){
and set image to UIImageView.Here is setImage method.
-(void)setImage:(SLFirstTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{
SLFirstTableViewCellItem * slFirstTableViewCellItem = [self.categories objectAtIndex:indexPath.row]; // categories is array of items,replace with yours.
NSString *ImageUrl = slFirstTableViewCellItem.imageUrl; //assume image url is in slFirstTableViewCellItem object.
cell.tag = indexPath.row;
if([[SDWebImageManager sharedManager] diskImageExistsForURL:[NSURL URLWithString:ImageUrl]]){
[cell.imgItem setImage: [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:ImageUrl]];
[self hideProgressView:cell];
}else{
[self showProgressView:cell];
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:[NSURL URLWithString:ImageUrl]
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
[[SDImageCache sharedImageCache] storeImage:image forKey:ImageUrl]; // cache image
if(cell.tag == indexPath.row){ // check if correct row
[cell.imgItem setImage:image];
[self hideProgressView:cell];
}
}else{
cell.imgItem.hidden = YES;
cell.progressBar.hidden = YES;
}
}];
}
}
And define showProgressView and hideProgressView methods as
-(void)showProgressView:(SLFirstTableViewCell *)cell {
cell.progressText.hidden = NO;
cell.progressBar.hidden = NO;
cell.imgItem.hidden = YES;
[cell.progressBar startAnimating];
[cell.progressText setText:#"Loading Image..."];
}
-(void)hideProgressView:(SLFirstTableViewCell *)cell{
cell.progressBar.hidden = YES;
cell.progressText.hidden = YES;
cell.imgItem.hidden = NO;
[cell.progressBar stopAnimating];
}
finally call setImage from cellForRowAtIndexPath method(before returning cell) as
[self setImage:cell atIndexPath:indexPath];
I have a class method which fetches images with a completion block. This fetched UIImage is added to an NSCache with a relevant key. This seems to work as expected, however, in the method which fetches images I am using a UIImage's imageWithData: method, which I have discovered does not cache it's data, only imageNamed: does.
I am understandably getting memory warnings because of this, how do I make sure the images loaded with UIImage's imageWithData: method are removed from memory when not needed anymore?
EDIT
Here is the code for the method which downloads the images.
- (void)imageForFootageSize:(FootageSize)footageSize withCompletionHandler:(void (^)(UIImage *image))completionBlock
{
if (completionBlock) {
__block UIImage *image;
// Try getting local image from disk.
//
__block NSURL *imageURL = [self localURLForFootageSize:footageSize];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
dispatch_async(dispatch_get_main_queue(), ^{
if (image) {
completionBlock(image);
} else {
//
// Otherwise try getting remote image.
//
imageURL = [self remoteURLForFootageSize:footageSize];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
image = [UIImage imageWithData:imageData];
if (image) {
//
// Save remote image to disk
//
NSURL *photoDirectoryURL = [Footage localURLForDirectory];
// Create the folder(s) where the photos are stored.
//
[[NSFileManager defaultManager] createDirectoryAtPath:[photoDirectoryURL path] withIntermediateDirectories:YES attributes:nil error:nil];
// Save photo
//
NSString *localPath = [[self localURLForFootageSize:footageSize] path];
[imageData writeToFile:localPath atomically:YES];
}
completionBlock(image);
});
});
}
});
});
}
}
EDIT 2
Methods which use the above class method to fetch and process the UIImage in the completionHandler.
Method inside UICollectionViewCell subclass.
- (void)setPhoto:(Photo *)photo withImage:(UIImage *)image
{
[self setBackgroundColor:[UIColor blackColor]];
[self.imageView setBackgroundColor:[UIColor clearColor]];
if (photo && !image) {
[photo imageForFootageSize:[Footage footageSizeThatBestFitsRect:self.bounds]
withCompletionHandler:^(UIImage *image) {
if ([self.delegate respondsToSelector:#selector(galleryPhotoCollectionViewCell:didLoadImage:)]) {
[self.delegate galleryPhotoCollectionViewCell:self didLoadImage:image];
}
image = nil;
}];
}
[self.imageView setImage:image];
BOOL isPhotoAvailable = (BOOL)(image);
[self.imageView setHidden:!isPhotoAvailable];
[self.activityIndicatorView setHidden:isPhotoAvailable];
}
Method in UICollectionView data source delegate
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DIGalleryPhotoCollectionViewCell *photoCell = [collectionView dequeueReusableCellWithReuseIdentifier:photoCellIdentifier forIndexPath:indexPath];
[photoCell setDelegate:self];
Footage *footage = [self footageForIndexPath:indexPath];
Photo *photo = ([footage isKindOfClass:[Photo class]]) ? (Photo *)footage : nil;
if (photo) {
//
// Photo
//
[photoCell setPhoto:photo withImage:[self.galleryCache objectForKey:photo.footageID]];
}
return photoCell;
}
Here are the other relevant methods:
- (void)galleryPhotoCollectionViewCell:(DIGalleryPhotoCollectionViewCell *)cell didLoadImage:(UIImage *)image
{
NSIndexPath *indexPath = [self.galleryCollectionView indexPathForCell:cell];
Footage *footage = [self footageForIndexPath:indexPath];
if ([footage isKindOfClass:[Footage class]]) {
Photo *photo = (Photo *)footage;
UIImage *cachedImage = [self.galleryCache objectForKey:photo.footageID];
if (!cachedImage) {
cachedImage = image;
[self.galleryCache setObject:image forKey:photo.footageID];
}
[cell setPhoto:photo withImage:image];
}
}
And also my getter method for the NSCache property galleryCache
- (NSCache *)galleryCache
{
if (!_galleryCache) {
_galleryCache = [[NSCache alloc] init];
}
return _galleryCache;
}
Instead of rolling your own image downloading and caching solution you might be better off using SDWebImage. Then you don't have to worry about the downloading, caching or anything. SDWebImage also using disk caching so you don't have to worry about freeing memory.
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
// do something with image
}
}];
I'm not sure but you also might have a retain cycle:
__weak typeof(self) weakSelf = self;
[photo imageForFootageSize:[Footage footageSizeThatBestFitsRect:self.bounds] withCompletionHandler:^(UIImage *image) {
if ([weakSelf.delegate respondsToSelector:#selector(galleryPhotoCollectionViewCell:didLoadImage:)])
{
[weakSelf.delegate galleryPhotoCollectionViewCell:weakSelf didLoadImage:image];
}
image = nil;
}];