Does UIWindow randomly remove its subview? - iphone

The reason I ask is because I was doing the following in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
PageViewController *pageViewController = [[PageViewController alloc] init];
[window addSubview:pageViewController.view];
[pageViewController release];
[window makeKeyAndVisible];
return YES;
}
But, when I tried to scroll the pageView, whose controller implements the UIScrollViewDelegate protocol, I got an error like:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL scrollViewDidScroll:]: unrecognized selector sent to instance 0x5f42a80
When I took out [pageViewController release];, this error went away. This is weird to me because window should retain ... OH GOD!!! duh... it retains the pageViewController's view, not the pageViewController.
I get it now why it's wrong to release pageViewController. Silly me... I think it's time for a break.

It looks like you figured this part out, but the answer to your question is no, UIWindow does not randomly remove its subview AFAIK. In this case, it's retaining pageViewController.view not pageViewController. So, you shouldn't release pageViewController nor should you release pageViewController.view because pageViewController.view will automatically get released when pageViewController is released. I don't see where you are releasing pageViewController. I recommend making it an ivar of the AppDelegate and then releasing it in the AppDelegate's dealloc method, as you've probably done with window. The dealloc method never gets called, so pageViewController (and window) will never explicitly get released anyway, but making pageViewController an ivar is better style IMHO. Either way, I'll bet they get cleaned up when the application terminates.

Related

Why applicationDidFinishLaunching is called after viewDidLoad? [duplicate]

I've been playing with the iPad's SplitView template in Xcode. Here are two of the many important methods that are auto-generated for you by the Split View-based Application template...
AppNameAppDelegate.m
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch
rootViewController.managedObjectContext = self.managedObjectContext;
// Add the split view controller's view to the window and display.
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
return YES;
}
RootViewController.m
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
When you build and run the project before making any changes at all, the application:didFinishLaunchingWithOptions method is called before the RootViewController:viewDidLoad method is called. I'm new to iPhone development, but I'm assuming this is the correct and typical sequence. So here are the changes I made...
Once I confirmed everything was working without any modifications, I changed the RootViewController code and set it as a subclass of UIViewController (instead of UITableViewController by default) and made the respective adjustments in Interface Builder. I built and ran, everything was still working fine.
Then, I added a UIView (with nothing in it) to the RootView in IB and when I built and ran it, suddenly the RootViewController:viewDidLoad is being called before the application:didFinishLaunchingWithOptions method.
I need to get it back to the way it was working before because, as you can see in the code, the viewDidLoad method depends on didFinishLauchingWithOptions method to execute so it can set the rootViewController's managedObjectContext that it uses to perform the fetch request.
Any ideas what caused this?
Any ideas how I can fix this?
Thanks so much in advance for your help! I'm gonna keep researching and playing with the code.
In the template app -applicationDidFinishLaunching adds RootViewController's view to the window, causing the view to load, so obviously -viewDidLoad will follow - applicationDidFinishLaunching.
ViewDidLoad is (indirectly) called from applicationDidFinishLaunching.
If, as you say, viewDidLoad is being called before applicationDidFinishLaunching it is because you have done something to cause the view to load before applicationDidFinishLaunching is called.
Did you add a breakpoint in -viewDidLoad and look at the stacktrace to see what was responsible for calling it?
Where are you initializing RootViewController? Typically, you do so in applicationDidFinishLaunching (at least on the iPhone). If you are initializing it in your app delegate's init method, that could cause the root view controller's viewDidLoad method to be invoked before applicationDidFinishLaunching.
This is propabaly caused by the fact that in MainWindow.xib, your application delegate object is not connected to File's Owner (UIApplication). You can open the MainWindow.xib and right click on your App Delegate to see if it has a connection in Referencing Outlet to File's Owner. If not, set it to. And this will fix your problem.

iPhone: this class is not key value coding-compliant for the key alarmsViews

