Now I'm using this code, with a little modification:
if (self._photoPath && !self._photo) {
dispatch_queue_t bg_thread = dispatch_queue_create("com.yourcompany.bg_thread", NULL);
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(bg_thread,^{
NSData *data = [NSData dataWithContentsOfFile:self._photoPath];
if(data != nil) {
dispatch_async(main_queue,^{
self._photo = [UIImage imageWithData:data];
[self.photoButton setImage:[UIImage imageNamed:#"photoButton.png"] forState:UIControlStateNormal];
});
}
});
}
As you can see, in fact after I get that photo I want set it immediately to my "photoButton",
but now, UI got smoothed, but my photoButton's appearance is always black...
What should I do next?
Thanks for your patience.
_______________Updated________
I have 2 viewControllers, A and B.
A is the root viewController, and B is A's child viewController.
In B, there is a button for calling the camera to take a photo.
After user toke a photo, the photo's apperance becomes that photo.
When I push a new B (with no photo) from A,
things goes smoothly.
But when there is an old B with a photo in it.
the animation gets a little stucked, since the following code I guess:
- (void)viewWillAppear:(BOOL) animated {
if (self._photoPath && !self._photo) {
NSData *data = [NSData dataWithContentsOfFile:self._photoPath];
if(data != nil)
self._photo = [UIImage imageWithData:data];
}
[super viewWillApear];
}
But I do need to get that photo before the view is displayed since I need to set that photo to my photoButton's background.
So, is there a way to avoid stucking the view's animation? Cause it really result in bad user experience.
Thanks a lot!
I found the answer.
Just add a line of code to refresh the button.
For me, it is
[self.photoButton setImage:[UIImage imageNamed:#"photoButton.png"] forState:UIControlStateNormal];
NSUInteger p[2] = {1,4};
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:
[NSIndexPath indexPathWithIndexes:p length:2]]
withRowAnimation:UITableViewRowAnimationNone];
Related
I want to pass a UIImage from a View Controller to another one, but it doesn't work. I actually get (null) if I log the UIImage Value in the second View Controller.
The Image File comes from parse.com. And it works absolutely fine in the first view controller. But as soon as I pass it to the second view controller, the image won't work there.
Here's the code:
My prepareForSegue in the .m file of the firstViewController (BookListTableViewController)
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ShowBookDetailSegue"]) {
NSIndexPath *indexPath = [self.bookListTableView indexPathForSelectedRow];
ParseBookDetailTableViewController *pbdtvc = segue.destinationViewController;
PFObject *tempObject = [totalStrings objectAtIndex:indexPath.row];
PFFile *eventImage = [tempObject objectForKey:#"bookImage"];
if(eventImage != NULL)
{
[eventImage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
UIImage *thumbnailImage = [UIImage imageWithData:imageData];
pbdtvc.bookImageDetail = thumbnailImage;
}];
}
my .h file of the secondViewController (ParseBookDetailViewController)
#property (strong, nonatomic) UIImage* bookImageDetail;
#property (strong, nonatomic) IBOutlet UIImageView *bookImageDetailView;
viewDidLoad of my .m file of the secondViewController (ParseBookDetailViewController)
NSLog(#"%#",bookImageDetail);
[bookImageDetailView setImage:bookImageDetail];
As I said: in the original View Controller the Image is correct. I can log it and even set it there to an ImageView. But in the second view controller there is (null).
Glad for help, thanks..
EDIT:
This is the code from my cellForRowAtIndexPath.
PFObject *tempObject = [totalStrings objectAtIndex:indexPath.row];
//cell.textLabel.text = [tempObject objectForKey:#"bookTitle"];
cell.titleTextField.text = [tempObject objectForKey:#"bookTitle"];
cell.bookAutor1Label.text = [tempObject objectForKey:#"bookAutor1"];
cell.isbnLabel.text = [tempObject objectForKey:#"bookISBN"];
cell.statusLabel.text = [tempObject objectForKey:#"bookStatus"];
cell.yearLabel.text = [tempObject objectForKey:#"BookDate"];
if ([cell.statusLabel.text isEqualToString:#"nicht verfügbar"]) {
cell.statusLabel.tintColor = [UIColor redColor];
cell.dotImageIcon.image = [UIImage imageNamed:#"dot_red.png"];
}else if ([cell.statusLabel.text isEqualToString:#"verfügbar"]){
cell.statusLabel.tintColor = [UIColor greenColor];
cell.dotImageIcon.image = [UIImage imageNamed:#"dot_green.png"];
}
PFFile *eventImage = [tempObject objectForKey:#"bookImage"];
if(eventImage != NULL)
{
[eventImage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
UIImage *thumbnailImage = [UIImage imageWithData:imageData];
prepareForSegueImage = thumbnailImage;
[cell.bookImageView setImage:thumbnailImage];
}];
}
At the bottom I have set the Image (that works) in a new property (prepareForSegueImage). NOW its loaded.
And now I've tried to pass only the prepareForSegueImage to the new view controller, but it actually doesn't pass the one from the cell, it passes the last one loaded in the whole table view from the first view controller.
I'm guessing:
[eventImage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
UIImage *thumbnailImage = [UIImage imageWithData:imageData];
pbdtvc.bookImageDetail = thumbnailImage;
}];
is an asynchronous request happening in the background, that will take some time to return. But your code is expecting it to happen instantly.
Load the image first and call the segue in the completion block,
EDIT
If it works in the first viewController (meaning you have the image) why are you requesting it again? why not simply pass the image you already have?
EDIT 2:
As explained in my comment, your variable is being overwritten each time the cellForRowAtindexPath is called. You could either wrap that code in an if statement, checking for an index or a certain image etc. or you can use the below code to get a specific cell and pull the image form that.
[tblView cellForRowAtIndexPath: [NSIndexPath indexPathForRow:2 inSection:0]];
If you have the image downloaded and have a relatively easy means of getting a reference to it you should not be downloading it again. This could cost the user on a 3G data plan as well as waste resources.
This happens because you are using the getDataInBackgroundWithBlock, which is an asynchronous call. As this happens in a background thread, it is probably not finished when you segue to the other viewcontroller.
You said it works fine in the first controller. I assume this means you have already downloaded the image. Put that in a property instead, and send this property to the next controller.
i am storing images as base64 NSData in database and getting from Database and display in UICollectionView cellForItemAtIndexPathas cell.bgImageView.imageimages are displaying but scrolling is not smooth and when i am trying to get back from another ViewController its taking too much time to load the view.
CODE
NSData *imageData=[[NSData alloc]initWithBase64EncodedString:itemObj.strImage options:NSDataBase64DecodingIgnoreUnknownCharacters];
cell.bgImageView.image = [UIImage imageWithData:imageData];
Code
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
ItemViewController *vc = [[ItemViewController alloc]initWithNibName:#"ItemViewController" bundle:nil];
vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
Item *itemObject = [[Item alloc]init];
itemObject = [arrayOfItems objectAtIndex:indexPath.item];
vc.strItemName = itemObject.strItemName;
vc.strItemDesc = itemObject.strShortDescription;
vc.strImage = itemObject.strImage;
[self presentViewController:vc animated:YES completion:nil];}
above is the code for next viewController and passing values and iam come back to CollectionView like this
- (void)dismiss{
[self dismissViewControllerAnimated:YES completion:nil];
}
If you are setting image as below,
[UIImage imageWithData:data]
this is synchronous operation, so it will block your main thread until it finishes loading.
THis is the reason why your scroll view is jerky because you blocked the main thread.
Try loading the images in backGround Thread, it will definitely solve ur problem.
Code for loading in backGround thread,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:#"www.example.com"]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.myImageView.image = image;
});
});
Put your iimage loading code in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
your code here....
});
I have an UITableView its loading number ofsongs from the back end (actually those are song's urls). Those are getting in JSON format. Now my problem is when number of songs are increases the table getting stuck. It hard to scroll. So how I can avoid this?
I'm loading views top of a main view. In main view viewDidLoad
arrSongList=[[NSMutableArray alloc]init];
arrSongList=[ws GetSongs];`
Then there is a menu to select song option. When I click on that menu cell it loads the songs view. for loading song view
-(void)getAllSongsAndReloadTable :(id)sender//1
{
[viewDisplayArea addSubview:viewSongs];
[self flipView : viewDisplayArea: viewDisplayArea : UIViewAnimationTransitionFlipFromLeft];
[progressAlert dismissWithClickedButtonIndex:0 animated:YES];
}
ViewSongs view has the song table. It loads in this way
This is for numberOfRowsInSection
else if (tableView.tag==4)
{
return [arrSongList count];
}
This is for cellForRowAtIndexPath
else if (tableView.tag==4)//All Songs
{
cell4 =nil;
if (cell4 == nil) {
NSArray *nib =[[NSBundle mainBundle]loadNibNamed:#"SongCell" owner:self options:nil];
cell4=[nib objectAtIndex:0];
}
[cell4 setSelectionStyle:UITableViewCellSelectionStyleNone];
cell4.lblSong.text=[[arrSongList objectAtIndex:indexPath.row] valueForKey:#"SONGTITLE"];
cell4.lblArtist.text=[[arrSongList objectAtIndex:indexPath.row] valueForKey:#"ARTISTNAME"];
NSString *imgUrl=[[arrSongList objectAtIndex:indexPath.row]valueForKey:#"SONGIMG"];
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString: imgUrl]];
UIImage *img = [[UIImage alloc] initWithData:data];
cell4.imgSong.image=img;
NSString *imgUrlLarge=[[arrSongList objectAtIndex:indexPath.row]valueForKey:#"SONGIMGLARGE"];
NSData *dataLarge=[NSData dataWithContentsOfURL:[NSURL URLWithString: imgUrl]];
UIImage *imgLarge = [[UIImage alloc] initWithData:dataLarge];
cell4.imgSongLarge=[[UIImageView alloc]init];
cell4.imgSongLarge.image=imgLarge;
[cell4.btnTick addTarget:self action:#selector(tickButtonTapped:) forControlEvents:UIControlEventTouchUpInside] ;
[cell4.btnAddtoMyList addTarget:self action:#selector(topLeft:) forControlEvents:UIControlEventTouchUpInside];
return cell4;
}
You will be having a array of songs urls. And you would have given number of rows in table as arrayOfUrlSongs.count. In stead of that make
Make a global count
int count = 10;
if(arrayOfUrlSongs.count < count)
return arrayOfUrlSongs.count;
else
{
return count;
}
Keep a reload button in footer view of table. On click of that increment count by 10 and reload table.
I hope this helps you.
You should also consider loading the cells in batches. I'd recommend checking out the Sensible TableView framework. The framework will automatically fetch all the songs from your web service and divide them in batches if you wish. Should save you a ton of work.
In my app, I am loading images for UITableViewCells asynchronously like this:
-(void) loadImage:(NSString *)urlString cell:(UITableViewCell *)cell {
UIImageView *iv = cell.imageView;
UIImage *image = [imageCache objectForKey:urlString];
if(!image) {
NSURL *url = [NSURL URLWithString:urlString];
image = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];
if(image) {
[imageCache setObject:image forKey:urlString];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
iv.image = image;
[cell addSubview:iv];
});
}
When the tableview is refreshed before all of the images have finished loading, an exception is thrown on iv.image = image; because iv has been deallocated. What is the best way to make sure I never try to set an image for a deallocated cell? Is there some way to kill any lingering asynchronous loads when the tableview is reloaded?
I would implement this other way around. Try to get an image from cache when you are constructing the cell. If the image is not found trigger on asynchronous download and notify cell or controller (from main thread) that image was downloaded. You can use KVO to connect cells with images cache.
I'm having a hard time removing my animation from memory. What is the best way to do this?
This is my animation code:
-(IBAction)animationOneStart {
NSMutableArray *images = [[NSMutableArray alloc] initWithCapacity:88];
for(int count = 1; count <= 88; count++)
{
NSString *fileName = [NSString stringWithFormat:#"testPic1_%03d.jpg", count];
UIImage *frame = [UIImage imageNamed:fileName];
[images addObject:frame];
}
loadingImageView.animationImages = images;
loadingImageView.animationDuration = 5;
loadingImageView.animationRepeatCount = 1; //Repeats indefinitely
[loadingImageView startAnimating];
[images release];
}
Thanks!
[images removeAllObjects];
[images release];
Don't need to release it anymore with ARC under Xcode 4.2! Update today :)
As far as I can tell, there is not a callback to know when the imageView's animation is finished. This is unfortunate, because it means that to remove the animation when the imageView is finished animating, you will need repeatedly check the imageView's isAnimating method until it returns false. The following is an example of what I mean:
You might be able to do something like this—in your original code, add the following line
[loadingImageView startAnimating];
[images release];
[self performSelector:#selector(checkIfAnimationIsFinished) withObject:nil afterDelay:4.5f];
Then create a method
-(void)checkIfAnimationIsFinished {
if (![loadingImageView isAnimating]) {
[loadingImageView release];
} else {
[self performSelector:#selector(checkIfAnimationIsFinished) withObject:nil afterDelay:0.5f];
}
}
This was just an example, I used the values 4.5f and 0.5f because, unfortunately, the timing is not precise, it is more of a ballpark figure. 4.9f and 0.1f might work better. Regardless, the problem is that if you set the method to fire 5.0f seconds after you start the animation, it is not 100% certain that the animation will be be finished or not. So you will need to check repeatedly if it is not finished.
Anyway, this is not the cleanest solution, but unless someone else knows how to get a callback when an imageView is finished animating, I don't know of a better solution.