EXC_BAD_ACCESS in for loop - iphone

In this code, in the second for loop, i get an EXC_BAD_ACCESS if I use an identifier other than i and j.
if (UIDeviceOrientationIsPortrait(deviceOrientation)){
numRows = 4;
numCols = 3;
}else {
numRows = 6;
numCols = 1;
}
for (int row = 0; row < numRows; row++){
for (int col = 0; col < numCols; col++) {
keysArray[row][col] = [[[keys objectAtIndex:0] retain] autorelease];
if (col < numRows)
[keys removeObjectAtIndex:0];
}
}
//Here is the crash
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++){
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
b.frame = CGRectMake(i * kKeyGap, j * kKeyGap, 57, 57);
[b setImage: [UIImage imageNamed:keysArray[j][i]]
forState:UIControlStateNormal];
[self.view addSubview:b];
}
}
Why would this cause such an error? I tried using Edit All In Scope to avoid missing one, but it still crashes.
Thanks

Your line:
keysArray[row][col] = [[[keys objectAtIndex:0] retain] autorelease];
I'm not an Objective-C expert or anything, but I'm not certain you need to autorelease that object because you haven't allocated memory for it. What happens when you try and remove that autorelease?
My suspicion is that after you autorelease the object and then attempt to assign the array value here:
[b setImage: [UIImage imageNamed:keysArray[j][i]]
forState:UIControlStateNormal];
you're getting the EXC_BAD_ACCESS.
I could be wrong, in which case I hope someone smarter than me can clear this up. :)

In your first loop you are referencing keysArray[row][col] in the second loop you seem to be using keysArray[col][row] which would cause it to crash if numRows and numCols are different

Related

Shuffle the positions of a row of buttons in a UIView

