I have an app where a user drives a car and collects coins. I have an array with all the bombs in it that the user tries to avoid. Basically, the problem is that when I spawn the coins, they often land on bombs, making the game impossible. So, I wrote the following code to prevent this, but coins still spawn on bombs (note that these are all uiimageviews). Here is my code:
UIImageView *one = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"goldCoin.png"]];
CGRect rectOne = CGRectMake(arc4random() % (900), arc4random() % (700), 40, 40);
BOOL coinSpawned = false;
while (coinSpawned == false) {
rectOne = CGRectMake(arc4random() % (900), arc4random() % (700), 40, 40);
coinSpawned = true;
for (UIImageView *two in bombArray) {
if (CGRectIntersectsRect(rectOne,two.frame)) {
coinSpawned = false;
}
}
}
[one setFrame:rectOne];
[self.view addSubview:one];
levelCount = levelCount + 1;
coinFrame = one.frame;
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(coinIntersection:)
userInfo:one
repeats:YES];
Further note that *two are the uiimageviews in the array bombArray and coinFrame is a global variable that I don't really use.Also, levelcount is something I use later, but is irrelevant for this problem.
Maybe this is just a copy-and-paste issue, but it looks like your logic for setting the coin's frame is actually inside of your while loop, so it executes regardless of the value of coinSpawned. You might not have noticed this because your indents are off. Try closing the while loop right after the for loop and see if that changes anything.
EDIT: Just realized the indents are actually off within the for loop, and your braces do appear to be correct. There are a number of other factors that might be involved here. Are your bombs' frames ever changing, and you're not accounting for the change? Are the bombs hierarchical siblings of the coins (if they are in different superviews, the coordinate systems may be different)?
Also, just an aside: why are you using true and false instead of YES and NO? This probably doesn't have to do with your problem, but it's very non-standard.
Related
I have picked up programming as a hobby, so please bear with any 'old school' or completely wrong practices!
I am trying to move three images across the view using a timer. They then need to be dodged by another UIImageView. The obstacles are in an array:
objArray = [NSMutableArray arrayWithObjects:[UIImage imageNamed:#"img-1.png"],
[UIImage imageNamed:#"img-2.png"],
[UIImage imageNamed:#"img-3.png"], nil];
I then have a for loop to create these views, and it's here that I think the problems start.
count = [objArray count];
for(int i=0; i < count; i++)
{
NSLog (#"Element %i = %#", i, [objArray objectAtIndex: i]);
randX = arc4random_uniform(450);
randX = randx + 50;
randY = arc4random_uniform(236);
randY = randy + 45;
imgObj = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[objArray objectAtIndex:i]]];
imgObj.center = CGPointMake(randX, randY);
[self.view addSubview:imgObj];
[self startAnimation];
All three images display ok. The problem is getting these to move.
I understand the need for the timer, which is initiated in the startAnimation method. I have tried setting a tag in the for loop. However this always results in the last object "img-3.png", which then moves ok.
My question is: how do I differentiate the three views I create in the loop so I can call them elsewhere?
Assigning tags to you UIImageViews should work:
imgObj.tag = i+1; //skipping 0 here since other subviews may have this tag
Then you should be able to access the views via
[self.view viewWithTag:tag];
Or you could create another NSMutableArray and store the UIImageViews in it.
The code below takes a list of directions and reads them out using AVSpeechSynthesizer. Once complete, the user will be able to select a variable amount of time and the app will read out the instructions to fit the time span.
The problem is that when I press the play button, the delay between directions is significantly longer than it should be. Instead of the two minutes I've hardcoded it with, it takes over three. I've logged the value of all my postUtteranceDelays and they add up properly. It's also not due to processing time because when set postUtteranceDelay to 0 there is no pause between directions. I'm not sure what is going on.
- (IBAction)play:(UIButton *)sender {
[sender setTitle:#"Showering" forState:UIControlStateNormal];
Shower *shower = [[SpecificShower alloc] init];
NSUInteger totalRatio = [shower calculateTotalRatio:shower];
NSNumber *offset = #18.0; // estimated time to speak instructions combined
NSNumber *seconds = #120.0; // hard coded but just for testing
int totalSeconds = seconds.intValue - offset.intValue;
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
for (NSDictionary* direction in shower.directions) {
AVSpeechUtterance *aDirection = [[AVSpeechUtterance alloc] initWithString:direction[#"text"]];
NSNumber *directionLength = direction[#"length"];
aDirection.rate = .3;
aDirection.preUtteranceDelay = 0;
// totalRatio is calculated by adding all the lengths together
// then the individual direction length is divided by totalRatio
// and that fraction is multiplied by total number of seconds
// to come up with the postUtteranceDelay for each direction
aDirection.postUtteranceDelay = totalSeconds * [directionLength floatValue]/totalRatio;
NSLog(#"%f", aDirection.postUtteranceDelay);
[self.synthesizer speakUtterance:aDirection];
}
}
You're not alone. This seems to be a bug as noted with a workaround over here.
There are also radars filed here and here.
Let's hope this gets fixed soon.
I have a scrollview which automatically pass an image to another. It has 7 images, I want to that when you get to the seventh image pass me a first in the same way as the rest of transitions
and not moving quickly to the first position.
It is what is commonly called infinite scroll. Any help is appreciated.
- (void)viewDidLoad
{
for (int i = 1; i < 8 ; i++) {
UIImageView *imagen = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"C%d.png",i]]];
imagen.frame = CGRectMake((i-1)*580,35, 580, 300);
[_scroller addSubview:imagen];
_scroller.indicatorStyle = UIScrollViewIndicatorStyleWhite;
}
_scroller.delegate = self;
_scroller.contentSize = CGSizeMake(580*7, 300);
_scroller.pagingEnabled = YES;
if (scrollingTimer == nil)
{
scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:6
target:self selector:#selector(scrollPages) userInfo:nil repeats:YES];
}
}
-(void)scrollToPage:(NSInteger)aPage{
float myPageWidth = [_scroller frame].size.width;
[_scroller setContentOffset:CGPointMake (aPage * myPageWidth, 0) animated:YES];
}
-(void)scrollPages{
[self scrollToPage:currentPage%7];
currentPage++;
}
One way to implement infinite scolling in a paging UIScrollView is to put a duplicate of the first page at the end of the scroll view (and vice versa, if you want infinite scrolling in both directions).
This article uses a UICollectionView instead, but the concept is the same for a paging UIScrollView. When the user scrolls to the duplicate of the first page at the end of the scroll view, set the content offset back to the actual first page (without animation, so the user doesn't know it's happened).
Here's some other resources I found useful:
Creating Circular and Infinite UIScrollViews
Apple has a sample project demonstrating infinite scrolling called StreetScroller
This answer to a similar question about infinite scrolling on SO
I am adding about 3000 MKOverlays to my map, and as you can imagine, it takes a while, up to about eight seconds sometimes. I'm looking for a way to use threading to improve performance, so the user can move the map around while overlays are being added. Preferably, the overlays would be added sequentially, starting with the just the ones within the map's region. I have tried something along these lines with GCD:
- (MKOverlayView*)mapView:(MKMapView*)mapView viewForOverlay:(id)overlay {
__block MKPolylineView* polyLineView;
//do the heavy lifting (I presume this is the heavy lifting part, but
// because this code doesn't compile, I can't actually *test* it)
// on a background thread
dispatch_async(backgroundQueue, ^ {
polyLineView = [[[MKPolylineView alloc] initWithPolyline:overlay] autorelease];
[polyLineView setLineWidth:11.0];
//if the title is "1", I want a blue line, otherwise red
if([((LocationAnnotation*)overlay).title intValue]) {
[polyLineView setStrokeColor:[UIColor blueColor]];
} else {
[polyLineView setStrokeColor:[UIColor redColor]];
}
//return the overlay on the main thread
dispatch_async(dispatch_get_main_queue(), ^(MKOverlayView* polyLineView){
return polyLineView;
});
});
}
But because GCD blocks are defined with void parameter and return types, this code doesn't work- I get an incompatible pointer type error on the return line. Is there something I am missing here, or another way to thread this? Or perhaps an entirely different way to improve the performance of the overlay-adding process? I appreciate any and all help!
Edit:
I have found that the problem is not where I actually add the overlays here:
for(int idx = 1; idx < sizeOverlayLat; idx++) {
CLLocationCoordinate2D coords[2];
coords[0].latitude = [[overlayLat objectAtIndex:(idx - 1)] doubleValue];
coords[0].longitude = [[overlayLong objectAtIndex:(idx - 1)] doubleValue];
coords[1].latitude = [[overlayLat objectAtIndex:idx] doubleValue];
coords[1].longitude = [[overlayLong objectAtIndex:idx] doubleValue];
MKPolyline* line = [MKPolyline polylineWithCoordinates:coords count:2];
[line setTitle:[overlayColors objectAtIndex:idx]];
[mapViewGlobal addOverlay:line];
}
Adding all 3000 takes maybe 100ms here. The part that takes a long time (I assume) is where I actually create the overlays, in the first method I showed.
There is a little gap between what you want and what the compiler can do. When you are calling dispatch_async, you are actually telling the CPU "here, have this chunk of code, and run it whenever you feel like it, not now, not blocking my user interface thread". But, your method has to return now. There is simply no way for you to create anything in a background thread, because you are going to have to wait for it anyway before mapView:viewForOverlay: returns, since it has to return something.
This method is not the place to use GCD or any background code. If your problem is the addition of a big number of overlays at once, I would split all the overlays into chunks of say 100 and add them to the map with a delay of 100ms between each batch.
I am generating a simple scrollView with some images attached to buttons.
This works fine apart from the fact that this scroll view is taking rather much memory.
Since this scrollView is just a sub Menu allowing the user to pick an image and soon after I do not need it, I would like to free this heavy block from memory.
Can you kindly help me out understanding this issue, and free this huge memory block when not needed
int flipFlop = 1;
masksAvailable = 18;
float topMaskXX = 85.0;
float topMaskYY = 96.0;
UIButton *button;
for (int buttonsLoop = 1;buttonsLoop < masksAvailable+1;buttonsLoop++){
button = [UIButton buttonWithType:UIButtonTypeCustom];
NSString *tempname = [NSString stringWithFormat:#"mask_frame%i.png",buttonsLoop];
// This fellow there is the memory eating monster
[button setBackgroundImage:[UIImage imageNamed:tempname] forState:UIControlStateNormal];
tempname = nil;
button.tag = buttonsLoop;
[button addTarget:self action:#selector(handleMaskKeys:) forControlEvents:UIControlEventTouchUpInside];
UIImageView *frameForSubImages = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image_frame.png"]];
frameForSubImages.frame = CGRectMake(0.0, 0.0, 320.0/2.9, 480.0/2.9);
frameForSubImages.center = CGPointMake(topMaskXX,topMaskYY);
[scrollView addSubview:frameForSubImages];
button.frame = CGRectMake(0.0, 0.0, 320.0/3.4, 480.0/3.4);
button.center = CGPointMake(topMaskXX,topMaskYY);
if (flipFlop == 1){
topMaskXX += 150;
} else {
topMaskYY += 185.0;
topMaskXX = 85.0;
}
flipFlop = flipFlop * -1;
[scrollView addSubview:button];
}
First of all, I'd like to suggest that you do a "Clean All" and a "Build and Analyze" on your project. It is very good at pointing out problems with retain/release.
Secondly, any class that retains objects should define a "dealloc" that releases those objects to make sure they get deleted when the object is released.
-(void) dealloc {
// release all retained objects here.
[super dealloc];
}
Thirdly, in your example above, it also looks like frameForSubImages might have an extra retain on it, since you've allocated it (+1 reference) and assigned it to a view (+1 reference) without ever calling release (which would be -1 reference and leave you with a refcount of 1).
Finally, I would also recommend reading the Memory Management Programming Guide for iOS.