GCD Scrollview loading issue - objective-c

I'm using GCD to load images for my scroll view but rather than following the frames i created it makes only a single frame in which the images just flicker through. Here's my code:
//Create the array
-(void)awakeFromNib {
images = [NSMutableArray arrayWithObjects: [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"imageOne" ofType:#"png"]], [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"imageTwo" ofType:#"png"]], [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"imageThree" ofType:#"png"]],nil];
}
//Populate scrollview
-(void)populate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
scrollview.pagingEnabled = YES;
scrollview.backgroundColor = [UIColor clearColor];
scrollview.clipsToBounds = YES;
scrollview.delegate = self;
[scrollview setContentSize:CGSizeMake(2 * scrollview.frame.size.width, scrollview.frame.size.height)];
int number = [images count] ;
NSLog(#"%i",images);
int rowTwoInt = 0;
int rowThreeInt = 0;
int rowFourInt = 0;
for (int i = 0; i < number; i++) {
if (i <= 3) {
finalFrame = CGRectMake( i*(80), 12, 80, 80);
}
if (i == 4) {
finalFrame = CGRectMake(0, 95, 80, 80);
}
if ((i >4) && (i <= 7)) {
rowTwoInt = rowTwoInt + 80;
finalFrame = CGRectMake(rowTwoInt, 95, 80, 80);
}
if (i == 8) {
finalFrame = CGRectMake(0, 178, 80, 80);
}
if ((i > 8) && (i <= 11)) {
rowThreeInt = rowThreeInt + 80;
finalFrame = CGRectMake(rowThreeInt, 178, 80, 80);
}
if (i == 12) {
finalFrame = CGRectMake(0, 261, 80, 80);
}
if ((i > 12) && (i <= 15)) {
rowFourInt = rowFourInt + 80;
finalFrame = CGRectMake(rowFourInt, 261, 80, 80);
}
IconButton = [UIButton buttonWithType:UIButtonTypeCustom];
IconButton.frame = CGRectMake(0, 0, 57, 57);
IconButton.center = CGPointMake(40, 29);
[IconButton addTarget:self action:#selector(Selected:) forControlEvents:UIControlEventTouchUpInside];
IconButton.tag = i;
dispatch_async(queue, ^{
UIImage *icon = [images objectAtIndex:i];
dispatch_sync(dispatch_get_main_queue(), ^{
[IconButton setImage:icon forState:UIControlStateNormal];
});
});
[cell addSubview:IconButton];
cell = [[UIView alloc] initWithFrame:finalFrame];
[scrollview addSubview:cell];
So what am i doing wrong? I'm new with GCD so it's probably something with that. Also if you see a way to improve UIScrollView performance, please let me know.

I suspect you have declared IconButton as an instance variable, a static variable, or a global variable.
Each time through your loop, you change the value of IconButton. By the time your twelve queue blocks execute, IconButton is set to its final value - a reference to the last button you created. All the blocks use the instance (or static or global) IconButton variable, so all the blocks update the same, final button.
Make a local variable to reference the button, and use that local variable in the loop:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
IconButton = button;
IconButton.frame = CGRectMake(0, 0, 57, 57);
...
dispatch_async(queue, ^{
UIImage *icon = [images objectAtIndex:i];
dispatch_sync(dispatch_get_main_queue(), ^{
[button setImage:icon forState:UIControlStateNormal];
});
});
Each block will capture the value of the local variable at the moment the block is created, so each block will capture a reference to a different button.

Related

UIScrollView subview not always clickable

I'm adding subviews to an UIScrollView. For this question I'm simplifying the added view : testView it contains an UIButton
My Scroll View is working great, but the touch on the buttons is not working well.
I can click on the first button but only on the (approximative) 100 first pixels.
the scrolling is working very well.
I cannot click on the end of the first button
I cannot click on the other buttons
here is my code :
__block CGFloat scrollViewContentSize = 0;
__block CGFloat buttonRectOrigineY = 0;
[self.itemsToDisplay enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CGRect frameTest = CGRectMake(0, buttonRectOrigineY/2, 320, 200);
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testButton.frame = frameTest;
UIView *testView = [[UIView alloc] initWithFrame:frameTest];
[testView addSubview:testButton];
[self.articleScrollView addSubview:testView];
buttonRectOrigineY += 200;
scrollViewContentSize += 200;
}];
[self.articleScrollView setContentSize:CGSizeMake(320, scrollViewContentSize)];
here is image to understand well my problem :
You can use bringSubviewToFront to avoid the issue
CGRect frameTest = CGRectMake(0, buttonRectOrigineY/2, 320, 200);
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testButton.frame = frameTest;
UIView *testView = [[UIView alloc] initWithFrame:frameTest];
[testView addSubview:testButton];
[testView bringSubviewToFront:testButton];
[self.articleScrollView addSubview:testView];
[self.articleScrollView bringSubviewToFront:testView];
If you have issues again, apply boarder around the button and check it the button area that visible manually.
I was mistaking the frame of my button and view. Here a working code if it can help anyone.
__block CGFloat scrollViewContentSize = 0;
__block CGFloat buttonRectOrigineY = 0;
[self.itemsToDisplay enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CGRect frameTestButton = CGRectMake(0, 0, 300, 150);
CGRect frameTestView = CGRectMake(10, buttonRectOrigineY, 300, 200);
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testButton.frame = frameTestButton;
testButton.backgroundColor = [UIColor greenColor];
UIView *testView = [[UIView alloc] initWithFrame:frameTestView];
if(idx == 0)
testView.backgroundColor = [UIColor yellowColor];
if(idx == 1)
testView.backgroundColor = [UIColor orangeColor];
if(idx == 2)
testView.backgroundColor = [UIColor redColor];
if(idx == 3)
testView.backgroundColor = [UIColor magentaColor];
if(idx == 4)
testView.backgroundColor = [UIColor purpleColor];
if(idx <= 4){
LogDebug(#"idx : %lu", (unsigned long)idx);
[testView addSubview:testButton];
[self.articleScrollView addSubview:testView];
[self.articleScrollView bringSubviewToFront:testView];
buttonRectOrigineY += 200;
scrollViewContentSize += 200;
}
}];
[self.articleScrollView setContentSize:CGSizeMake(320, scrollViewContentSize)];

Setting UIButton Image Using AFNetworking

I have this code in my project that creates UIButtons with labels and images with values coming from a web service.
- (void)getMoviePosterImages
{
for (int i = 0; i < [self.rowCount intValue]; i++)
{
NSArray *postersArray = [self.jsonMutableArray objectAtIndex:i];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[postersArray objectAtIndex:6]]] imageProcessingBlock:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
[moviePosterButton setBackgroundImage:image forState:UIControlStateNormal];
}
failure:nil];
[operation start];
}
}
Here's the code where I created the UILabels:
- (void)displayMovieTitlesAndFrames
{
int imageCount = [self.rowCount intValue];
int offset_x = 21;
int offset_y = 24;
for (int i = 0; i < imageCount; i++)
{
// compute x & y offset
if (i % 3 == 0)
{
offset_x = 21;
}
else if (i % 3 == 1)
{
offset_x = 115;
}
else
{
offset_x = 210;
}
whiteBackgroundLabel = [[[UILabel alloc] initWithFrame:CGRectMake(offset_x, offset_y, MOVIE_THUMBNAIL_W, MOVIE_THUMBNAIL_H)] autorelease];
whiteBackgroundLabel.backgroundColor = [UIColor whiteColor];
whiteBackgroundLabel.layer.cornerRadius = 3.0;
whiteBackgroundLabel.layer.borderColor = [[UIColor lightGrayColor] CGColor];
whiteBackgroundLabel.layer.borderWidth = 1.0;
[scrollview addSubview:whiteBackgroundLabel];
moviePosterButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
moviePosterButton.frame = CGRectMake(offset_x + 5, offset_y + 5, MOVIE_THUMBNAIL_W - 10, MOVIE_THUMBNAIL_H - 10);
moviePosterButton.backgroundColor = [UIColor clearColor];
[moviePosterButton setBackgroundImage:[UIImage imageNamed:#"placeholder.png"] forState:UIControlStateNormal];
moviePosterButton.tag = i;
[moviePosterButton addTarget:self
action:#selector(imageButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[scrollview addSubview:moviePosterButton];
movieTitleLabel = [[[UILabel alloc] initWithFrame:CGRectMake(offset_x, offset_y + 143, 90, 20)] autorelease];
movieTitleLabel.backgroundColor = [UIColor whiteColor];
movieTitleLabel.layer.cornerRadius = 3.0;
movieTitleLabel.layer.borderColor = [[UIColor lightGrayColor] CGColor];
movieTitleLabel.layer.borderWidth = 1.0;
movieTitleLabel.font = [UIFont fontWithName:#"Helvetica" size:11.0];
movieTitleLabel.textColor = [UIColor darkGrayColor];
movieTitleLabel.textAlignment = UITextAlignmentCenter;
movieTitleLabel.numberOfLines = 1;
movieTitleLabel.text = [[self.jsonMutableArray objectAtIndex:i] objectAtIndex:1];
[scrollview addSubview:movieTitleLabel];
quickBuyButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
quickBuyButton.frame = CGRectMake(offset_x + 1 + (MOVIE_THUMBNAIL_W - QUICK_BUY_BUTTON_W - 2), offset_y - 1 + 2, QUICK_BUY_BUTTON_W, QUICK_BUY_BUTTON_H);
quickBuyButton.backgroundColor = [UIColor clearColor];
[quickBuyButton setBackgroundImage:QUICK_BUY_BUTTON forState:UIControlStateNormal];
quickBuyButton.tag = i;
[quickBuyButton addTarget:self
action:#selector(quickBuyButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[scrollview addSubview:quickBuyButton];
// increment offset
if (offset_x == 210)
{
offset_y += 171;
}
}
if ((offset_x == 21) || (offset_x == 115))
{
offset_y += 171;
}
[self adjustScrollViewAndBackground:offset_y];
}
All my UIButtons are created with their respective tags and I want to put their images on each on those buttons. The code above works only on the last UIButton created. I want to populate all of my buttons with images using the code above. Can someone tell me where I'm getting it all wrong?
In your getMoviePosterImages method you are setting an image to the moviePosterButton which might be the last button you just created so this is normal if only the last button gets its image. You need either to retrieve each buttons by their tags or build an array with all you buttons so you can easily access them. You could maybe do something like that:
- (void)getMoviePosterImages
{
for (int i = 0; i < [self.rowCount intValue]; i++)
{
NSArray *postersArray = [self.jsonMutableArray objectAtIndex:i];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[postersArray objectAtIndex:6]]] imageProcessingBlock:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
[(UIButton *)buttonsArray[i] setBackgroundImage:image forState:UIControlStateNormal];
// or
// [(UIButton *)[self.view viewWithTag:i] setBackgroundImage:image forState:UIControlStateNormal];
}
failure:nil];
[operation start];
}
}
Hope this helps ;)