I have a word,depending on its length,I create buttons assigning a character from the word randomly.I am presenting the shuffled character button arrangement in a row.Now I would like to sort the positions of those buttons,i.e. 1st to 3rd,3rd to 5th and so on.Please note that the number of buttons are not fixed,they vary depending on word length.Here is the sample code snippet for an understanding of how I create buttons.
-(void)buttonsCreation:(int)noOfButtons
{
NSMutableArray *charactersArray = [NSMutableArray array];
self.wordButtons = [[NSMutableArray alloc]initWithCapacity:30];
BOOL record = NO;
int randomNumber;
self.jumbledWord = [jumbledWord stringByReplacingOccurrencesOfString:#" " withString:#""];
for (int i=0; [charactersArray count] < jumbledWord.length; i++) //Loop for generate different random values
{
randomNumber = arc4random() % jumbledWord.length;//generating random number
if(i==0)
{
[charactersArray addObject:[NSNumber numberWithInt:randomNumber]];
}
else
{
for (int j=0; j<= [charactersArray count]-1; j++)
{
if (randomNumber ==[[charactersArray objectAtIndex:j] intValue])
record = YES;
}
if (record == YES)
record = NO;
else
[charactersArray addObject:[NSNumber numberWithInt:randomNumber]];
}
}
int arrayValue;
float x;
float y;
if ([jumbledWord length]>11)
{
int remainingWordslength = [jumbledWord length]-11;
x = ((475/2) - (38.0 *(11/2.0))) + 8;
y = ((475/2) - (38.0 *(remainingWordslength/2.0))) + 8;
}
else
{
x = ((475/2) - (38.0 *([jumbledWord length]/2.0))) + 8;
}
for(int i=0;i<[jumbledWord length];i++)
{
CGFloat translation = 0.0;
if(i>=0 && i<11)
{
arrayValue = [[charactersArray objectAtIndex:i] intValue];
UIButton *characterButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
characterButton.frame = CGRectMake(x,160.0, 35.0, 35.0);
x = x + 38;
if (jumbledWord.length>=1 && jumbledWord.length<5)
{
translation = (self.view.frame.size.width - characterButton.frame.size.width)+65.0;
}
if (jumbledWord.length>=5 && jumbledWord.length<11)
{
translation = (self.view.frame.size.width - characterButton.frame.size.width)+160.0;
}
characterButton.transform = CGAffineTransformMakeTranslation(translation, 0);
[UIView animateWithDuration:1.30f
delay:0.7
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
characterButton.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
}];
[wordButtons addObject:characterButton];
NSString *characterValue = [NSString stringWithFormat:#"%c",[jumbledWord characterAtIndex:arrayValue]];
[characterButton setTitle:characterValue forState:UIControlStateNormal];
[characterButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
if (arrayValue==0)
{
arrayValue = 100;
}
[characterButton setTag:arrayValue*100];
[self.view addSubview:characterButton];
}
else if(i>=11 && i<20)
{
arrayValue = [[charactersArray objectAtIndex:i] intValue];
UIButton *characterButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
characterButton.frame = CGRectMake(y,200.0, 35.0, 35.0);
y = y + 38;
if (jumbledWord.length>=11 && jumbledWord.length<15)
{
translation = (self.view.frame.size.width - characterButton.frame.size.width)+70.0;
}
if (jumbledWord.length>=15 && jumbledWord.length<=20)
{
translation = (self.view.frame.size.width - characterButton.frame.size.width)+150.0;
}
characterButton.transform = CGAffineTransformMakeTranslation(translation, 0);
[UIView animateWithDuration:1.30f
delay:0.7
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
characterButton.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
}];
[wordButtons addObject:characterButton];
NSString *characterValue = [NSString stringWithFormat:#"%c",[jumbledWord characterAtIndex:arrayValue]];
[characterButton setTitle:characterValue forState:UIControlStateNormal];
[characterButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
if (arrayValue==0)
{
arrayValue = 100;
}
[characterButton setTag:arrayValue*100];
[self.view addSubview:characterButton];
}
}
}
I do acknowledge the ideal choice would be to assign the frame i.e. "x" positions for buttons,but how do I do this.The other option being exchanging the index positions and I tried it too,i.e.:
-(void)shuffleButtons
{
for (UIButton *characterButton in wordButtons)
{
id sortObject1 = [self.wordButtons objectAtIndex:0];
id sortObject1 = [self.wordButtons objectAtIndex:1];
id sortObject1 = [self.wordButtons objectAtIndex:2];
[self.wordButtons removeObjectAtIndex:0];
[self.wordButtons removeObjectAtIndex:1];
[self.wordButtons insertObject:characterButton atIndex:1];
[self.wordButtons insertObject:characterButton atIndex:0];
[self.wordButtons removeObjectAtIndex:2];
[self.wordButtons insertObject:characterButton atIndex:1];
}
}
OOPS and it crashes,any one please guide me a way.I need to accomplish because there is an option for user to shuffle the jumbled arrangement of a word.
Thanks in advance :)
I've already developed a sample project for this.
Take a look at DragME source over github.
Since we are having all the buttons in wordButtons array,we can make use of those index locations and exchange their places with a little animation ;) ,i.e.
- (void)exchangeButtons
{
UIButton *firstButton = self.wordButtons[0];
UIButton *lastButton = self.wordButtons[self.wordButtons.count - 1];
UIButton *secondButton = self.wordButtons[1];
UIButton *thirdButton = self.wordButtons[2];
[self exchangeButton:firstButton withAnother:lastButton];
[self exchangeButton:secondButton withAnother:thirdButton];
}
//Animate buttons while interchanging positions
- (void)exchangeButton:(UIButton *)firstButton withAnother:(UIButton *)secondButton
{
CGRect firstButtonFrame = firstButton.frame; [UIView animateWithDuration:1.0f animations:^{ firstButton.frame = secondButton.frame; secondButton.frame = firstButtonFrame; }];
}
Please note that the exchange button code may vary,it all depends on the length of word i.e. number of buttons.So you may place conditions like if(wordButtons.count >= 6) or >=10 etc. before you exchange the button indices.
P.S. -- A more effective and non-hard coded solution
Let's store the frame of the button while creating a row of buttons and use that to interchange the button positions i.e. while creation assign our button frame to declared CGRect object,i.e.
CGRect originalFrameArray[16]; --> **Global variable**
originalFrameArray[i] = characterButton.frame;
Now let's create 2 arrays,one for shuffling the word and storing the character buttons indices,other for modifying the buttons array and then start exchanging the buttons with little animation :)
- (void)exchangeButtons
{
NSMutableArray *charactersArray = [NSMutableArray array];
NSMutableArray *copyOfButtons = [NSMutableArray arrayWithArray:wordButtons];
BOOL record = NO;
int randomNumber;
for (int i=0; [charactersArray count] < jumbledWord.length; i++) //Loop for generate different random values
{
randomNumber = arc4random() % jumbledWord.length;//generating random number
if(i==0)
{
[charactersArray addObject:[NSNumber numberWithInt:randomNumber]];
}
else
{
for (int j=0; j<= [charactersArray count]-1; j++)
{
if (randomNumber ==[[charactersArray objectAtIndex:j] intValue])
record = YES;
}
if (record == YES)
record = NO;
else
[charactersArray addObject:[NSNumber numberWithInt:randomNumber]];
}
}
int arrayValue;
for(int i=0;i<[jumbledWord length];i++)
{
arrayValue = [[charactersArray objectAtIndex:i] intValue];
[wordButtons replaceObjectAtIndex:arrayValue withObject:[copyOfButtons objectAtIndex:i]];
}
for(int i=0; i < [jumbledWord length]; i++)
{
UIButton *characterButton = [wordButtons objectAtIndex:i];
[self shiftButtonToFrame:characterButton :originalFrameArray[i]];
}
}
//Animate the buttons while they exchange positions
- (void) shiftButtonToFrame:(UIButton *) characterButton :(CGRect) frame
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:1.0];
[characterButton setFrame:frame];
[UIView commitAnimations];
}
Hope it helps some one.Thanks and happy coding :)

