CDVViewController still executing JS after parent has been removed - objective-c

I have attempted to insert a Cordova CleaverView (CDVViewController) into a UIViewController(SenchaViewController) instantiated from a storyboard in order to render a javascript Sencha based view.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (![ self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
CDVViewController *cdvc = [CDVViewController new];
cdvc.wwwFolderName = #"www";
cdvc.startPage = #"sencha.html";
cdvc.useSplashScreen = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
cdvc.view.frame = CGRectMake(0, 0, 768, 1004);
} else {
cdvc.view.frame = CGRectMake(0, 0, 320, 460);
}
[self.view addSubview:cdvc.view];
[self.view bringSubviewToFront:self.menuButton];
[self.view bringSubviewToFront:self.favButton];
cdvc = nil;
NSString *jsSetIdString = [NSString stringWithFormat:#"fetchGuid = function(){return '%#';}", [player valueForKey:#"playerID"]];
[[cdvc webView] stringByEvaluatingJavaScriptFromString:jsSetIdString];
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
}
My problem is after I remove the parent ViewController(SenchaViewController) which owns the CDVViewController's view instance, the CDVViewController does not get released and is still running in the background as I can still see javascript logging in the console. The causes for me to have several CDVViewController all running at once.
This is how the app adds the Parent View Controller:
- (void)renderPlayer:(NSNotification*)notification {
SenchaViewController* newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SenchaView"];
NSDictionary *player = notification.object;
// Set active player
if( player != nil && [player valueForKey:#"playerID"] != #"" ){
// Attach Player Dictionary to Sencha View
newTopViewController.player = player;
// Send it to the top
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = newTopViewController;
self.slidingViewController.topViewController.view.frame = frame;
}
This is how the Parent View controller which owns the CDVController is being removed
_topViewController.view removeFromSuperview];
[_topViewController willMoveToParentViewController:nil];
[_topViewController removeFromParentViewController];
Am I missing something? I was assuming ARC would dealloc this for me

Cordova has some dirty aspects, and this is one of them. Removing a CDVViewController is not enough to get it released. You have to also call the [controller dispose] method.

Related

View not receiving touches after custom spinner is invoked

I followed the guide here to make my own custom view pop up to indicate I am searching for the users results:
http://joris.kluivers.nl/blog/2012/03/02/custom-popups-revisited/
When the search results are found, I dismiss the custom view, an a Flurry Ad pops up immediately. I click the x button on the ad and my next view appears. This view is not receiving any touch events anymore, neither is the tab bar at the bottom working. I suspect it has to do something with the custom view still animating away(via a transform to 0.0f) when the Flurry ad is coming up, but I am not sure. I am including the code for how the spinner is created, as well as the dismissal.
Note: I have confirmed the problem is related to Flurry Ads showing up modally while my customSpinner is going away. I don't know how to fix it though. When I don't display the Ad, the touch events are registering fine.
PCFFinder.c
- (void)queryServer:(NSString *)queryString {
__block PCFCustomSpinner *spinner = [[PCFCustomSpinner alloc] initWithFrame:CGRectMake(0, 0, 200, 200):#"Searching.."];
[spinner show];
NSString *url = #"https://selfservice.mypurdue.purdue.edu/prod/bwckschd.p_get_crse_unsec";
NSString *referer = #"https://selfservice.mypurdue.purdue.edu/prod/bwckgens.p_proc_term_date";
dispatch_queue_t task = dispatch_queue_create("Task 3", nil);
dispatch_async(task, ^{
NSString *webData = [PCFWebModel queryServer:url connectionType:#"POST" referer:referer arguements:queryString];
if (webData) {
classesOffered = [PCFWebModel parseData:webData type:2];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner dismiss];
spinner = NULL;
if ([classesOffered count] > 0) {
if (self.view.window)
searching = NO;
//the ad pops up in the next view controllers viewWillAppear method and after exiting the ad, the views no longer work
[self performSegueWithIdentifier:#"ChooseClass" sender:self];
}else {
[spinner dismiss];
PCFCustomAlertView *alert = [[PCFCustomAlertView alloc] initAlertView:CGRectMake(0, 0, 300, 200) :#"Search Results" :#"No results were found. Please try broadening the search or double check your input." :#"OK"];
[alert show];
//the views still work here though..
searching = NO;
}
});
//rest omitted because it is irrelevant.
}
CustomPopup.c
-(void)dismiss
{
__block UIWindow *animatedWindow = self.window;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationCurveEaseOut animations:^{
animatedWindow.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
}completion:^(BOOL finished) {
animatedWindow.hidden = YES;
animatedWindow = nil;
}];
}
-(void)show
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.windowLevel = UIWindowLevelAlert;
self.window.backgroundColor = [UIColor clearColor];
self.center = CGPointMake(CGRectGetMidX(self.window.bounds), CGRectGetMidY(self.window.bounds));
[self.window addSubview:self];
[self.window makeKeyAndVisible];
//animated
self.window.transform = CGAffineTransformMakeScale(1.5f, 1.5f);
self.window.alpha = 0.0f;
__block UIWindow *animationWindow = self.window;
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationCurveEaseIn animations:^{
animationWindow.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
animationWindow.alpha = 1.0f;
}completion:nil];
}
I have tried setting the view as first responder in the viewWillAppear method, but that doesn't work either.
In your dismiss method you are setting the single window to have a zero scale and hidden. This is surely your problem. Animate the pop ups view then remove it from the super view instead.

Mapview crashes in iOS 6

I'm having a problem when I'm loading map view in iOS 6. But it's working 100% with previous OS versions.
Here's the code:
//.h file
MKMapView *galleryListMap; //map view
NSMutableArray *copyOfAllRecords; //array with locations
And here is the code structure in my .m file. I have synthesized variables and released them in dealloc method.
//this will runs in a background thread and populateMap method will make all the annotations
//[self performSelectorInBackground:#selector(populateMap) withObject:nil];
//here is populateMap method
-(void)populateMap{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSArray *existingpoints = galleryListMap.annotations;
//removes alll the existing points
[galleryListMap removeAnnotations:existingpoints];
//assign point to map view
for(int i = 0; i < [copyOfAllRecords count]; i++) {
Gallery *gallery = (Gallery *)[copyOfAllRecords objectAtIndex:i];//array which has the elements
//create location based on the latitude and longtitude
CGFloat latDelta = gallery.latitude;
CGFloat longDelta = gallery.longitude;
CLLocationCoordinate2D newCoord = {latDelta, longDelta};
//adds the notations
AddressAnnotation *addrAnnotation = [[[AddressAnnotation alloc] initWithCoordinate:newCoord]autorelease];
//assing the location the map
[addrAnnotation setId:i];
[addrAnnotation setTitle:gallery.name];
if([gallery.exhibition length]==0 && !gallery.isResturant){
[addrAnnotation setSubtitle:#""];
}else{
[addrAnnotation setSubtitle:gallery.exhibition];
}
**//gives the exception on this line
[galleryListMap addAnnotation:addrAnnotation];**
}
MKCoordinateRegion region;
MKCoordinateSpan span = MKCoordinateSpanMake(0.2, 0.2);
Gallery *lastGalleryItem = [copyOfAllRecords lastObject];
CLLocationCoordinate2D location = {lastGalleryItem.latitude, lastGalleryItem.longitude};
region.span=span;
region.center=location;
[galleryListMap setRegion:region animated:TRUE];
[galleryListMap regionThatFits:region];
galleryListMap.showsUserLocation = YES;
[pool release];
}
- (MKAnnotationView *) mapView:(MKMapView *)mkmapView viewForAnnotation:(AddressAnnotation *) annotation{
static NSString *identifier = #"currentloc";
if([annotation isKindOfClass:[AddressAnnotation class]]){
MKPinAnnotationView *annView = [[[MKPinAnnotationView alloc]initWithAnnotation:addAnnotation reuseIdentifier:identifier]autorelease];
Gallery *galItem = (Gallery *)[copyOfAllRecords objectAtIndex:annotation.annotationId];
CGRect viewFrame;
UIView *myView;
UIImage *tagImage;
//checks whether the resturant or not
if (galItem.isResturant) {
viewFrame = CGRectMake( 0, 0, 41, 45 );
myView = [[UIView alloc] initWithFrame:viewFrame];
tagImage= [UIImage imageNamed:#"map_restaurant"];
}else{
viewFrame = CGRectMake( 0, 0, 41, 45 );
myView = [[UIView alloc] initWithFrame:viewFrame];
tagImage= [UIImage imageNamed:galItem.mapMarker];
}
UIImageView *tagImageView = [[UIImageView alloc] initWithImage:tagImage];
[myView addSubview:tagImageView];
[tagImageView release];
UIGraphicsBeginImageContext(myView.bounds.size);
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[annView setImage:viewImage];
[myView release];
int recordCount =[copyOfAllRecords count];
if (recordCount != 0 ) {
UIButton *annotationButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[annotationButton addTarget:self action:#selector(showLinks:) forControlEvents:UIControlEventTouchUpInside];
annotationButton.tag = [annotation annotationId];
annView.rightCalloutAccessoryView = annotationButton;
}
if([annotation annotationId]==recordCount-1){
//hides loading screen
//[HUD hide:YES];
self.mapAlreadyLoaded = YES;
}
return annView;
}
return nil;
}
The exception I get is:
*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSArrayM: 0x21683870> was mutated while being enumerated.'
*** First throw call stack:
(0x32dc62a3 0x3259897f 0x32dc5d85 0x37c4d33b 0x37c50373 0x86e63 0x37dcc67d 0x36e52311 0x36e521d8)
But if I use:
[self populateMap];
//[self performSelectorInBackground:#selector(populateMap) withObject:nil];
the application works fine.
Any idea why this is happening?
There are a number of reasons this could be happening - the error is telling you that at some point you (or an underlying iOS library) tried to enumerate an array that changed while that process was on-going.
The fact that it works when you populateMap off the main thread and doesn't work when you call it on a secondary thread suggests there's some sort of race condition going on.
Bear in mind that not all of UIKit is thread safe (most of it isn't) - so there might be an issue there. You add the annotations to your map whilst still on a background thread:
[galleryListMap addAnnotation:addrAnnotation];
...this is also the line on which your crash happens. It stands to reason that when you add an annotation to a MapView it probably iterates through all its current annotations to update the display. Because you're making these calls off a background thread this could introduce lots of problems.
Instead, try this:
[galleryListMap performSelectorOnMainThread:#selector(addAnnotation:)
withObject:addrAnnotation
waitUntilDone:YES]
This will force the map view to add the annotation on the main thread. As a general rule, there are only a few things in UIKit that are thread-safe (the apple documentation has a complete list).

UIScrollView with UIImageview and gestures using transitionFromView acting strange

I made a view which holds a UIScrollview:
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 65, 300, 188)];
//BackViews will hold the Back Image
BackViews = [[NSMutableArray alloc] init];
for (int i=0; i<BigPictures.count; i++) {
[BackViews addObject:[NSNull null]];
}
FrontViews = [[NSMutableArray alloc] initWithCapacity:BigPictures.count];
[self.pageControl setNumberOfPages:BigPictures.count];
Then I add several UIImageviews containing images:
//BigPictures holds objects of type UIImage
for (int i = 0; i < BigPictures.count; i++) {
UIImageView *ImageView = [[UIImageView alloc] initWithImage:[BigPictures objectAtIndex:i]];
ImageView.frame = [self.scrollView bounds];
[ImageView setFrame:CGRectMake(self.scrollView.frame.size.width * i, ImageView.frame.origin.y, ImageView.frame.size.width, ImageView.frame.size.height)];
//this saves the FrontView for later (flip)
[FrontViews insertObject:ImageView atIndex:i];
[self.scrollView addSubview:test];
}
// Detect Single Taps on ScrollView
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flip)];
[self.scrollView addGestureRecognizer:tap];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * BigPictures.count, self.scrollView.frame.size.height);
self.scrollView.pagingEnabled = YES;
self.scrollView.delegate = self;
Ok so far so good. Now the method which does the flipImage part:
- (void)flip {
int currentPage = self.pageControl.currentPage;
UIView *Back = nil;
if ([BackViews objectAtIndex:currentPage] == [NSNull null]) {
//CreateBackView is just creating an UIView with text in it.
Back = [self CreateBackView];
[BackViews replaceObjectAtIndex:currentPage withObject:Back];
[UIView transitionFromView:[[self.scrollView subviews] objectAtIndex:currentPage] toView:[BackViews objectAtIndex:currentPage] duration:0.8 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
} else {
[UIView transitionFromView:[[self.scrollView subviews] objectAtIndex:currentPage] toView:[FrontViews objectAtIndex:currentPage] duration:0.8 options:UIViewAnimationOptionTransitionFlipFromRight completion:NULL];
[BackViews replaceObjectAtIndex:currentPage withObject:[NSNull null]];
}
[self.view addSubview:Back];
[self rebuildScrollView];
}
This is what rebuildScrollView does:
- (void)rebuildScrollView
{
for (UIView *subview in self.scrollView.subviews) {
[subview removeFromSuperview];
}
for (int i = 0; i < BigPictures.count; i++) {
if ([BackViews objectAtIndex:i] == [NSNull null]) {
[self.scrollView addSubview:[FrontViews objectAtIndex:i]];
} else {
[self.scrollView addSubview:[BackViews objectAtIndex:i]];
}
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * BigPictures.count, self.scrollView.frame.size.height);
self.scrollView.pagingEnabled = YES;
self.scrollView.delegate = self;
}
So the behavior is the following:
If I click on the first image (1 of 3 in scrollview) the effect is the way I want it, meaning the frontimage turns around and shows the empty (white) back with some text in the middle
If I click on the second image, the image turns but the back is completely empty showing the grey background of the window. If I scroll to the other images, the still show the front image (as expected)
Now I click on the third image and its the same as 1) great.
Current layout is now [BackView, Nothing, Backview)
Lets run that again. But now I click on the last image and its the same as 2) :(
Any ideas whats going wrong here?
EDIT: Some new findings. I doubled the amount of pictures and this is how Front and Backviews are placed (after flipping each one). P = Picture & B = Backview.
P_1(B_1) - actually the only correct one
P_2(empty - should be B_2)
P_3(B_2 - should be B_3)
P_4(empty - should be B_4)
P_5(B_3 - should be B_5)
P_6(empty - should be B_6)
Did a complete rebuild and now it works. Really strange because I used the exact same code.

How to display imagepickercontroller through popoverview controller?

I have used the following code to get photos from album in iphone but it displays the error "UIImagePickerController must be presented via UIPopoverController"
- (void)showImagePicker:(UIImagePickerControllerSourceType)sourceType
{
if ([UIImagePickerController isSourceTypeAvailable:sourceType])
{
[self setupImagePicker:sourceType];
[self presentModalViewController:imagePickerController animated:YES];
}
}
- (void)setupImagePicker:(UIImagePickerControllerSourceType)sourceType
{
imagePickerController.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
// user wants to use the camera interface
//
imagePickerController.showsCameraControls = NO;
if (imagePickerController.cameraOverlayView != self.view)
{
// setup our custom overlay view for the camera
//
// ensure that our custom view's frame fits within the parent frame
CGRect overlayViewFrame = imagePickerController.cameraOverlayView.frame;
CGRect newFrame = CGRectMake(0.0,
CGRectGetHeight(overlayViewFrame) -
self.view.frame.size.height - 9.0,
CGRectGetWidth(overlayViewFrame),
self.view.frame.size.height + 9.0);
self.view.frame = newFrame;
imagePickerController.cameraOverlayView = self.view;
}
}
}
-(IBAction)getPhoto:(id)sender {
imagePickerController = [[UIImagePickerController alloc] init];
[self showImagePicker:UIImagePickerControllerSourceTypePhotoLibrary];
}
Can any one please alter my code to work on iPad.
Thanks in advance.
-(IBAction)Click_event
{
UIImagePickerController *imagePickerController_=[[UIImagePickerController alloc] init];
UIPopoverController *popover_=[[UIPopoverController alloc] initWithContentViewController:imagePickerController_];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
imagePickerController_.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[popover_ presentPopoverFromRect:CGRectMake(400, 400, 0, 0) inView:self.Mybutton
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}

transitionFromView:toView:duration:options:completion: is not animating the transition

I have a view controller that displays the views of 2 sub view controllers in a given area of its view. The 2 sub view controllers are FlopVC and FipVC.
I want to animate the transition from one sub view to the other. The code I'm using is:
-(IBAction)flip:(id)sender{
UIViewController *newVC = nil;
if (self.isFlip) {
newVC = [[FlopVC alloc] initWithNibName:nil bundle:nil];
}else{
newVC = [[FipVC alloc] initWithNibName:nil bundle:nil];
}
newVC.view.frame = CGRectMake(120, 20, 240, 260);
[self.view addSubview:newVC.view];
[UIView transitionFromView:self.currentVC.view
toView:newVC.view
duration:0.9
options:UIViewAnimationTransitionFlipFromLeft
completion:^(BOOL finished) {
self.currentVC = newVC;
self.isFlip = ! self.isFlip;
}];
}
The sub views are swapped, but without any animation. What am I doing wrong?
PS the full project is here.
UIViewAnimationTransitionFlipFromLeft != UIViewAnimationOptionTransitionFlipFromLeft
if you are using the new iOS5 view container paradigm you need to do something along the lines of the following:
-(IBAction)flip:(id)sender{
UIViewController *newVC = nil;
if (self.isFlip) {
newVC = [[FlopVC alloc] initWithNibName:nil bundle:nil];
}else{
newVC = [[FipVC alloc] initWithNibName:nil bundle:nil];
}
newVC.view.frame = CGRectMake(120, 20, 240, 260);
// required for the new viewController container
self.currentVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
[self transitionFromViewController:self.currentVC
toViewViewController:newVC.view
duration:0.9
options: UIViewAnimationOptionTransitionFlipFromLeft
animations:nil
completion:^(BOOL finished) {
// required for the new viewController container
[self.currentVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
self.currentVC=newVC;
}];
}
reference the section Implementing a Container View Controller and the 2011 WWDC videos on UIViewController containers for more info.
Here is working code that (by sheer coincidence) does exactly what you're describing. My two child vc's are stored in self->swappers. The integer cur keeps track of which one is currently showing. The spot in my interface where the subview is to go is marked by a view outlet, panel.
UIViewController* fromvc = [self->swappers objectAtIndex:cur];
cur = (cur == 0) ? 1 : 0;
UIViewController* tovc = [self->swappers objectAtIndex:cur];
tovc.view.frame = self.panel.bounds;
// must have both as children before we can transition between them
[self addChildViewController:tovc]; // "will" called for us
// note: when we call remove, we must call "will" (with nil) beforehand
[fromvc willMoveToParentViewController:nil];
[self transitionFromViewController:fromvc
toViewController:tovc
duration:0.4
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:nil
completion:^(BOOL done){
// note: when we call add, we must call "did" afterwards
[tovc didMoveToParentViewController:self];
[fromvc removeFromParentViewController]; // "did" called for us
}];