UIActivityIndicator and GCD/Threading

i am fetching a list of photos from a server and displaying them. i'm using GCD to thread the server calls. I got it working, but i want to add in a UIActivityIndicator for each UIImageView to show that its doing something and will appear.
i'm not sure what the best method would be.
code:
UIScrollView *myScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, photoView.frame.size.height)];
myScrollView.backgroundColor = [UIColor whiteColor];
myScrollView.pagingEnabled = TRUE;
myScrollView.scrollEnabled = TRUE;
myScrollView.frame = CGRectMake(myScrollView.frame.origin.x, myScrollView.frame.origin.y, (6 * THUMBNAIL_SIZE) + (PHOTO_VIEWER_OFFSET_COLUM_1 * 2), myScrollView.frame.size.height);
//get list of photos
for (int i = 0; i < 6; i++)
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(#"async thread %#", [NSThread currentThread]);
//retrieveThumbnailedGeotaggedPhotoWithPhotoID will make a server call
MyUIImageView *myImageView = [[MyUIImageView alloc] initWithImage:[self retrieveThumbnailedGeoTaggedPhotoWithPhotoID:#"test"]];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"main thread %#", [NSThread currentThread]);
myImageView.frame = CGRectMake(myImageView.frame.origin.x, myImageView.frame.origin.y, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
myImageView.backgroundColor = [UIColor clearColor];
myImageView.position = i;
myImageView.isEnlarged = FALSE;
myImageView.delegate = self;
int group = (i / 6);
myImageView.frame = CGRectMake((i * myImageView.frame.size.width) + (PHOTO_VIEWER_OFFSET_COLUM_1 * ((group * 2) + 1)),
PHOTO_VIEWER_OFFSET_COLUM_1,
myImageView.frame.size.width,
myImageView.frame.size.height);
myScrollView.contentSize = CGSizeMake((myScrollView.frame.size.width * (group + 1)), myScrollView.contentSize.height);
[myScrollView addSubview:myImageView];
});
});
}
i'm pretty sure animating and stopping the activityIndicator needs to be on the main thread, but not sure how to include it. it needs to be animating during the "retrieveThumbnailedGeoTaggedPhotoWithPhotoID" method which is in a thread (but not main thread).
k, i re-ordered it:
for (int i = 0; i < 8; i++)
{
//create the subviews first
MyUIImageView *myImageView = [[MyUIImageView alloc] initWithFrame:CGRectMake((i * THUMBNAIL_SIZE) + (PHOTO_VIEWER_OFFSET_COLUM_1 * (((i / 6) * 2) + 1)),
PHOTO_VIEWER_OFFSET_COLUM_1,
THUMBNAIL_SIZE,
THUMBNAIL_SIZE)];
myImageView.backgroundColor = [UIColor whiteColor];
[myScrollView addSubview:myImageView];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.center = CGPointMake(THUMBNAIL_SIZE / 2, THUMBNAIL_SIZE / 2);
[myImageView addSubview:spinner];
[spinner startAnimating];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
UIImage *myImage = [[UIImage alloc] init];
//only make the server call in the async queue
myImage = [self retrieveThumbnailedGeoTaggedPhotoWithPhotoID:#"test"];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"main thread %#", [NSThread currentThread]);
//MyUIImageView *myImageView = [[MyUIImageView alloc] initWithImage:myImage];
[myImageView setImage:myImage];
myImageView.frame = CGRectMake(myImageView.frame.origin.x, myImageView.frame.origin.y, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
myImageView.backgroundColor = [UIColor clearColor];
myImageView.position = i;
myImageView.isEnlarged = FALSE;
myImageView.delegate = self;
int group = (i / 6);
myImageView.frame = CGRectMake((i * myImageView.frame.size.width) + (PHOTO_VIEWER_OFFSET_COLUM_1 * ((group * 2) + 1)),
PHOTO_VIEWER_OFFSET_COLUM_1,
myImageView.frame.size.width,
myImageView.frame.size.height);
myScrollView.contentSize = CGSizeMake((myScrollView.frame.size.width * (group + 1)), myScrollView.contentSize.height);
[spinner stopAnimating];
});
});
}

