Objective-c: recursive call of viewDidLoad after addSubview - objective-c

I am working in Xcode 4.5.2.
I have the following problem:
When i make projects from non-empty projects - that is the views are initialised from the storyboard, sometimes when i add the code
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
b.frame = CGRectMake(20, 20, 100, 100);
[self.view addSubview:b];
to the viewDidLoad method it is recursively called again after the addSubview method call and sometimes (bless the sky) it doesn't.
Can anyone tell me what's the problem and how to solve it?
My gratitude.

Edit: As #WDUK correctly states, the following answer is wrong. It would explain recursive in loadView, not viewDidLoad.
Old, wrong answer:
Recursive calls occur when after the call to -[super viewDidLoad]
the view property has not been set and you then call self.view.
You probably just forgot to connect the view to the file's owner's
view outlet.

When the device runs out of available memory, it could dealloc the view if it is not currently visible. You should always make sure you handle this behaviour correctly.
I could happen when you for example switch to another view when using a navigation or tabbarcontroller.

Related

NSTextField set cursor

Okay so I feel like there's something obvious I'm missing in this question. I've used makeFirstResponder throughout my code to move from textField 1 to 2, 2 to 3, etc. That seems to work as I want it to, yet when the new view is loaded, I want the cursor to be in textField1, and yet the following code does not place the cursor in textField1 upon load.
- (void) awakeFromNib{
[[[self view] window] makeFirstResponder:textField1];
}
I also tried setInitialFirstResponder, and that didn't have any effect either (I don't even think that would be right.) So, is it because it is in the awakeFromNib method? Can anyone tell me what I'm missing? Thanks in advance.
EDIT - My solution was differed slightly from the accepted answer so I thought I'd post my implementation. Because the view I wanted to set the first responder for was a subview added later (think the second screen of an application wizard), I simply added a setCursorToFirstTextField method:
- (void) setCursorToFirstTextField {
[[[self view] window] makeFirstResponder:textField1];
}
And made sure to call it after I had added the subview to the custom view on the original window.
Yes, you're right about the problem being the location of the method in awakeFromNib. If you log [self.view window] in your awakeFromNib, you'll see that it's NULL. I don't know how exactly you have things set up, but I'm guessing (if this relates to your WizardController question) that you're doing an alloc initWithNibName:bundle: in another class to create your view controller and then adding that controller's view to the view hierarchy. If you throw some logs in there, it will show you that awakeFromNib in the controller class is called after the alloc init, but before the view is added as a subview, so there is no window at that time. The way I got around this problem was to create a setup method in the view controller class (with the makeFirstResponder code in it), and call it from the class where you create the controller after you add it as a subview.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.wizard = [[WizardController alloc] initWithNibName:#"WizardController" bundle:nil];
[self.window.contentView addSubview:wizard.view];
[self.wizard doSetup];
}

Difference between viewDidLoad and loadView?

Two objective-c methods, -(void) viewDidLoad and -(void)loadView are methods called upon execution of a program but whats the different between them?
Do you mean viewDidLoad and loadView? viewDidLoad is a method called when your view has been fully loaded. That means all your IBOutlets are connected and you can make changes to labels, text fields, etc.
loadView is a method called if you're (typically) not loading from a nib. You can use this method to set up your view controller's view completely in code and avoid interface builder altogether.
You'll typically want to avoid loadView and stick to viewDidLoad.
Use -(void)loadView when you create the view. Typically usage is:
-(void)loadView {
UIView *justCreatedView = <Create view>;
self.view = justCreatedView;
}
Use -(void)viewDidLoad when you customize the appearance of view. Exapmle:
-(void)viewDidLoad {
self.view.backgroundColor = [UIColor blackColor];
...
}
i think you are talking about loadView and viewDidLoad.
loadView is a method that you not using a nib file - you use it to programmatically 'write' your interface
viewDidLoad fires automatically when the view is fully loaded. you can then start interacting with it.
more to read read in the discussion here iPhone SDK: what is the difference between loadView and viewDidLoad?

Replacing Storyboard Segue with pushViewController causes strange behaviour

