I am building an App where the user can spin a UIPickerView like a slot machine. The problem I am facing at the moment that at runtime the images in my pickerView start disappearing at random. Then spin again and some of them are back.
Here is the code I use to make the array of images (in my project there are 17 images in every array but to save a bit of space I narrowed it down):
- (void)viewDidLoad{
[super viewDidLoad];
[drinkPickerView_ selectRow:1000 inComponent:0 animated:NO];
[drinkPickerView_ selectRow:1000 inComponent:1 animated:NO];
[drinkPickerView_ selectRow:1000 inComponent:2 animated:NO];
if (managedObjectContext_ == nil)
{
managedObjectContext_ = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
drinkPickerView_.delegate = self;
drinkPickerView_.dataSource = self;
UIImage *fris1 = [UIImage imageNamed:#"fris01.PNG"];
UIImage *fris2 = [UIImage imageNamed:#"fris02.PNG"];
UIImage *fris3 = [UIImage imageNamed:#"fris03.PNG"];
UIImage *fris4 = [UIImage imageNamed:#"fris04.PNG"];
UIImage *fris5 = [UIImage imageNamed:#"fris05.PNG"];
UIImageView *frisView1 = [[UIImageView alloc]initWithImage:fris1];
UIImageView *frisView2 = [[UIImageView alloc]initWithImage:fris2];
UIImageView *frisView3 = [[UIImageView alloc]initWithImage:fris3];
UIImageView *frisView4 = [[UIImageView alloc]initWithImage:fris4];
UIImageView *frisView5 = [[UIImageView alloc]initWithImage:fris5];
UIImage *alcoholisch1 = [UIImage imageNamed:#"alcohol01.PNG"];
UIImage *alcoholisch2 = [UIImage imageNamed:#"alcohol02.PNG"];
UIImage *alcoholisch3 = [UIImage imageNamed:#"alcohol03.PNG"];
UIImage *alcoholisch4 = [UIImage imageNamed:#"alcohol04.PNG"];
UIImage *alcoholisch5 = [UIImage imageNamed:#"alcohol05.PNG"];
UIImageView *alcoholischView1 = [[UIImageView alloc]initWithImage:alcoholisch1];
UIImageView *alcoholischView2 = [[UIImageView alloc]initWithImage:alcoholisch2];
UIImageView *alcoholischView3 = [[UIImageView alloc]initWithImage:alcoholisch3];
UIImageView *alcoholischView4 = [[UIImageView alloc]initWithImage:alcoholisch4];
UIImageView *alcoholischView5 = [[UIImageView alloc]initWithImage:alcoholisch5];
UIImage *freaky1 = [UIImage imageNamed:#"freaky01.PNG"];
UIImage *freaky2 = [UIImage imageNamed:#"freaky02.PNG"];
UIImage *freaky3 = [UIImage imageNamed:#"freaky03.PNG"];
UIImage *freaky4 = [UIImage imageNamed:#"freaky04.PNG"];
UIImage *freaky5 = [UIImage imageNamed:#"freaky05.PNG"];
UIImageView *freakyView1 = [[UIImageView alloc]initWithImage:freaky1];
UIImageView *freakyView2 = [[UIImageView alloc]initWithImage:freaky2];
UIImageView *freakyView3 = [[UIImageView alloc]initWithImage:freaky3];
UIImageView *freakyView4 = [[UIImageView alloc]initWithImage:freaky4];
UIImageView *freakyView5 = [[UIImageView alloc]initWithImage:freaky5];
NSArray *frisImageArray = [[NSArray alloc]initWithObjects:frisView1,frisView2,frisView3,frisView4,frisView5, nil];
NSArray *alcoholischImageArray = [[NSArray alloc]initWithObjects:alcoholischView1,alcoholischView2,alcoholischView3,alcoholischView4,alcoholischView5, nil];
NSArray *freakyImageArray = [[NSArray alloc]initWithObjects:freakyView1,freakyView2,freakyView3,freakyView4,freakyView5, nil];
NSString *frisFieldName = [[NSString alloc]initWithFormat:#"frisDrankenPlaatjesArray"];
[self setValue:frisImageArray forKey:frisFieldName];
NSString *alcoholischFieldName = [[NSString alloc]initWithFormat:#"alcoholischeDrankenPlaatjesArray"];
[self setValue:alcoholischImageArray forKey:alcoholischFieldName];
NSString *freakyFieldName = [[NSString alloc]initWithFormat:#"freakyDrankenPlaatjesArray"];
[self setValue:freakyImageArray forKey:freakyFieldName];
}
This is the code where I load the images in the pickerView:
-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row
forComponent:(NSInteger)component reusingView:(UIView *)view{
if (component==0) {
NSString *arrayName = [[NSString alloc]initWithFormat:#"alcoholischeDrankenPlaatjesArray"];
NSArray *array = [self valueForKey:arrayName];
return [array objectAtIndex:(row%5)];
}
else if(component==1){
NSString *arrayName = [[NSString alloc]initWithFormat:#"frisDrankenPlaatjesArray"];
NSArray *array = [self valueForKey:arrayName];
return [array objectAtIndex:(row%5)];
}
else if (component==2){
NSString *arrayName = [[NSString alloc]initWithFormat:#"freakyDrankenPlaatjesArray"];
NSArray *array = [self valueForKey:arrayName];
return [array objectAtIndex:(row%5)];
}
else{
return nil;
}
}
this is the code that makes the pickerView spin:
-(IBAction)rotate:(id)sender{
int numInRow =1;
int lastVal = -1;
for (int i=0;i<3; i++) {
int newValue = arc4random()%1000;
if (newValue==lastVal)
numInRow++;
else
numInRow =1;
lastVal=newValue;
[drinkPickerView_ selectRow:newValue inComponent:i animated:YES];
[drinkPickerView_ reloadComponent:i];
}
}
I hope this is enough information and I hope somebody can help me out.
I had a similar problem. I tried retaining the objects, arrays, images but that didn't fix it. Turns out that if I populated the arrays again (ala how you initialized them in the viewDidLoad method) in the -pickerView viewForRow: method then it worked great. Seems inefficient to repopulate the same arrays with every method call but it seemed to do the trick.
I think the problem lays at where you initialize your UIImages:
UIImage *fris1 = [UIImage imageNamed:#"fris01.PNG"]; <-- this is autorelease!
You need to set your UIImages as properties so that they can be retained.
Like so:
#property (nonatomic,retain) UIImage *fris1;
and synthesize them in your .m. and applying the image as:
self.fris1 = [UIImage imageNamed:#"fris01.PNG"];
This way, the UIImage is retained throughout the app life.
This is from UIPickerView.h.
/*
these methods return either a plain UIString, or a view (e.g UILabel) to display the row for the component. for the view versions, we cache any hidden and thus unused views and pass them back for reuse. If you return back a different object, the old one will be released. the view will be centered in the row rect
*/
(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;
So when your pickerview tries to show a previously shown uiimageview it releases the previous one. That's why you should create different uiimageview for each component.
Related
So I have class called CSImageViews (subclass of uiview), that is essentially a row of UIImageViews enclosed in a single subclassed UIView.
I've added a custom init method like so (node/yaml contains uiimage name data):
- (id)initWithFrame:(CGRect)frame withNode:(NSDictionary *)node
{
self = [super initWithFrame:frame];
if (self) {
_node = node;
_imageViewYAML = [node objectForKey:#"items"];
_imageViews = [self getImageViewsForItems:_imageViewYAML];
}
return self;
}
And my getImageViewsforItems is like so (adds them all to subview):
-(NSArray *)getImageViewsForItems:(NSArray *)items
{
NSMutableArray *ivs = [NSMutableArray arrayWithCapacity:[items count]];
for (int i = 0; i < [items count]; i++) {
NSDictionary *item = [items objectAtIndex:i];
NSString *type = [item objectForKey:#"type"];
NSString *name = [item objectForKey:#"name"];
UIImage *image = [UIImage imageNamed:name];
UIImageView *iv = [[UIImageView alloc] init];
iv.image = image;
iv.tag = i;
[ivs addObject:iv];
[self addSubview:iv];
}
return ivs;
}
Now when I add this custom view to my main view like this nothing happens:
CSImageViews *imageViews = [[CSImageViews alloc] initWithFrame:frame withNode:node];
[view addSubview:imageViews];
But if I add this 'csimageviews' container into a new uiview first it appears:
CSImageViews *imageViews = [[CSImageViews alloc] initWithFrame:frame withNode:node];
UIView *v = [[UIView alloc] initWithFrame:frame];
[v addSubview:imageViews];
[view addSubview:v];
thoughts?
Stupid error by me. In this particular subclassed UIView I have a method that was called every time its parent view controller was shown. This method actually removed all subviews from the view... so I wasn't able to see the uiimageviews when I rendered it in the initial view... doh.
i want to create an application in one row of tableView there will be 4 images of button which will be loaded from server in background because if i directly load them than the table view will hang some. for this i have used this code for creating button in cell and performing to download images in background.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *hlCellID = #"hlCellID";
UITableViewCell *hlcell = [tableView dequeueReusableCellWithIdentifier:hlCellID];
if(hlcell == nil) {
hlcell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:hlCellID] autorelease];
hlcell.accessoryType = UITableViewCellAccessoryNone;
hlcell.selectionStyle = UITableViewCellSelectionStyleNone;
}
int section = indexPath.section;
NSMutableArray *sectionItems = [sections objectAtIndex:section];
int n = [sectionItems count];
int i = 0, j = 0, tag = 1;
int x = 10;
int y = 30;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (i = 1; i<= n ; i++) //[arr count]
{
for(j=1; j<=4;j++)
{
if (i>=n) break;
Item *item = [sectionItems objectAtIndex:i];
CGRect rect = CGRectMake(x = x, y = y, 68, 65);
UIButton *button=[[UIButton alloc] initWithFrame:rect];
[button setFrame:rect];
UIImage *buttonImageNormal=[UIImage imageNamed:item.image];
[button setBackgroundImage:buttonImageNormal forState:UIControlStateNormal];
[button setContentMode:UIViewContentModeCenter];
// set the image to be loaded (using the same one here but could/would be different)
NSURL *imgURL = [NSURL URLWithString:#"http://londonwebdev.com/wp-content/uploads/2010/07/featured_home.png"];
// Create an array with the URL and imageView tag to
// reference the correct imageView in background thread.
NSMutableArray *arr = [[NSArray alloc] initWithObjects:imgURL, [NSString stringWithFormat:#"%d", tag], nil ];
// Start a background thread by calling method to load the image
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:arr];
// button.tag = [tagValue intValue];
button.tag = tag;
//NSLog(#"....tag....%d", button.tag);
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[hlcell.contentView addSubview:button];
[button release];
tag++;
x = x + 77;
}
x = 10;
y = y + 74;
}
[pool release];
return hlcell;
}
the above code work perfectly but when i download the images and trying to assign at the some particular button than i can't find the button tags althoug i can find the button tags while touchupinside action.
- (void) loadImageInBackground:(NSArray *)urlAndTagReference {
NSLog(#"Received URL for tagID: %#", urlAndTagReference);
// Create a pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Retrieve the remote image. Retrieve the imgURL from the passed in array
NSData *imgData = [NSData dataWithContentsOfURL:[urlAndTagReference objectAtIndex:0]];
UIImage *img = [[UIImage alloc] initWithData:imgData];
// Create an array with the URL and imageView tag to
// reference the correct imageView in background thread.
NSMutableArray *arr = [[NSArray alloc] initWithObjects:img, [urlAndTagReference objectAtIndex:1], nil ];
// Image retrieved, call main thread method to update image, passing it the downloaded UIImage
[self performSelectorOnMainThread:#selector(assignImageToImageView:) withObject:arr waitUntilDone:YES];
[pool release];
}
- (void) assignImageToImageView:(NSArray *)imgAndTagReference
{
// Create a pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i;
UIButton *checkView;
// [imagesForCategories addObject:[imgAndTagReference objectAtIndex:1]];
// UITableViewCell *cell = [celebCategoryTableView cellForRowAtIndexPath:[imgAndTagReference objectAtIndex:1]];
// UIImageView *profilePic = (UIImageView *)[cell.contentView viewWithTag:20];
// profilePic.image = [imgAndTagReference objectAtIndex:0];
// checkView.tag = [[imgAndTagReference objectAtIndex:1] intValue];
// loop
for (UIButton *checkView in [self.tblImage subviews] )
{ i++;
NSLog(#"Checking tag: %d against passed in tag %d",checkView.tag, [[imgAndTagReference objectAtIndex:1] intValue]);
if ([checkView tag] == [[imgAndTagReference objectAtIndex:1] intValue]) {
if (i==35) break;
// Found imageView from tag, update with img
// [checkView setImage:[imgAndTagReference objectAtIndex:0]];
[checkView setImage:[imgAndTagReference objectAtIndex:0] forState:UIControlStateNormal];
//set contentMode to scale aspect to fit
checkView.contentMode = UIViewContentModeScaleAspectFit;
//change width of frame
CGRect frame = checkView.frame;
frame.size.width = 80;
checkView.frame = frame;
}
}
// release the pool
[pool release];
// Remove the activity indicator created in ViewDidLoad()
[self.activityIndicator removeFromSuperview];
}
the all code works perfect but i can't find the table cell subview here for (UIButton *checkView in [self.tblImage subviews] so how to find the subviews of table cell subviews.?
i want to create something like this pls see image.! after that new city will come and just section change the data will show new images in row and cell section.
you may use SDWebImage
Web Image
This library provides a category for UIImageVIew with support for remote images coming from the web.
It provides:
An UIImageView category adding web image and cache management to the Cocoa Touch framework
An asynchronous image downloader
An asynchronous memory + disk image caching with automatic cache expiration handling
A guarantee that the same URL won't be downloaded several times
A guarantee that bogus URLs won't be retried again and again
Performances!
just use it
[youImageView setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil options:SDWebImageRetryFailed];
I have been trying to get a double picker to show both text and images. the left picker will have text and the right will have images. When I do text, it works fine but when I try to make images appear I get an error that says
0x6b8b230> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key column1.'
I got this so far but Im stuck and have no idea what to do.
#property (strong, nonatomic) NSArray *textvals;
#property (strong, nonatomic) NSArray *imagevals;
#synthesize textvals;
#synthesize imagevals;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSArray *totalArray = [[NSArray alloc] initWithObjects:#"Text 1",
#"text2", #"text 3", #"text 4", nil];
self.textvals = totaltextArray;
UIImage *image1 = [UIImage imageNamed:#"first.png"];
UIImage *image2 = [UIImage imageNamed:#"second.png"];
UIImage *image3 = [UIImage imageNamed:#"third.png"];
UIImage *image4 = [UIImage imageNamed:#"fourth.png"];
UIImageView *image1View = [[UIImageView alloc] initWithImage:image1];
UIImageView *image2View = [[UIImageView alloc] initWithImage:image2];
UIImageView *image3View = [[UIImageView alloc] initWithImage:image3];
UIImageView *image4View = [[UIImageView alloc] initWithImage:image4];
NSArray *totalimagearray = [[NSArray alloc] initWithObjects:image1View, image2View, image3View, image4View,nil];
//[self setValue:totalimagearray forKey:"1"];
self.imagevals = totalimagearray;
}
yes it is absolutely possible. u need to handle pickerView delegate method
(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row
forComponent:(NSInteger)component reusingView:(UIView *)view
here you simply return custom view(could be anything UIImage UILabel)
and set userInteractionEnable property to no for customize view..
I tried to change the image in the imageview but I can't seem to do that.
My code shown below creates a new subview for each image to display. However, the images just overlap and shows the overlapped images at the end which is not supposed to be.
.m:
for (int i = 0; i < [imageArr count]; i++) {
NSArray *imageNameArr = [[imageArr objectAtIndex:i] componentsSeparatedByString:#"."];
check=line;
NSString *msg = [textView.text stringByAppendingString:[NSString stringWithFormat:#"Opening image: %#\n",[imageArr objectAtIndex:i]]];
[textView performSelectorOnMainThread:#selector(setText:) withObject:msg waitUntilDone:NO];
line=line+1;
NSString *imagePath = [NSString stringWithFormat:#"%#/%#",jName,[imageArr objectAtIndex:i]];
NSString *imageFile = [NSHomeDirectory() stringByAppendingPathComponent:imagePath];
NSLog(#"------------------------------");
NSLog(#"ImageFile: %#\n",imageFile);
NSLog(#"Interger value: %i\n",i);
UIImage *myImage = [UIImage imageWithContentsOfFile:imageFile];
UIImageView *imageView = [[UIImageView alloc] initWithImage:myImage];
imageView.contentMode = UIViewContentModeScaleAspectFit;
[scrollView addSubview:imageView];
[imageView release];
.....
.....
}
How to show the images individually, without overlapping?
put this line out of for loop:
UIImageView *imageView = [[UIImageView alloc] initWithImage:myImage];
[scrollView addSubview:imageView];
and use it like:
UIImageView *imageView = [[UIImageView alloc] init];
[scrollView addSubview:imageView];
and use:
[imageView setImage:[UIImage imageWithContentsOfFile:fullImgNm]];
Inside for loop.
You create the imageview out of loop and change the source(Image) for that imageview inside the loop.
You do this before loop starts,
UIImageView *imageView = [[UIImageView alloc] init];
[scrollView addSubview:imageView];
and add the image like this inside the loop,
[imageView setImage:[UIImage imageWithContentsOfFile:imageFile]];
I have the following code which works fine UIScrollView. But when loading the picture can be seen only at the last subview and UIActivityIndicatorView not working for loading each picture.
How to be done for such a task?
myProject2ViewController.h
#import <UIKit/UIKit.h>
#import "PageScrollView.h"
#interface myProject2ViewController : UIViewController
{
NSMutableArray *pages;
PageScrollView *scrollView;
UIView *myOneSubview;
UIImageView *imageView;
UIActivityIndicatorView *myIndicator;
}
#property (nonatomic, retain) PageScrollView *scrollView;
#property (nonatomic, retain) UIImageView *imageView;
#property (nonatomic, retain) UIActivityIndicatorView *myIndicator;
#end
myProject2ViewController.m
#import "myProject2ViewController.h"
#implementation myProject2ViewController
#synthesize scrollView;
#synthesize imageView;
#synthesize myIndicator;
- (void)dealloc
{
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *imageUrls = [NSArray arrayWithObjects:
#"link_1",
#"link_2",
#"link_3",
nil];
pages = [[NSMutableArray alloc] init];
for (int i = 0; i < [imageUrls count]; i++) {
CGRect frameOne = CGRectMake(0.0f, 50.0f, 320.0f, 416.0f);
myOneSubview = [[UIView alloc] initWithFrame:frameOne];
myOneSubview.backgroundColor = [UIColor blackColor];
NSString *imageUrl = [imageUrls objectAtIndex:i];
myIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
myIndicator.center = CGPointMake(160.0f, 130.0f);
myIndicator.hidesWhenStopped = YES;
myIndicator.backgroundColor = [UIColor clearColor];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 50.0f, 320.0f, 416.0f)];
[NSThread detachNewThreadSelector:#selector(loadImages:) toTarget:self withObject:imageUrl];
[myOneSubview addSubview:imageView];
[pages addObject:myOneSubview];
}
scrollView = [[PageScrollView alloc] initWithFrame:self.view.frame];
scrollView.pages = pages;
scrollView.delegate = self;
self.view = scrollView;
}
- (void)loadImages:(NSString *)string
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[myIndicator startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:string]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[imageView setImage:image];
[image release];
[imageData release];
[self performSelectorOnMainThread:#selector(loadImageComplete) withObject:self waitUntilDone:YES];
[pool drain];
}
-(void)loadImageComplete
{
NSLog(#"Load image complete!");
[myIndicator stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
#end
When you do:
CGRectMake(0.0f, 50.0f, 320.0f, 416.0f);
Inside your for loop, you're specifying those same exact position and dimension for EVERY image you create in the for loop. In other words, every next image you create will sit ontop of the previous image, as a result, you will only see the final image you created in the for loop.
Try adding your counter "i" variable to the X and Y value in CGRectMake() to offset every image that gets subsequently created.
So let say each image was 100 pixel by 100 pixel, then something like:
CGRectMake(0.0f + (i * 100.0f), 50.0f, 320.0f, 416.0f);
Would make each image sit right after the previous one (since each image is 100 pixel wide, we offset it by 100 pixel and use that value as the starting point of the next image). If you add an extra value after (i * 100) so it becomes (i * 100 + 50) then that would give you a left padding of 50 pixels from the previous image.
If your images are different dimensions you'll need to calculate the width and height of the image before hand and factor those into your CGRectMake() method.
Hope that helps.
In addition, instead of creating so many views to add to your scroll view, you could write the code like so:
scrollView = [[UIScrollview alloc] init];
. . .
for(...)
{
UIImageView *currentImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f + (i * 100.0f), 50.0f, 320.0f, 416.0f);
[currentImage setImage:[UIImage imageNamed:[imageUrls objectAtIndex:i]]];
. . .
[scrollView addSubview: currentImage];
[currentImage release];
}