Programmatically create UITextFields

I can't figure out what is wrong here. Please see coments above NSLog function.
-(void)loadView
{
......
int x_position = 10;
for (self.x = 0; self.x < 3; self.x++)
{
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(10, x_position, 300, 25)];
self.textField.tag = self.x;
// Output 0, 1, 2
NSLog(#"%d", self.x);
x_position += 40;
[self.view addSubview:self.textField];
}
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self action:#selector(showNames) forControlEvents:UIControlEventTouchDown];
[btn setTitle:#"Remove from view" forState:UIControlStateNormal];
btn.frame = CGRectMake(0, x_position + 30, 210, 50);
[self.view addSubview:btn];
[self.textField release];
[self.view release];
}
-(void)showNames
{
while (self.x > 0) {
self.x--;
// output 2, 1, 0
NSLog(#"%d", self.x);
NSLog(#"%#", tmp);
}
}
Here is console log
<UITextField: 0x4b39410; frame = (10 90; 300 25); text = 'Ad'; clipsToBounds = YES; opaque = NO; tag = 2; layer = <CALayer: 0x4b38c30>>
<UITextField: 0x4e22320; frame = (10 50; 300 25); text = 'Asd'; clipsToBounds = YES; opaque = NO; tag = 1; layer = <CALayer: 0x4e0a4c0>>
<UIView: 0x4b32330; frame = (0 20; 320 460); layer = <CALayer: 0x4b329a0>>
I expect object at tag 0 to be UITextField, not UIView.
What is wrong here ?
Every view's tag defaults to zero, so your main UIView will have a tag of zero, and so will any other view where you didn't explicitly set the tag.
I suggest using an offset value for your tags, so you can make them all unique. For example:
#define TEXTFIELD_TAG_OFFSET 100
for (self.x = 0; self.x < 3; self.x++)
{
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(10, x_position, 300, 25)];
self.textField.tag = self.x + TEXTFIELD_TAG_OFFSET;
// Output 0, 1, 2
NSLog(#"%d", self.x);
x_position += 40;
[self.view addSubview:self.textField];
}
Now you can reference the Nth textfield with the tag number TEXTFIELD_TAG_OFFSET + N. That way your textfields will all have unique tags.

Image not stored in an NSMutableArray

i am trying to store image in NsMutable Array.But it not working in that function. But eventually in other function it work properly.can someone tell me why it is not working.
- (void)readPlistData {
objectButtonArray = [[NSMutableArray alloc] init]; \\Use to store Buttons
editButtonArray = [[NSMutableArray alloc] init]; \\ another array use to store deletebutton(such as.crossmark button.)
selectImage = [[NSMutableArray alloc] init];
self.myPlistPath = [NSString stringWithString:[self plistPath]];
plistArray = [[NSMutableArray alloc] initWithContentsOfFile:self.myPlistPath];
for (int i =0; i< [plistArray count];i++)
{
UIGraphicsEndImageContext();
NSData *imageData = [plistArray objectAtIndex:i];
currentObjectImage = [UIImage imageWithData:imageData] ;
[selectImage addObject:currentObjectImage]; \\value not stored in selectimage NSMutableArray
CGRect imageSize = CGRectMake(0, 0, 100, 90);
UIGraphicsBeginImageContext(imageSize.size); // this will crop
[currentObjectImage drawInRect:imageSize];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
imageButton = [[UIButton alloc]initWithFrame:CGRectMake(width, 0, 100, 90)];
[imageButton setTag:tag];
[imageButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[imageButton setImage:newImage forState:UIControlStateNormal];
CGRect backFrame = backView.frame;
backFrame.size.width = 120+width;
backView.frame = backFrame;
[backView addSubview:imageButton];
editButton = [[UIButton alloc]initWithFrame:CGRectMake(width-10, -10, 35, 35)];
[editButton setImage:[UIImage imageNamed:#"DeleteButton.png"] forState:UIControlStateNormal];
[editButton addTarget:self action:#selector(deleteObjectViewImage:) forControlEvents:UIControlEventTouchUpInside];
editButton.hidden = YES;
[editButton setTag:tag];
[backView addSubview:editButton];
tag++;
width = 120 + width;
[objectButtonArray addObject:imageButton];
[editButtonArray addObject:editButton];
}
scrollView.contentSize = backView.bounds.size;
}
can someone help me.
I think the problem is Reference name "currentObjectImage" basically when you are doing
[selectImage addObject:currentObjectImage];
it saves the reference of "currentObjectImage", where in the next iteration next image is loaded in it which creates problem.
Replace above line with
[selectImage addObject:[currentObjectImage copy]];
I think it will solve your problem.