I can not hide the button and set image before the function finish

I want to hide the button loadImgButton when I click it. This button will call the method below:
- (IBAction)produceImage:(id)sender
{
[loadImgButton setHidden:YES];
[image1 setImage:[UIImage imageNamed:#"Fanny2.JPG"]];
[image2 setImage:[UIImage imageNamed:#"Fanny3.JPG"]];
NSLog(#"i am here");
for (int i = 0; i < 100000; i++) {
for(int j = 0;j < 10000; j++) {
}
}
}
The problem is that the button is not hidden and the image is not set until the function is finish. What is the reason cause this situation and how to fix it? Thanks!
Updates to the UI happen in the run loop, methods such as setHidden are simply setting flags so that the UI can be updated later. You might want to look at "Is there a way to make drawRect work right NOW?" for ways to force the UI to be updated immediately. However, you might be better thinking about other ways to achieve the result you require.
- (IBAction)produceImage:(id)sender
{
[loadImgButton setHidden:YES];
// Call another function which download image with delay
[self performSelector:#selector(image:) withObject:sender afterDelay:2];
}
- (IBAction)image:(id)sender
{
[image1 setImage:[UIImage imageNamed:#"Fanny2.JPG"]];
[image2 setImage:[UIImage imageNamed:#"Fanny3.JPG"]];
NSLog(#"i am here");
for (int i = 0; i < 100000; i++) {
for(int j = 0;j < 10000; j++) {
}
}
}
You probably want to use an animation block so that you can run your function on its completion. The problem with your code is that I believe that setHidden just sends a request that gets executed when the system comes around to it. That's why it is running your function first. Try this:
[UIView animateWithDuration:0.1 animations:^{
[loadImgButton setHidden:YES];
}
completion:^ (BOOL finished) {
for (int i = 0; i < 100000; i++) {
for(int j = 0;j < 10000; j++) {
}
}
}];
If your button is not hidden that once you check your nib file and check out your button object is connected with file onwer or not?
Create 1 new method.
- (IBAction)produceImage:(id)sender
{
[loadImgButton setHidden:YES];
[self new];
}
-(void)new
{
[image1 setImage:[UIImage imageNamed:#"Fanny2.JPG"]];
[image2 setImage:[UIImage imageNamed:#"Fanny3.JPG"]];
NSLog(#"i am here");
for (int i = 0; i < 100000; i++) {
for(int j = 0;j < 10000; j++) {
}
}
}
Try this
-(IBAction)buttonPressed:(id)sender{
[(UIButton*)sender performSelectorInBackground:#selector(setHidden:) withObject:[NSNumber numberWithBool:YES]];
NSLog(#"heeere");
for (int i = 0; i < 100000; i++) {
for(int j = 0;j < 10000; j++) {
}
}
}

Access position of UIButton objects in Array

I have an array of UIButtons. What I want to do is with another button, randomly set the positions of each of the buttons in the array.
So I initialize the array with the UIButtons:
buttonArray = [[NSMutableArray alloc] initWithObjects:button1,button2,button3,button4,button5,button6,button7, nil];
Then I have a randomize method to set each of the button's positions.
this is the part where I'm stuck. I've found some threads about having to cast the type of the object in the array so the compiler understands. but I can't seem to make it work.
- (IBAction)randomizePositions:(id)sender
{
for (int i = 0; i < [buttonArray count]; ++i)
{
float xPos = arc4random() % 1000;
float yPos = arc4random() % 700;
CGRect randomPosition = CGRectMake(xPos, yPos, button1.frame.size.width, button1.frame.size.width);
(UIButton *)[buttonArray objectAtIndex:i].frame = randomPosition;
}
}
It's this part here that I can't seem to get right. It's pretty obvious by now that I'm a beginner so any help would be much appreaciated.
(UIButton *)[buttonArray objectAtIndex:i].frame = randomPosition;
You may want to grab a pointer to the UIButton in the array earlier, as it might be easier to consider what you are working with.
- (IBAction)randomizePositions:(id)sender
{
for (int i = 0; i < [buttonArray count]; ++i)
{
UIButton *currentButton = (UIButton *)[buttonArray objectAtIndex:i];
float xPos = arc4random() % 1000;
float yPos = arc4random() % 700;
[currentButton setFrame:CGRectMake(xPos, yPos, currentButton.frame.size.width, currentButton.frame.size.height)];
}
}
Unless, of course, you want to always be working with button1 size.

How to send a time-delayed message to a UIView on a timer

I am adding UIViews to a superview in the following loop:
int xValue = 0 ;
int yValue = 10;
for (NSString *element in characters) {
UIView *newCharView = [[MyChar alloc] initWithFrame:CGRectMake(xValue,yValue,100,100)];
[(MyChar *)newCharView initLayers:element];
[[self view] addSubview:newCharView];
[newCharView autorelease];
if (xValue <= 240) {
xValue+=22;
} else {
xValue = 0;
yValue += 22;
}
}
MYChar contains a method which fires an animation. I want the method to be called sequentially in my loop with a delay of, say, 0.2 seconds. How can I achieve this? I tried delaying the animation itself but obviously just get the same delay in all the UIViews.
You could try using something like this:
int xValue = 0 ;
int yValue = 10;
double delay = 0.2;
for (NSString *element in characters) {
UIView *newCharView = [[MyChar alloc] initWithFrame:CGRectMake(xValue,yValue,100,100)];
[self performSelector:#selector(delayedInitView:) withObject:element afterDelay:delay];
if (xValue <= 240) {
xValue+=22;
} else {
xValue = 0;
yValue += 22;
}
delay += 0.2;
}
-(void) delayedInitView: (id) element {
[(MyChar *) newCharView initLayers: element];
[[self view] addSubview: newCharView];
[newCharView autorelease];
}
You will have to experiment with that, as I don't know what exactly you are trying to achieve.
EDIT:
Ouch, it's definitely not a good way to go with such a large amount of views. You could try using one background loop to init all of them.
I would definitely have to think it over a lil more if I personally faced that problem, but here's a simple suggestion, you will have to tune it to your needs and probably take care of its thread/memory management safety:
[self performSelectorInBackground:#(backgroundInitViews:) withObject:characters]
-(void) backgroundInitViews: (NSArray*) elements {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
int xValue = 0 ;
int yValue = 10;
for (NSString *element in elements) {
UIView *newCharView = [[MyChar alloc] initWithFrame:CGRectMake(xValue,yValue,100,100)];
[(MyChar *) newCharView initLayers: element];
[[self view] addSubview: newCharView];
[newCharView autorelease];
if (xValue <= 240) {
xValue+=22;
} else {
xValue = 0;
yValue += 22;
}
[NSThread sleepForTimeInterval:0.2];
}
[pool drain];
}
Give this a try:
[self performSelector:#selector(postAnimationMethod) withObject:self afterDelay:0.2];

Objective C speeding up animation / object creation

What kind of trick can I use to speed up and improve an image sequence animation?
I'm using the code below, but then just running through the codes below takes a couple of seconds to finish.
Once the animations have been created, my phone seems to get much slower too.
Please enlight.
Thanks,
Tee
NSMutableArray * sequenceArray;
sequenceArray = [[NSMutableArray alloc] init];
int k;
int xPos = 0;
int yPos = 50;
for (k = 1; k <= 48; k++)
{
UIImageView* dynamicView = [[UIImageView alloc] initWithFrame:self.view.frame];
[sequenceArray addObject:dynamicView];
int i;
NSMutableArray *a = [NSMutableArray array];
for (i = 1; i <= 102; i++)
{
[a addObject:[UIImage imageNamed:[NSString stringWithFormat:#"loop2 %d.png", i]]];
}
dynamicView.animationImages = a;
//[a release];
// all frames will execute in 1.75 seconds
dynamicView.animationDuration = 1.6;
// repeat the annimation forever
dynamicView.animationRepeatCount = 0;
CGRect frame1 = dynamicView.frame;
frame1.size.width = 34;
frame1.size.height = 34;
frame1.origin.x = xPos;
frame1.origin.y = yPos;
dynamicView.frame = frame1;
if (k % 8 == 0) {
xPos = 0;
yPos += 40;
}
xPos += 40;
NSLog(#"step last");
// start animating
[dynamicView startAnimating];
// add the animation view to the main window
[self.view addSubview:dynamicView];
[dynamicView release];
}
So, you're creating 48 UIImageViews, each with an animation of the same 102 frames? Am I reading that right?
The first thing that occurs to me is to pull this out of the other loop, since it's loading the same array each time, and just assign it to each view:
int i;
NSMutableArray *a = [NSMutableArray array];
for (i = 1; i <= 102; i++)
{
[a addObject:[UIImage imageNamed:[NSString stringWithFormat:#"loop2 %d.png", i]]];
}
But, really, why are you doing this?