Returning same class object to UIPageViewController - iphone

I have used the UIPageViewController for loading around 5 images in a webview.
So whenever the below line of code
- (UIViewController *)pageViewController:(UIPageViewController *) pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
, is called, the return object is a class object(in my case class name is LoadPage). Everything works fine in my code, but I am facing some weird problem here and little uncomfortable.
So I planned to move to single class instead of two.
Now the return type for this method will be self.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
return self;
}
Am I right?
But When I try to initialize the UIPageViewController, the application is getting crashed at one particular line.
- (void) createViewControllers {
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options: nil];
pageViewController.delegate = self;
pageViewController.dataSource = self;
NSArray* nextScreens = [NSArray arrayWithObject:self];
[pageViewController setViewControllers: nextScreens direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:pageViewController];
[self.view addSubview:pageViewController.view];
[[self view] addSubview:[pageViewController view]]; //CRASH
[pageController didMoveToParentViewController:self];
CGRect pageViewRect = self.view.bounds;
pageViewRect = CGRectInset(pageViewRect,0, 0);
pageViewController.view.frame = pageViewRect;
self.view.gestureRecognizers = pageViewController.gestureRecognizers;
}

You cannot return self in pageViewController:viewControllerAfterViewController because that would produce a cycle in the view controller hierarchy.
You add the pageViewController as child view controller to self and then later the pageViewController adds self as child view controller (page) to itself. Thats not allowed!

Related

UIImagePickerControllerCameraDeviceFront works every other time

This question is very similar to an existing question asked here UIImagePickerControllerCameraDeviceFront only works every other time I tried the solution presented but it didn't work for me
I have a simplest of a project with two view controllers. In the blue one I am displaying a small UIView with a UIImagePickerController in it. NOTE: I am displaying front facing camera when app is launched.
I hit the next button and go to orange view controller and when I hit the back button and come back to blue view controller the UIImagePickerController flips from Front to rear. I guess the reason is that it thinks its busy and moves to the rear cam. If I keep moving back and forth between the view controllers the camera keeps flipping front, back, front, back, front, back...
Here is my code and screenshots, what am I doing wrong?
In my *.h
#import <UIKit/UIKit.h>
#interface v1ViewController : UIViewController <UIImagePickerControllerDelegate>
{
UIImagePickerController *picpicker;
UIView *controllerView;
}
#property (nonatomic, retain) UIImagePickerController *picpicker;
#property (nonatomic, retain) UIView *controllerView;
#end
In my *.m file (This code is only used when blue colored view controller is displayed)
#import "v1ViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>
#implementation v1ViewController
#synthesize picpicker;
#synthesize controllerView;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
controllerView = picpicker.view;
[controllerView setFrame:CGRectMake(35, 31, 250, 250)];
controllerView.alpha = 0.0;
controllerView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[self.view addSubview:controllerView];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
controllerView.alpha = 1.0;
}
completion:nil
];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
You are dismissing the controller in both the viewDidDisappear and viewWillDisappear methods.
That could be the cause of your problem.
Although I do not have a device with a camera available right now to verify this, it seems that you're not dismissing the pickerview controller correctly. The documentation states that you should call dismissModalViewControllerAnimated: on the parent controller in order to dismiss the picker (though, calls to presented controllers will propagate to presenters - so this is not the problem), but in your case you're not displaying the controller modally in the first place so it will not work.
What I would try in this case is to release the picker instead (if not under ARC) and set it to nil (instead of calling [picpicker dismissModalViewControllerAnimated:YES];).
PS. In fact, it seems that there is a bigger problem with your design. Since each button is set to present the other party modally you are not dismissing any of the controllers ever. The controllers just keep stacking on each other. You should either consider to embed them in a navigation controller and have it handle the hierarchy or just set dismissModalViewControllerAnimated: (dismissViewControllerAnimated:completion: on iOS5+) as the action of the second controller's button instead of a modal segue.
This is a very simple issue. I don't know why this happens exactly, but it seems that UIImagePickerController was designed to recreated each time it's needed instead of keeping any reference to it, which seems logical if you think about it. Basically, you need to recreate and reconfigure your picker each time. Below I've pasted some code to give an image of what I mean.
Simple solution:
- (UIImagePickerController *)loadImagePicker {
UIImagePickerController *picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
return picpicker;
}
and in:
-(void)viewWillAppear:(BOOL)animated{
if(!self.picpicker){
self.picpicker = [self loadImagePicker];
[self.view addSubview: self.picpicker];
}
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.picpicker removeFromSuperview];
self.picpicker = nil;
}