I've been banging my head with this error for the last 5 hours. I have done all the googling I could yet none of the solutions seem to work for me so I will explain my particular situation and see if you guys can pick out what my issue is.
My main UI is a View with 6 Views inside of it. Each view contains an image and a label inside of it. Am I allowed to do this to contain objects together?
Anyways, I erased all my connection one by one from the File Owner and started over again. I pressed ctrl and dragged the mouse over to the main view to attach it to my view. Ran the project: great, no errors.
Connected one of the sub views, bam! Error pops up right away.
My File Owner's custom class is set to my UIViewController class.
I have the IBOutlets defined properly in the header, and synthesized in the implementation.
Let me know what you need to help me out.
** EDIT **
I set a breakpoint and found the line marked with --> <-- to be the culprit:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds];
self.viewController = [[UIViewController alloc] initWithNibName:#"View_main" bundle:nil];
self.window.rootViewController = self.viewController;
--> [self.window makeKeyAndVisible]; <--
return YES;
}
And the error thrown by the compiler is:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIViewController 0x6c6a0d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myView.'
All help welcomed!
check this link you'll find the answer here
The error “this class is not key value coding-compliant for the key XXX” usually occurs when loading a nib that refers to a property that doesn’t actually exist. This usually happens when you remove an outlet property from your code but not from the connections in the nib.
My File Owner's custom class is set to my UIViewController class.
This is ambiguous, and clashes with:
self.viewController = [[UIViewController alloc] initWithNibName:#"View_main" bundle:nil];
You are using a "vanilla" UIViewController rather than your custom subclass (whose class name you haven't mentioned). When the nib is loaded and it attempts to connect your outlets, they do not exist, so the exception is raised. Change the alloc/init to use your UIViewController subclass:
#import "View_main.h"
...
self.viewController = [[View_main alloc] initWithNibName:#"View_main" bundle:nil];
And ensure that the custom class of your view controller is set to View_main in the nib as well.

iPhone: unable to start simple application

I'm trying to make an iPhone app here, and I've gotten it down to a simple HelloWorld problem. For some reason, the following does not work in XCode 4.4. I'd really appreciate figuring out what's going on.
I follow these steps:
Start an 'empty project' type
Name it
Add in a new objective-c class with a .xib. Say this new view controller is StartViewController, so I now have StartViewController .xib, .h, and .m.
Check: file's owner for the .xib matches the .h file. It does in IB.
Change the background of the .xib to something other than black (I like stripes).
Add these lines to the main app delegate:
import "StartViewController.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
StartViewController* theController = [[StartViewController alloc]init];
[self.window addSubview:theController.view];
[self.window makeKeyAndVisible];
return YES;
}
And the app immediately crashes on running with:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "StartView" nib but the view outlet was not set.'
How can I make it work?
EDIT: PS, I have no idea why the code formatting appears to have failed. I was under the impression that it was just four spaces at the beginning of a line...
Do what am error tells you to do. Set an outlet for Startview in your VC.
I'm answering this because it's so ludicrous.
Turns out that order matters.
I deleted the old controlling files, and then added 'startingviewcontroller', edited the appdelegate function to be startingviewcontroller, then it all works. Not sure why that should matter, but hey.

How to dealloc uiviewcontroller/unload views on logout action

I think this is a pretty common usecase as I have seen it in several apps. But after spending couple of days, I am still struggling with this. I have a structure like the following:
UITabBarController
-- UINavigationController1
---- UITableViewController1
-- UINavigationController2
---- UITableViewController2
Now I have a logout button on UITableViewController2. When I click on that logout button I want all and any viewcontroller to be deallocated, all view unloaded. Basically start fresh like launching the app. I basically want the viewDidLoad on each of those UITableViewController called again.
I tried the following method to be called in my appdelegate when the logout action on UITableViewController2 is taken.
-(void) logout {
for (UINavigationController* ctrl in self.tabBarController.viewControllers) {
[ctrl popToRootViewControllerAnimated:NO];
ctrl.visibleViewController.view = nil;
}
[self.tabBarController.view removeFromSuperview];
[self.window addSubview:self.tabBarController.view];
}
But alas, it does not seem to work?
Any ideas how such a thing is accomplished? Also I see different behaviors in iOS4 vs iOS5 with the visibleViewController. I am not using any modal viewcontroller here. Any gotchas?
Update: I am not using ARC
thanks
mbh
Your for-loop will release and thus dealloc any view controllers that you have pushed onto the respective UINavigationController roots (depending on how many tabs you have), i.e. as these will not have a superview when you pop back to the root of each navigation controller, these are dealloc-ed automatically. These are your UITableViewControllers taken care of.
As for the respective UINavigationControllers, you would need your tabbar-controller to release the old instance. IMHO, this should be done for you when you release the UITabBarController.
This then leaves the UITabBarController itself. I don't think it can be done tbh. Your code will only remove the view, but not dealloc the tabbar controller itself. And as Krishna K points out, you need at least one view controller to reload all others.
Putting the code into the appdelegate makes sense, but you need to ensure that your logout() will not cause a retain on the UITableViewController2 as well as the UITabbarController as it's called from UITableViewController2 somewhere.
One idea to explore, does your AppDelegate hold an instance to the TabBar-Controller which you could release and create a new instance after removing the view from self.window?
// manually create UITabBarController - AppDelegate holds instance
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mytabcontroller=[[UITabBarController alloc] initWithNibName:#"foo" bundle:nil];
}
- (void) logout {
[self.tabBarController.view removeFromSuperview];
[mytabcontroller release];
mytabcontroller=[[UITabBarController alloc] initWithNibName:#"foo" bundle:nil];
[self.window addSubview:self.tabBarController.view];
}
But as I said, there might be caveats with memory management at this point.
You need to release your view controllers. When their release method is called, that method should include statements to release all of its object's resources (and also dealloc its superclass).
Your rootViewController for both Navigation controllers are their respective TableView controllers. So I don't think popToRootViewController would do anything.
You probably need to reset the data and refresh the views instead of deallocating the views.

UIViewController custom titleView crashes app

I have a navigation based iPhone app.
When you press on a cell in the tableview a new UIViewController is pushed to the navigation stack. In this view controller I am setting a custom titleView in the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
// Setup custom navigation title
[self setTitle:#"Mediaportal"];
navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];
[navItem setTitle:[theServer name]];
[navItem setSubTitle:#""];
[self.navigationItem setTitleView:navItem.view];
…
}
Once I switch back to the RootViewController:
[self.navigationController popToRootViewControllerAnimated:YES];
the app crashes with the following error (NSZombieEnabled=YES):
*** -[CALayer retain]: message sent to deallocated instance 0x5a5fd80
From what I can see the RootViewController still tries to access the custom titleView, which was deallocated with the second view controller. Once I comment out the custom titleView part in my code the app works.
I tried to set the navigationItem.titleView to nil (as found in the apple docs) before the second ViewController is deallocated, but that doesn't help.
Do you have a hint what I can do to prevent this crash?
Thanks,
Mark.
I had this exact same error a month or so ago, exactly the same situation. It drove me NUTS.
I discovered that the viewController i was popping too hadn't been deallocated at all. I had a custom UIButton subclass added to that view however that had been deallocated when the second view had been pushed. So when popping back, the UIButton wasn't there.
Check the view you are popping back to, to ensure you've not got any classes that you are deallocating, or are being autoreleased without you knowing.
Hope this helps.
I finally found the solution for it (a quite simple one).
I have to alloc and init the navItem through its property then it is being retained:
self.navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];