I am trying to change my view after the device is rotated. I can't do autorotation support in the view that is drawing because I have custom graphics occurring and it is much easier to reinitialize the view.
The problem I am having is that using [pageViewController setViewControllers:] doesn't seem to want to work inside the didRotateToInterfaceOrientation block.
Here's the method:
- (void) didRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
if (UIInterfaceOrientationIsPortrait(orientation)) {
NSLog(#"%#", pageViewController.viewControllers);
isLandscape = YES;
ContentViewController *vc = [self viewControllerAtIndex:currentPage];
NSLog(#"%#", vc);
[pageViewController setViewControllers:[NSArray arrayWithObject:vc] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
NSLog(#"%#", pageViewController.viewControllers);
}
}
which produces this output:
<ContentViewController: 0x74aa8f0>
<ContentViewController: 0xfc0cb00>
<ContentViewController: 0x74aa8f0>
Is there some reason I can't change the view controllers inside this method? If so, where should I be doing this change?
EDIT:
I have been able to bypass the issue using dispatch_async(), but I'd still like to know why this was a problem in the first place.
Related
As in the title, I do not manage to have that callback called in order to dismiss the game center view controller neither on my iOS 7 iPhone nor iOS 8 iPad. This is the code I use:
GKGameCenterViewController *controller=nil;
- (IBAction)achievementButtonClicked:(id)sender {
if (!controller){
controller=[[GKGameCenterViewController alloc] init];
controller.delegate=self;
}
NSLog(#"controller=%#", controller);
if (controller) [self presentViewController:controller animated:YES completion:nil];
}
-(void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController{
[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
// I also tried [self dismissViewControllerAnimated:YES completion:nil] but anyway the function seems to not even enter here
}
If I take the function off, the delegate complaints it is missing, so the issue should not be connected to that. What might that be and how to fix it?
My problem is that I used:
controller.delegate=self;
omitting:
controller.gameCenterDelegate = self;
Once inserted the latter, the view controller dismisses without problems, both when I manually present the controller and when it is shown to login her. I really wonder why that beast has too delegates, if not to confuse developers...
I'm not using a navigation controller or a tab bar controller, I'm not using the push/pop method or presenting views modally. In my main view controller, I am adding a view controller like so:
UIViewController *nextController;
nextController = [[GamePlayViewController alloc] initWithNibName:#"GamePlayView" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
temporaryController = nextController;
[self.view addSubview:nextController.view];
This view controller follows a delegate protocol and when the user is finished in this game view, this code is called:
[delegate backToMenu:self];
which calls this function in the app's main view controller:
- (void)backToMenu:(GamePlayViewController *)sender {
NSLog(#"back to menu");
[temporaryController.view removeFromSuperview];
}
Removing the view with removeFromSuperview seems to get rid of the view only, but I can see due to NSLogging that code is still executing in the .m file of that removed view. The view is still in in the app's memory. It has not been discarded as I had hoped.
"Release" is an old relic never to be used with ARC, so how can I entirely remove the viewController that was created with alloc/initWithNibName?
Thanks!
You should also be using the view controller life cycle methods.
Adding:
GamePlayViewController *nextController = [[GamePlayViewController alloc] initWithNibName:#"GamePlayView" bundle:nil];
nextController.delegate = self;
[self addChildViewController:nextController];
[self.view addSubview:nextController.view];
[nextController didMoveToParentViewController:self];
temporaryController = nextController;
Removing:
[temporaryController didMoveToParentViewController:nil];
[temporaryController.view removeFromSuperview];
[temporaryController removeFromParentViewController];
temporaryController = nil;
Also if temporaryController is a strong property (or you've used an iVar), you should nil it out after removing it.
As the CAAnimation retains its delegate make you remove the animation and nil out the delegate.
-(void)didMoveToParentViewController:(UIViewController *)parentViewController
{
[super didMoveToParentViewController:parentViewController];
if (!parentViewController) {
CAAnimation *animation = [movingObject.layer animationForKey:#"animatePositionX"];
animation.delegate = nil;
[movingObject.layer removeAnimationForKey:#"animatePositionX"];
}
}
If you want to check your view controller is being deallocoated you should implement the dealloc method and place a breakpoint inside of it. I suggest a breakpoint over a NSLog as I don't know how much you already log out so it might get missed, with a breakpoint it is much clearly - actually stopping the program flow.
I have the following piece of code to add a QLPreviewController subview
{
QLPreviewController *preview = [[QLPreviewController alloc] init];
preview.delegate = self;
preview.dataSource = self;
[self addChildViewController:preview];
[self.view addSubview:preview.view];
[preview didMoveToParentViewController:self];
self.previewController = preview;
}
-(NSInteger) numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller
{
return 1;
}
-(id) previewController:(QLPreviewController *)previewController previewItemAtIndex:(NSInteger)index
{
return self.url;
}
self.url is an NSURL that is located in NSTemporaryDirectory - file://localhost//.../blah.pdf
My issue is that when my laptop is connected to the internet, the document shows up as a subview, but when my laptop is not connected, numberOfPreviewItemsInPreviewController & previewItemAtIndex do not get called.
I've tried a vanilla program with a simple view controller, and it seemed to work fine. (My app is more complex than that).
When I try presenting the document as a modal view, it seems to work irrespective of whether or not the simulator is connected to the internet.
[self presentViewController:preview animated:NO completion:nil]; --> works consistently.
I need to get the subview working for online & offline modes, it would be great if someone could help!
You might be encountering strange behaviour because the QLPreviewController's view is not designed to be embedded in another view. From the QLPreviewController class reference overview:
To display a Quick Look preview controller you have two options: You can push it into view using a UINavigationController object, or can present it modally, full screen, using the presentModalViewController:animated: method of its parent class, UIViewController.
Having said that, you could try:
Forcing the QLPreviewController to (re)display its contents. Try adding [self.previewController reloadData]; to the end of your first method. This should force the data source method(s) to fire.
Forcing the view to "refresh" it's subviews: [self.view setNeedsLayout] (which may in fact force a reloadData like the first option).
Good luck!
I have an app that is loading a overlay controller (shows camera so I can scan). It works great on the iPhone and it works great on the iPad after I call it a second time. Let me explain.
I have a UIButtonBarItem that loads a view controller modally. There are several controls on in the controller, most buttons (defined using a nib). If I load the controller (by responding to the UIButtonBarItem action) on an iPhone, it loads and all the buttons work fine, every time.
But... if I load the same view controller using an UIPopoverController, none of the buttons will respond the first time I load it. So, I touch the screen somewhere outside of the controller and dismiss the controller. Then, I touch the same action button again and now when the controller loads, all the controls in the the view controller work great. REALLY WEIRD!
[POSSIBLE HINT]
The buttons were placed all over the place in weird positions when I loaded it the first time. Each subsequent call had the buttons showing in the right places. I got this to work by disabling "Autoresize subviews" in the nib. The buttons are now in the right places but they still won't respond when I load this popover the first time.
Here's the code I'm using to respond to the UIButtonBarItem.
-(void)launchOverlayController:(id)sender
{
if([pickerControllerPopover isPopoverVisible])
{
[pickerControllerPopover dismissPopoverAnimated:YES];
pickerControllerPopover = nil;
return;
}
// Deselect any selected cell
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:NO];
// Working code that shows the overlay (camera on) but the overlay takes the whole screen
SRSScanVINViewController *scanVINViewController = [[SRSScanVINViewController alloc] init];
[pickerController setOverlay:scanVINViewController];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:scanVINViewController];
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[navController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
pickerControllerPopover = [[UIPopoverController alloc] initWithContentViewController:pickerController];
[pickerControllerPopover setDelegate:self];
[pickerControllerPopover setPopoverContentSize:CGSizeMake(320.0f, 460.0f)];
[pickerControllerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else
{
[self presentViewController:pickerController animated:YES completion:nil];
}
}
I'm totally out of ideas. I can't see why the controls within the overlaycontroller would work fine every time I call it except for the first time.
Thanks for anyones help in advance.
So the answer is that the superclass is mucking with your view. I'm going to guess it was not designed to be subclassed, but no way to know for sure. What it does in one of the 'view..' methods is to override self.view with its own view, and make your view a subview of that view. The first time around it makes the frame of your view have zero dimensions. The next time it leaves it as it was before - maybe some persistent flag. It also inserts the view at different places in its subviews, which seems odd but if you have the code you'd probably see why.
Soooo - the solution to the problem is to just move your view's subviews to the superView (the subclass's view), then set your view's frame to the null frame:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]; // StackOverflow says to add this TCL?
// Set the initial scan orientation
[self setLayoutOrientation:self.parentPicker.orientation];
if ([self.parentPicker hasFlash])
{
[flashButton setEnabled:YES];
[flashButton setStyle:UIBarButtonItemStyleBordered];
[self.parentPicker turnFlash:NO];
} else
{
[flashButton setEnabled:NO];
}
textCue.text = #"";
viewHasAppeared = NO;
// move the subviews
while([self.view.subviews count]) [self.view.superview addSubview:[self.view.subviews objectAtIndex:0]];
self.view.frame = CGRectMake(0,0,0,0);
}
PS: note that you were missing a superView call here but it didn't seem to matter much (you don't know which method your complex superclass may want so I'd be sure to send them everything you intercept.
I recently encountered a hair-pulling situation in my iOS app, where I was trying to successively dismiss one presented UIViewController from my window's rootViewController, using:
[rootViewController dismissViewControllerAnimated:YES completion:NULL]
and present another one shortly thereafter (in another method, incidentally), with:
UIViewController *vc2 = [[[MyViewController2 alloc] initWithNibName:nil bundle:nil] autorelease];
[rootViewController presentViewController:vc2 animated:YES completion:NULL];
Problem was, I could never get the second view controller to show up. Turns out, as near as I can tell, dismissViewControllerAnimated:completion: needs that asynchronous block of "completion" time to pass, before presentViewController:animated:completion: will work properly again. This fact is not directly documented in Apple's docs, from what I can tell.
The solution I came up with was to wrap the dismissal with a method that specifies the selector you would want to call afterwards, like so:
- (void)dismissViewController:(UIViewController *)presentingController
postAction:(SEL)postDismissalAction
{
[presentingController dismissViewControllerAnimated:YES
completion:^{
[self performSelectorOnMainThread:postDismissalAction
withObject:nil
waitUntilDone:NO];
}];
}
And then I would call:
[self dismissViewController:self.window.rootViewController
postAction:#selector(methodForNextModalPresentation)];
Anyway, I wanted to post, as I looked around and hadn't seen anyone with this particular problem, so I thought it might be useful for people to understand. And also, I wanted to verify that I'm not hacking a solution that has a better design pattern for resolution.
Just for the sake of clarity. are you saying that this code doesn't work?
[myRootViewController dismissViewControllerAnimated:YES completion:^{
[myRootViewController pushViewController:newController animated:YES];
}];