I can't seem to figure this out for the life of me. I have a custom table view cell, in that cell I have a few buttons configured. Each button connects to other view controllers via a storyboard segue. I've recently removed these segues and put a pushViewController method in place. Transition back and forth across the various views works as expected however the destination view controller is not displaying anything! I have some code below as an example.
Buttons have this method set:
[cell.spotButton1 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
[cell.spotButton4 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
showSpotDetails Method contains this code:
- (void)showSpotDetails:(id)sender
{
// determine which button (spot) was selected, then use its tag parameter to determine the spot.
UIButton *selectedButton = (UIButton *)sender;
Spot *spot = (Spot *)[spotsArray_ objectAtIndex:selectedButton.tag];
SpotDetails *spotDetails = [[SpotDetails alloc] init];
[spotDetails setSpotDetailsObject:spot];
[self.navigationController pushViewController:spotDetails animated:YES];
}
The details VC does receive the object data.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"spotDetailsObject %#", spotDetailsObject_.name);
}
The NSLog method below does output the passed object. Also, everything in the details view controller is as it was. Nothing has changed on the details VC. It just does not render anything ever since I removed the segue and added the pushViewController method. Perhaps I am missing something on the pushViewController method? I never really do things this way, I try to always use segues...
Any suggestions?
Welcome to the real world. Previously, the storyboard was a crutch; you were hiding from yourself the true facts about how view controllers work. Now you are trying to throw away that crutch. Good! But now you must learn to walk. :) The key here is this line:
SpotDetails *spotDetails = [[SpotDetails alloc] init];
SpotDetails is a UIViewController subclass. You are not doing anything here that would cause this UIViewController to have a view. Thus you are ending up a with blank generic view! If you want a UIViewController to have a view, you need to give it a view somehow. For example, you could draw the view in a nib called SpotDetails.xib where the File's Owner is an SpotDetails instance. Or you could construct the view's contents in code in your override of viewDidLoad. The details are in the UIViewController documentation, or, even better, read my book which tells you all about how a view controller gets its view:
http://www.apeth.com/iOSBook/ch19.html
The reason this problem didn't arise before is that you drew the view in the same nib as the view controller (i.e. the storyboard file). But when you alloc-init a SpotDetails, that is not the same instance as the one in the storyboard file, so you don't get that view. Thus, one solution could be to load the storyboard and fetch that SpotDetails instance, the one in the storyboard (by calling instantiateViewControllerWithIdentifier:). I explain how to do that here:
http://www.apeth.com/iOSBook/ch19.html#SECsivc

UIButton touches up IBAction causing EXC_BAD_ACCESS with ARC

