I have this cascade :
Somewhere in the app
- (void) go
{
MyCustomViewController* controller = [[MyCustomViewController alloc] init];
controller.delegate = self;
[controller run];
}
in MyCustomViewController
- (id) init
{
// there is an if statement here in real to choose another XIB if needed
// but I only display here the code that is called in my tests
self = [super initWithNibName:#"MyXIB" bundle:nil];
if (!self) return nil;
self.delegate = nil;
return self;
}
- (void) run
{
// do things with self.view
}
MyCustomViewController inherits GenericWindowController
in GenericWindowController
/*- (id) initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) return nil;
self.view.userInteractionEnabled = NO; // THE APP CRASHES HERE ! self.view is nil
...
return self;
}*/
// METHOD created following first answers : NOT CALLED
- (void) viewDidLoad
{
self.view.userInteractionEnabled = NO;
// and many other things done with self.view
}
MyXIB has its File's owner set to MyCustomViewController and the view is connected.
All files are included and checked into the project.
GenericWindowController is designed to make some standard stuff.
MyCustomViewController extends this stuff to work with a custom View, as designed in MyXIB.
Why self.view is nil in GenericWindowController ?
Why viewDidLoad is not called ?
A view controller should not try to access its view in initWithNibName:bundle:. It should wait until viewDidLoad.
self.view is only valid after viewDidLoad -- in init... it is still nil.
Incredible !!! The problem was because of a missing localization for the XIB. Added the localization, and no problem anymore.
Related
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;
}
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...
I have a UINavigationController application with a root view controller and each time I am pushing a view controller to the stack. Let's say the stack is A B C D where A is the root view controller here. The issue is that when I am at view controller D and do a popToRootViewController it went back to A but A has a back button on it. When I click on back the back just slides in and disappear, why is this happening?
EDIT: I am actually subclassing my UINavigationController so that I can set my rootViewController as follows:
#import "CustomNavigationController.h"
#implementation CustomNavigationController
#synthesize fakeRootViewController;
//override to remove fake root controller
-(NSArray *)viewControllers {
NSArray *viewControllers = [super viewControllers];
if (viewControllers != nil && viewControllers.count > 0) {
NSMutableArray *array = [NSMutableArray arrayWithArray:viewControllers];
[array removeObjectAtIndex:0];
return array; }
return viewControllers; }
//override so it pops to the perceived root
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated {
//we use index 0 because we overrided “viewControllers”
((UIViewController *)[self.viewControllers objectAtIndex:0]).navigationItem.hidesBackButton = YES;
return [self popToViewController:[self.viewControllers objectAtIndex:0] animated:animated]; }
//this is the new method that lets you set the perceived root, the previous one will be popped (released)
-(void)setRootViewController:(UIViewController *)rootViewController {
rootViewController.navigationItem.hidesBackButton = YES;
[self popToViewController:fakeRootViewController animated:NO];
[self pushViewController:rootViewController animated:NO]; }
- (void)dealloc {
self.fakeRootViewController = nil;
[super dealloc]; }
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
UIViewController *fakeController = [[[UIViewController alloc] init] autorelease];
self.fakeRootViewController = fakeController;
NSMutableArray *array = [NSMutableArray arrayWithArray:[super viewControllers]];
[array insertObject:fakeController atIndex:0];
self.viewControllers = array;
}
return self; }
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use. }
#pragma mark - View lifecycle
/* // Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView { }
*/
/* // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad]; }
*/
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil; }
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait); }
#end
MORE UPDATE:
So after I set my rootViewController and then push a viewController and then tried to call popToRootViewController from that view, it all worked fine. However, if I push another viewController after the second one and then call the popToRootViewController, now I can see that weird back button on the root.
I too face the same problem. So in your root controller assign leftBarButtonItem equal to nil.
self.navigationItem.leftBarButtonItem = nil;
If your root controller reusing in the program. Then you need to check ->
BOOL needBackBarButton = (1 < [self.navigationController.viewControllers count]) ? YES : NO ;
if (! needBackBarButton)
{
self.navigationItem.leftBarButtonItem = nil;
}
For other controllers
if (needBackBarButton)
{
// Create custom navigationItem here.
}
else
{
self.navigationItem.leftBarButtonItem = nil;
}
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.
I am building an application which should allow the user to scroll through the images. Since I have many images (downloaded from the web), what i doing is I have UP and down button on the parent view, in addition i have the scroll view. Based on the selected option (up or down), I add the ImageClass ( created a class which extends UIViewController) view to the scroll view.
Now, on the selected view the user can mark a point or do any stuff.
The question is how from the parent view I can call methods of the Uiviewcontroller. I know how I can set the delegate methods but what I want is that parent controller can call any a method say redraw method which would redraw the entire view.
Code:
-(IBAction) down:(id) sender{
[scrollView2 removeFromSuperview];
}
-(IBAction) down :(id) sender {
[scrollView2 removeFromSuperview];
if(scrollView2 == nil)
scrollView2 = [[UIScrollView alloc] init];
[self.view addSubview:scrollView2];
[self.view sendSubviewToBack:scrollView2];
[scrollView2 setBackgroundColor:[UIColor blackColor]];
[scrollView2 setCanCancelContentTouches:NO];
scrollView2.clipsToBounds = YES;
// default is NO, we want to restrict drawing within our scrollview
scrollView2.indicatorStyle = UIScrollViewIndicatorStyleWhite;
// CSImageView is a class which of the type UIViewController
imageVie = [[CSImageView alloc] init];
[scrollView2 addSubview:imageVie.view];
[scrollView2 setContentSize:CGSizeMake(1500,1500)];
[scrollView2 setScrollEnabled:YES];
[imageView release];
}
Now, from the parent view controller I want to call say:
imageVie.redraw(); method
Code for CSImageView
#interface CSImageView : UIViewController {
NSNumber *imageId ;
}
#property (nonatomic, retain) NSNumber *venueId;
-(void) redraw;
#end
#implementation CSImageView
#synthesize imageId;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[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 from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void) redraw {
NSLog(#"I am ehre in the test function");
}
#end
Can you please help me on the same. I am not able to call the redraw method. Any input would be appreciated.
First, CSImageView should probably be a subclass of UIView, not UIViewController. You should keep a reference to your instance (i.e. as an instance variable or synthesized property) so you can access it from methods other than - down:.
Wow.
I can't say I follow what you've coded, but here's how you do it.
During this part...add a tag to the view
// CSImageView is a class type UIViewController
imageVie = [[CSImageView alloc] init];
imageVie.tag = 99 // Any number. Preferably a constant
[scrollView2 addSubview:imageVie.view];
...
Then on the parent
view controller. You can ...
-(IBAction) down:(id) sender {
CSImageView *view = [scrollView2 viewWithTag:99];
[view redraw];
... }
Something like that.