I am having a problem loading images to UICollectionView. I am trying to take a photo and save it with certain name to Documents folder, and I want this photo to appear in Collection View after photo taking dismisses.
Up until now my images are being saved in the documents folder and automatically added to an _stepImagesArray but I can't figure out how to load those images and populate uicollectionview with them.
Please help, it has been a while with my trials and errors :)
StepsCollectionViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
_stepImagesArray = [[NSMutableArray alloc] init];
}
#pragma mark - UICollectionView settings
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection: (NSInteger)section {
return _stepImagesArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.imageCell.image = [UIImage imageNamed:[_stepImagesArray objectAtIndex:indexPath.row]];
cell.labelCell.text = _stepImagesArray[indexPath.row];
return cell;
}
- (IBAction)takePhoto
{
UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
photoPicker.delegate = self;
photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:photoPicker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)photoPicker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *selectedImage = [info valueForKey:UIImagePickerControllerOriginalImage];
[self.selectedImageView setImage:selectedImage];
arrayCount = self.stepImagesArray.count;
NSLog(#"array count is: %i",arrayCount);
NSString *jpgImageName = [NSString stringWithFormat:#"Documents/FolderName_%#%i.jpg", passedStepName, arrayCount+1];
NSString *jpgImageNameForArray = [NSString stringWithFormat:#"FolderName_%#%i.jpg", passedStepName, arrayCount+1];
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent: jpgImageName];
[UIImageJPEGRepresentation(selectedImage, 0.1) writeToFile:jpgPath atomically:YES];
// Let's check to see if files were successfully written...
// Create file manager
NSError *error;
NSFileManager *fileMgr = [NSFileManager defaultManager];
// Point to Document directory
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
// Write out the contents of home directory to console
NSLog(#"Documents directory: %#", [fileMgr contentsOfDirectoryAtPath:documentsDirectory error:&error]);
[_stepImagesArray addObject:jpgImageNameForArray];
[photoPicker dismissViewControllerAnimated:YES completion:nil];
}
CollectionViewCell.h
#interface CollectionViewCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UIImageView *imageCell;
#property (weak, nonatomic) IBOutlet UILabel *labelCell;
#end
Related
I am creating an array of images selected from the phones camera roll. They are being stored but for some reason I can't get them to display in my collectionView.
in my CollectionViewController.m
- (void)viewDidLoad {
[self.photoCollectionView registerClass:[PhotoCell class] forCellWithReuseIdentifier:#"PhotoCell"];
self.assets = [[NSMutableArray alloc]init];
[super viewDidLoad];
- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.assets.count;
}
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PhotoCell *cell = (PhotoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"PhotoCell" forIndexPath:indexPath];
ALAsset *asset = self.assets[indexPath.row];
cell.asset = asset;
cell.backgroundColor = [UIColor redColor];
return cell;
}
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSMutableArray *tmpAssets = [#[] mutableCopy];
[tmpAssets addObject:image];
self.assets = tmpAssets;
self.testImage.image = [self.assets objectAtIndex:0];
[picker dismissViewControllerAnimated:YES completion:Nil];
[self.photoCollectionView reloadData];
}
in my PhotoCell.h class
#interface PhotoCell : UICollectionViewCell
#property (nonatomic,strong) ALAsset *asset;
#end
in my PhotoCell.m class
#import "PhotoCell.h"
#interface PhotoCell ()
#property(nonatomic, strong) IBOutlet UIImageView *photoImageView;
#end
#implementation PhotoCell
- (void) setAsset:(ALAsset *)asset
{
_asset = asset;
self.photoImageView.image = [UIImage imageWithCGImage:[asset thumbnail]];
}
#end
Any help would be great thank you.
I need some help figuring out how to pass data from my collection view to the detail view controller. I am starting by simply passing the image through. My code is below:
ViewController:
-(MyCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CELL" forIndexPath:indexPath];
PFObject *imageObject = [imageFilesArray objectAtIndex:indexPath.row];
__block UIImage *MyPicture = [[UIImage alloc]init];
PFFile *imageFile = [imageObject objectForKey:#"test"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
MyPicture = [UIImage imageWithData:data];
cell.CollectionImg.image = [UIImage imageWithData:data];
cell.cellLabel.text = [object objectForKey:#"phone"];
}
}];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
TopDetailViewController *detailVC = [[TopDetailViewController alloc] initWithNibName:#"TopDetailViewController" bundle:[NSBundle mainBundle]];
detailVC.img= [imageFilesArray objectAtIndex: indexPath.row];
[self.navigationController pushViewController:detailVC animated:YES];
}
And the Detail view:
.h
#property (nonatomic, strong) IBOutlet UIImageView *imageView;
#property (nonatomic, strong) IBOutlet NSString *img;
.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView.image = [UIImage imageNamed:self.img];
// Do any additional setup after loading the view from its nib.
}
If someone could help me out I would greatly appreciate.
You can only use [UIImage imageNamed:] to retrieve images that are in your app bundle. It looks like you create these images at run-time based on the PDF file, so just passing in the string to the detail view and loading it with [UIImage imageNamed:] won't work. You should pass the reference to the image or the PFFile object so that the detail view can generate the image itself.
In my app, there's only 2 views. First view is a collection view and when you tap on an image, it goes to the MWPhotoBrowser to view the image in it's full size.
I'm retrieving ~100 images over the internet and adding them to a NSMutableArray.
There's a problem with this. When I run the app for the first time and select a thumbnail image from the grid it shows the last image. Not the one I selected. Note that I emphasized on the first time because it only happens in the first time. If I go back and tap on another image, it correctly shows the selected one. Below is a screenshot of how its supposed to work.
Here is the code
#import "UIImageView+WebCache.h"
#import "ViewController.h"
#import "MWPhotoBrowser.h"
#import "Image.h"
#import "ImageCell.h"
#interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource, MWPhotoBrowserDelegate>
#property (strong, nonatomic) NSMutableArray *images;
#property (strong, nonatomic) NSMutableArray *browserImages;
#property (strong, nonatomic) MWPhotoBrowser *browser;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
self.browser.displayActionButton = YES;
self.browser.displayNavArrows = YES;
self.browser.displaySelectionButtons = NO;
self.browser.zoomPhotosToFill = YES;
self.browser.alwaysShowControls = YES;
self.browser.enableGrid = NO;
self.browser.startOnGrid = NO;
self.browser.wantsFullScreenLayout = NO;
[self.browser showNextPhotoAnimated:YES];
[self.browser showPreviousPhotoAnimated:YES];
[self loadImages];
}
- (void)loadImages
{
self.images = [[NSMutableArray alloc] init];
self.browserImages = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", HOST_URL, #"All_Images.php"]];
NSData *jsonResults = [NSData dataWithContentsOfURL:url];
NSDictionary *results = [NSJSONSerialization JSONObjectWithData:jsonResults options:0 error:NULL];
NSDictionary *images = results[#"Images"];
for (NSDictionary *img in images) {
Image *imageObj = [[Image alloc] init];
imageObj.imageId = [img objectForKey:#"id"];
imageObj.imageName = [img objectForKey:#"name"];
// Get the full image path
NSString *fullImagePath = [NSString stringWithFormat:#"%#%#", HOST_URL, [img objectForKey:#"full_image"]];
imageObj.imagePath = fullImagePath;
// Get the thumbnail image path depending on the device
NSString *thumbnailPath;
if (DEVICE_IS_PAD) {
thumbnailPath = [NSString stringWithFormat:#"%#%#", HOST_URL, [img objectForKey:#"thumbnail_ipad"]];
} else {
thumbnailPath = [NSString stringWithFormat:#"%#%#", HOST_URL, [img objectForKey:#"thumbnail_iphone"]];
}
imageObj.imageThumbnail = thumbnailPath;
// Creates an object for each image and fill it with the retrieved info
[self.images addObject:imageObj];
// This array stores the image paths for later use (displaying them in a photo browser)
MWPhoto *browserImage = [MWPhoto photoWithURL:[NSURL URLWithString:imageObj.imagePath]];
browserImage.caption = imageObj.imageName;
[self.browserImages addObject:browserImage];
}
[self.collectionView reloadData];
}
#pragma mark - MWPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser
{
return self.browserImages.count;
}
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index
{
if (index < self.browserImages.count) {
return self.browserImages[index];
}
return nil;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.images.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ImageCell";
ImageCell *cell = (ImageCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
Image *image = self.images[indexPath.row];
[cell.imageView setImageWithURL:[NSURL URLWithString:image.imageThumbnail] placeholderImage:[UIImage imageNamed:#"placeholder"]];
return cell;
}
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// Opens the image browser
[self.browser setCurrentPhotoIndex:indexPath.row];
[self.navigationController pushViewController:self.browser animated:YES];
}
#end
I cannot find out why it shows the last image of the grid the first time I select an image. I have uploaded a demo project with the issue so it'll be easier for you to take a quick look at it.
https://www.dropbox.com/s/tp6a67f83l0rzin/PhotoBrowserTest.zip
I'd really appreciate anyone's help to correct this problem.
Thank you.
Change in your project these methods
- (NSUInteger)numberOfPhotos {
if (_photoCount == NSNotFound || _photoCount == 0) {
if ([_delegate respondsToSelector:#selector(numberOfPhotosInPhotoBrowser:)]) {
_photoCount = [_delegate numberOfPhotosInPhotoBrowser:self];
} else if (_depreciatedPhotoData) {
_photoCount = _depreciatedPhotoData.count;
}
}
if (_photoCount == NSNotFound) _photoCount = 0;
return _photoCount;
}
AND
- (id)initWithDelegate:(id <MWPhotoBrowserDelegate>)delegate {
if ((self = [self init])) {
_delegate = delegate;
[self setCurrentPhotoIndex:0];
}
return self;
}
You need to check if your _photoCount == 0, because the first time you don´t have this info and _photoCount will be 0, but the second time your numberOfPhotosInPhotoBrowser will be the right
When I load the images to show to the UICollectionView I load all the images from the array like this
- (void)viewDidLoad
{
allImagesArray = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"Others";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
collectionOthers.delegate =self;
collectionOthers.dataSource=self;
for(NSString *str in directoryContent)
{
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
NSData *data = [NSData dataWithContentsOfFile:finalFilePath];
if(data)
{
UIImage *image = [UIImage imageWithData:data];
[allImagesArray addObject:image];
NSLog(#"array:%#",[allImagesArray description]);
image = nil;
}
finalFilePath=nil;
data=nil;
}
paths= nil;
documentsDirectory= nil;
location= nil;
fPath= nil;
directoryContent = nil;
}
This is the biggest issue in my app since it uses so many memory. It is because number and size of the images, this could just take up memory. I would only want to load images when they are needed, and discard them when they are no longer needed.However I do not know where and how to change my code so that it will be that way. I am doing this for three month or so and I really need help.
Update
This is my code for the specific part
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseID = #"ReuseID";
OthersCell *mycell = (OthersCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseID forIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[mycell viewWithTag:1];
imageInCell.image = [allImagesArray objectAtIndex:indexPath.row];
NSLog(#"a");
return mycell;
}
Clearly, you should load the images just-in-time. One should never hold an array of images (because they take up a lot of memory), but rather just hold an array of filenames. So I'm suggesting you retire allImagesArray and instead define a NSMutableArray called filenames. You could then create the UIImage objects on the fly:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
OthersCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[cell viewWithTag:1];
imageInCell.image = [UIImage imageWithContentsOfFile:filenames[indexPath.item]];
return cell;
}
This, assumes, of course, that you populated this NSMutableArray of filenames in viewDidLoad:
- (void)viewDidLoad
{
filenames = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"Others";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
collectionOthers.delegate =self;
collectionOthers.dataSource=self;
for(NSString *str in directoryContent)
{
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
[filenames addObject:fileFilePath];
}
}
This has a problem, though, because imageWithContentsOfFile (as well as loading it into a NSData first and then doing imageWithData) is a bit slow if the images aren't tiny. On slower devices, this can result in a slight stuttering of a quick scroll of a collection view. So, a better approach would be to (a) load the images asynchronously; (b) use a NSCache to optimize performance for when you scroll backwards.
So, first, define a cache:
#property (nonatomic, strong) NSCache *imageCache;
And, instantiate this in viewDidLoad:
self.imageCache = [[NSCache alloc] init];
self.imageCache.name = #"com.company.app.imageCache";
And then, cellForItemAtIndexPath can (a) set the image from the cache; and (b) if not found, retrieve the image asynchronously updating cache and cell appropriately, e.g.:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
OthersCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[cell viewWithTag:1];
NSString *cacheKey = filenames[indexPath.item];
imageInCell.image = [self.imageCache objectForKey:cacheKey];
if (imageInCell.image == nil) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:filenames[indexPath.item]];
if (image) {
[self.imageCache setObject:image forKey:cacheKey];
dispatch_async(dispatch_get_main_queue(), ^{
OthersCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[updateCell viewWithTag:1];
imageInCell.image = image;
});
}
});
}
return cell;
}
And, obviously, make sure you purge the cache if you receive memory warnings (in iOS 7, the cache doesn't always automatically purge itself under pressure like it used to do):
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[self.imageCache removeAllObjects];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
This is the method in which you should be loading the images.
In viewDidLoad, I'd build the array of NSString file paths to each image, then I'd use the collectionView:cellForItemAtIndexPath: method to load the image from the specific file path for this particular cell.
In viewDidLoad You could just load a list of available images. So remove the for loop: for(NSString *str in directoryContent) { ... } loop there (EDIT: or make it a simple for loop, just to populate an array with filenames for the files having data).
When you update a specific collectionviewcell in collectionView:cellForItemAtIndexPath:, just load the image (only 1). The cell will now hold the image data instead of your view controller. So when the cell is released, so is the image data.
I have an app that has a takePhoto UIButton on a UIViewcontroller that enables the user to take a photo by using the imagePickerController. I then want the photo to be placed in a UITableView.
The photo part works and for testing purposes I have the image displayed in a UIImage photoImageView.image
I have added the UITableView delegate and then UITable is called photoTable and the cell is called photoCell. I have tried to figure out the code below for adding the image to the table without success. Any help greatly appreciated.
- (IBAction)takePhoto:(id)sender
{
[self startCameraControllerFromViewController: self usingDelegate: self];
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image;
image = (UIImage *)
[info valueForKey:UIImagePickerControllerOriginalImage];
photoImageView.image=image;
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
[picker dismissViewControllerAnimated:YES completion:nil];
}
-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller usingDelegate: (id <UIImagePickerControllerDelegate, UINavigationControllerDelegate>) delegate
{
if (([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera] == NO) || (delegate == nil) || (controller == nil)) return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
cameraUI.allowsEditing = NO;
cameraUI.delegate = delegate;
[controller presentViewController:cameraUI animated:YES completion:nil];
return YES;
}
- (UITableViewCell*)tableView:(UITableView*)photoTable cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString* CellIdentifier = #"photoCell";
UITableViewCell* cell = [photoTable dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell.imageView.image = [UIImage imageNamed:#"nameOfImage.jpg"];
return cell;
}
create the image property, and tableview outlet in .h file
#property(nonatomic, strong) UIIImage *photoImage;
#property(nonatomic, strong) IBOutlet UITableView *tableview;
in .m file in didFinishPickingMediaWithInfo: method
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image;
image = (UIImage *)
[info valueForKey:UIImagePickerControllerOriginalImage];
self.photoImage = image;
[self.tableView reloadData]; //call reloadData method to load data with image
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
[picker dismissViewControllerAnimated:YES completion:nil];
}
in cellForRowAtIndexPath: method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.movieTable dequeueReusableCellWithIdentifier:#"cell"];
cell.imageView.image = self.photoImage;
or use array and add image to array in didFinishPickingMediaWithInfo: and use the image
cell.imageView.image = [self.photoArray objectAtIndex:0];
}
In your following method:
imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
Add this line:
[_tableView reloadData];
Return updated number of rows in:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
and in cellForRowAtIndexPath modify your code like this:
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.imageView.image = [UIImage imageNamed:#"nameOfImage.jpg"];
NOTE: if you want to show more than one image, like every image you capture, then save images in an array. and retrieve them in cellForRowAtIndexPath and assign different image to every cell.
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.imageView.image = [UIImage imageNamed:#"nameOfImage.jpg"];
}
Try it, I hope it helps!
I think you have to save images in document directory after capture photo
like:
- (void)saveImage:(UIImage*)imagepk
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:#"savedImage.png"];
UIImage *image = imagepk; // imageView is my image from camera
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:savedImagePath atomically:NO];
// Code for saving image here
}
and then acces from document directory and add in array.