There have been a few questions on StackOverflow where users have experienced the same problem as I am having. However, none of their solutions fit my case. (See here, here, here and here for some of the SO questions I've read but have not found helpful.)
In my case, I have a NIB that has a couple UIButtons, with an associated Controller View. The view is relatively old to my project and I've been able to use these buttons without any trouble until today. After making a few code changes that were not related to the button behavior, I've run into an error that crashes the app, breaks the code at the main() function and gives me an EXC_BAD_ACCESS error message whenever I touch any of the buttons on my View.
How or why might this happen? I've actually commented out almost all functional code, especially that which I modified earlier today and I still can't stop the error from occurring.
My project is using Automatic Reference Counting and I haven't seen this error before. Furthermore, I did not modify the NIB, nor the IBAction associated with the buttons so I don't see what would cause this. The only way to stop the error is to unlink my UIButtons in my NIB to the IBActionmethods defined in my Controller View header file.
The only "unique" aspect of my use-case is that I load either one or two instances of this view, within another sub view controller. The number of instances of the broken view that are loaded is contingent on the number of objects in an array. Below is the code that I use to instantiate and load these views as subviews of another view.
//Called else where, this starts the process by creating a view that
//will load the problematic view as a sub-view either once or twice.
- (id)initWithPrimarySystemView:(SystemViewController *)svc
{
//First we create our parent, container view.
self = [super initWithNibName:#"ContainerForViewInstaniatedFromArrayObjs" bundle:nil];
if (self)
{
//Assign parent DataModel to local instance
[self setDataModel:((DataModelClass*)svc.DataModel)];
for (AnotherModel* d in DataModel.ArrayOfAnotherModels)
{
//Instantiate the SubViewController.
SubViewController* subsvc = [[SubViewController alloc]
initWithNibName:#"Subview"
bundle:nil
subviewPosition:d.Position ];
//Add the SubViewControllers view to this view.
[subsvc.view setFrame:CGRectMake((d.Position-1)*315, 0, 315, 400)];
[self.view addSubview:subsvc.view];
}
[self setDefaultFrame: CGRectMake(0, 0, 640, 400)];
}
return self;
}
This works perfectly and, previously, hadn't even caused any trouble with the buttons that were on the associated view, however, now all UIButtons crash the app when tapped.
The initialization function for SubViewController, as well as the viewDidLoad method contain nothing other than the standard, auto-generated code that is added when you create a new ViewController.
What can I do to either fix or diagnose this problem?
See my comments in your code:
{
SubViewController* subsvc = [[SubViewController alloc] initWithNibName:#"Subview" bundle:nil subviewPosition:d.Position ];
//!i: By default, subsvc is a __strong pointer, so your subview has a +1 retain count
// subsvc owns subsvc.view, so subsvc.view has a +1 retain count as well
//Add the SubViewControllers view to this view.
[subsvc.view setFrame:CGRectMake((d.Position-1)*315, 0, 315, 400)];
[self.view addSubview:subsvc.view];
//!i: This bumps subsvc.view to +2, as self.view strong-references it
//!i: subsvc is going out of scope, so the reference count on subsvc will drop
// to 0 and it is dealloc'd. subsvc.view's retain count drops to +1, as it
// is still referenced by self.view
//
// Most likely, in -[SubViewController dealloc], you were not doing a
// setTarget:nil, setAction:nil on the button. Thus, the button now
// has a dangling pointer and will crash when hit
}
To fix this, add each SubViewController instance to an array owned by the master view controler. That will keep the SubViewController instances around to receive the button taps.
Make sure in your dealloc you call:
[button removeTarget:nil
action:NULL
forControlEvents:UIControlEventAllEvents];
Even though you though you didn't need "dealloc's" in ARC, you do because of what iccir explained.

Trying to dismiss subview and UIView

I'm still very new to iOS developing. In fact, if there is a super noob, I would be one :p. Currently I am working on creating an IBAction button that accesses a subview. I have 2 ViewControllers, AddClientVC and NewClientVC, both with .nib files. So basically, inside my AddClientVC I implement an IBAction button with the following code:
- (IBAction)buttonPressed:(id)sender
{
UIView *transparentBG = [[UIView alloc] initWithFrame:CGRectMake(-5, -5, 1500, 2500)];
transparentBG.backgroundColor = [UIColor blackColor];
transparentBG.opaque = NO;
transparentBG.alpha = 0.5;
[self.view addSubview:transparentBG];
transparentBG.center = transparentBG.center;
vc = [[NewClientVC alloc] initWithNibName:#"NewClientVC" bundle:nil];
[self.view addSubview:vc.view];
vc.view.center = self.view.center;
}
As you can see I implemented a UIView as a transparent background. Basically AddClientVC --> Transparent Background --> NewClientVC. Now I have created another IBAction button but this time inside NewClientVC as a function to dismiss the accessed subview which looks like this:
- (IBAction)saveDismiss:(id)sender
{
[self.view removeFromSuperview];
}
The problem I'm having right now is when I click the saveDismiss button it only removes the subview that I called previously on AddClientVC but it didn't remove the transparent background I have created as a UIView. So the problem is how do I implement an action which simultaneously removes my subview and the UIView transparent background I created.
I need all the help I can get :)
I'm not too sure I fully understand what you want to happen, but maybe you could try something like this?
- (IBAction)saveDismiss:(id)sender
{
[vc removeFromSuperView];
[self.view removeFromSuperview];
}
I recommend not to manage your screens by adding subviews manually but instead use
- (void)presentModalViewController: (UIViewController *)modalViewController
animated: (BOOL)animated
method on your root viewController.
Or better instantiate a UINavigationController and use push and pop methods to drill down/up your views.
See apple reference here
Do not worry about code execution speed and stay confident in apple's SDK. UIKit is optimized for best user experience. Trying to boost your code by doing inappropriate SDK use is, in my opinion, a risky strategy. ;) – Vincent Zgueb
Sorry Vincent but I don't agree with you. I reached here because I want to implement an gesture that adds a sub-view for my view, which will be the navigation of my app.
[self.view addSubview:ctrl.view];
is faster presenting the view than
[self.navigationController presentModalViewController:ctrl animated:NO]
and by the way, the solution to the topic in my case was:
[self.view sendSubviewToBack:ctrl.view];