iOS6 autorotation - UIPageViewController

My UI structure:
UINavigationController
--UIViewController(first)
|
|Modal
|
V
UINavigationController (subclass)
--UIViewController (contains UIPageViewController)
For iOS6 new autorotation, I created a subclass of UINavigationController and added:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Only want keep the orientation is portrait.
And I initialize UIPageViewController like:
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
PageDataViewController *startingViewController = [self viewControllerAtIndex:start storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
Then I perform the Modal segue,
if the first UIViewController is portrait, the pageViewController works,
but if the first UIViewController is landscape, I always got exception:
* Assertion failure in -[UIPageViewController willAnimateRotationToInterfaceOrientation:duration:],
/SourceCache/UIKit_Sim/UIKit-2372/UIPageViewController.m:945
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No view controllers'
** First throw call stack: (0x2a5e012 0x19bae7e 0x2a5de78 0x1450f35 0xd75974 0x9f12b4 0x9f13ea 0x9e4c32 0x9f1372 0x9f13ea 0x9e4c32
0x9f1372 0x9f1751 0x93a1a6 0xbee5f9 0x9ec4f3 0x9ec777 0x9ec7b7
0xd57fe2 0xd49ad9 0xd49b54 0x9b1899 0x9b1b3d 0x13b8e83 0x2a1d376
0x2a1ce06 0x2a04a82 0x2a03f44 0x2a03e1b 0x2c967e3 0x2c96668 0x90265c
0xea7d 0x2685 0x1) libc++abi.dylib: terminate called throwing an
exception
Actually There is another solution for it.
The rotation just calling the willAnimateRotationToInterfaceOrientation method to the rootViewController, you have to implement the method of it.
#interface UIPageViewController (Rotation)
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
#end
//Implementation file
#import "UIPageViewController+Rotation.h"
#implementation UIPageViewController(Rotation)
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {}
#end
In the documentation about "supportedInterfaceOrientations" it is written:
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window
Did you try setting the rootViewController for the main windows to your current NavigationController classes?
Like in the AppDelegate:
self.window.rootViewController = nvc;
And when your navigationController is changing, set the rootViewController again...
I have "fixed" the problem, by setting view controllers to UIPageViewController in viewWillAppear: again, because when this method is called, spine location for page view controller is correct and based on this information i set one, or two pages (view controllers)
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(self.pageViewController.spineLocation == UIPageViewControllerSpineLocationMid)
{
BookPageViewController *currentViewController = self.pageViewController.viewControllers[0];
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = #[currentViewController, nextViewController];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = #[previousViewController, currentViewController];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
if(self.pageViewController.spineLocation == UIPageViewControllerSpineLocationMin)
{
[self.pageViewController setViewControllers:[NSArray arrayWithObject:[self.pageViewController.viewControllers objectAtIndex:0]] direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
}
and set UIPageViewController Delegate method as below
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
BookPageViewController *currentViewController = self.pageViewController.viewControllers[0];
if(UIInterfaceOrientationIsPortrait(orientation))
{
[self.pageViewController setViewControllers:[NSArray arrayWithObject:currentViewController] direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMin;
}
if(UIInterfaceOrientationIsLandscape(orientation))
{
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = #[currentViewController, nextViewController];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = #[previousViewController, currentViewController];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
return UIPageViewControllerSpineLocationMid;
}
return UIPageViewControllerSpineLocationMin;
}
I have "fixed" the problem, by setting view controllers to UIPageViewController in viewWillAppear: again, because when this method is called, spine location for page view controller is correct and based on this information i set one, or two pages (view controllers)

UINavigationController and pushing an openGL UIView = never ending loops

I am trying to push an opengl UIView to my navigation controller like this
GraphViewController *gvc = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:gvc animated:YES];
[gvc release];
The initWithTicker method looks like this
-(id) initWithTicker:(NSString*)ticker{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.title = ticker;
EAGLView *eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
self.view = eagl;
}
return self;
}
When I go back and forward in my UINavigationController, the drawView method (in EAGLView) keeps looping. Furthermore, if I pushViewController again, the first one does not stop and a new one is created! I've tried making this an instance variable so only one is created and it has the same effect. I would be grateful if anyone has insight as to why this is happening
sergio Suggestion:
-(id) initWithTicker:(NSString*)ticker{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.title = ticker;
}
return self;
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view = eagl;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
[super viewDidLoad];
}
same behaviour.
---This is how I fixed my drawView looping problem--
-(void)viewDidAppear:(BOOL)animated {
[eagl startAnimation];
[super viewDidAppear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[eagl stopAnimation];
[super viewDidDisappear:animated];
}
--Craigs solution --
if(graphView == nil){
graphView = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}else{
[graphView release];
graphView = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}
Are you creating a new GraphViewController every time you want to push one onto your navigation stack? If so, it doesn't really matter how you're handling the creation of your EAGLView instance variable, since you're never going to be interacting with that view controller again anyway.
For example:
User taps something, a new GraphViewController is pushed on the stack
User goes back, this view controller continues to run
Return to 1. and repeat (thus creating a SECOND GraphViewController, and then a third, and then a fourth... etc.)
What you should probably be doing is maintaining your GraphViewController as an instance variable, and only creating it once. This will ensure that you're in turn only creating one EAGLView.
if (_graphViewController == nil) {
_graphViewController = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}
[self.navigationController pushViewController:_graphViewController animated:YES];
Then, be sure to release the view controller in your dealloc method if you're going to be maintaining it as an ivar.
Would you try executing this code of yours:
EAGLView *eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
self.view = eagl;
inside of loadView? I am not sure about why your view is behaving like you say, but that is the place where you are supposed to build your UI... so it might make a difference...
Furthermore, I would call [eagl startAnimation]; only in viewDidLoad...
Hope it helps...

how to fix iOS leak when flipping between two views

My iPhone app badly leaks when flipping back and forth between a main uiviewcontroller and a help uiviewcontroller .
Here is the source of the main view, followed by source of the help view.
MAIN VIEW - FLIP TO HELP.....................
// Changes from operational view to Help view.
- (IBAction)showHelp:(id)sender
{
// End trial mode:
self.stop_trial_if_started;
self.rename_trial_if_edited;
// Switch to trial help:
help_view_context = 0;
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
HELP VIEW - INIT.............................
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
help_scroll.editable = FALSE;
return;
}
HELP - RETURN TO MAIN VIEW.........................
// User clicked the button to return to operational view:
- (IBAction)done:(id)sender {
NSLog(#"help- done");
if( help_view_context == 0 ) {
[self.delegate trial_help_DidFinish:self];
}else{
[self.delegate file_help_DidFinish:self];
}
}
MAIN VIEW - RETURN FROM HELP...............................
// Inits operational view when user changes from Help view back to operational view.
- (void)trial_help_DidFinish:(HelpView *)controller {
NSLog(#"trial_help_DidFinish");
[self dismissModalViewControllerAnimated:YES];
self.init_trial_operation;
}
You are creating a controller with ref count of 1 and a local reference each time showHelp: is called:
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
you are losing your reference to it at the end of this method.
You happen to have references to it in done: (self) and *_help_didFinish (controller), but you never release it in either of those locations. Dismissing the controller is fine, but you also have to release it.
(Another option would be to never create a second one, and maintain an iVar to the original.)
You could well be leaking on this line
controller.delegate = self;
What is your property declaration for the delegate. If it's anything other than assign, then you either need to change it (preferred option) or make sure you are releasing it in the dealloc method of HelpView controller.

Modal View Not Working

I am using the following code in my view controller and I want it to present another view controller called "chooserViewController" modaly
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
{
[self presentModalViewController:chooserViewController animated:YES];
}
I am getting a compile error not recognizing "chooserViewController". Am I doing it wrong?
Update:
- (void)add:(id)sender
{
RoutineExerciseChooserViewController *routineExerciseChooserViewController = [[RoutineExerciseChooserViewController alloc] initWithNibName:#"RoutineExerciseChooserViewController" bundle: nil];
[self presentModalViewController:routineExerciseChooserViewController animated:YES];
[routineExerciseChooserViewController release];
}
You need to create chooserViewController:
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated {
ChooserViewController *chooserViewController = [[ChooserViewController alloc] initWithNibName:#"ChooserView" bundle: nil];
[self presentModalViewController:chooserViewController animated:YES];
[chooserViewController release];
}
If you're not loading from a nib, obviously you'll use a different way to create chooserViewController, but you have to do something to ensure it exists, and can then be presented.