Setting self.window.rootViewController causes SIGABRT - ios

I am trying to do an application where you press a button then a UIAlertView comes up and there is a UIView inside it with 3 custom button. So far everything work. When i click on one of the 3 button i want to change the image of an UIImageView and that works as well. The problem is that out of nowhere a sigabrt is now happening each time i try to start my apps.
The SIGABRT happen in my AppDelegate.m on this line:
self.window.rootViewController = self.viewController;
If somebody can help me that would be great and by the way im not very used to xcode and objective-c so i don't have a clue why this is happening.
Here is my viewController.h
#import UIKit/UIKit.h (i removed < > cause they were hiding everything inbetween in the post.)
#interface savingstatusViewController : UIViewController {
UIView *loginView;
UIImageView *statusImage;
UIAlertView * MyTicketAlert;
}
- (IBAction)status:(id)sender;
#property (nonatomic, retain) IBOutlet UIView *loginView;
#property (nonatomic, retain) IBOutlet UIImageView *statusImage;
#property (nonatomic, retain) IBOutlet UIAlertView * MyTicketAlert;
- (IBAction)green:(id)sender;
- (IBAction)yellow:(id)sender;
- (IBAction)red:(id)sender;
#end
Here is my viewController.m
#import "savingstatusViewController.h"
#implementation savingstatusViewController
#synthesize loginView;
#synthesize statusImage,MyTicketAlert;
- (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];
}
- (void)viewDidUnload
{
[self setLoginView:nil];
[self setStatusImage:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
- (IBAction)status:(id)sender
{
MyTicketAlert = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:self cancelButtonTitle:nil
otherButtonTitles: nil];
[MyTicketAlert addSubview:loginView];
[MyTicketAlert show];
MyTicketAlert.frame = CGRectMake(0, 1000, 440, 110);
}
- (IBAction)green:(id)sender
{
statusImage.image = [UIImage imageNamed:#"etat_controle.png"];
[MyTicketAlert dismissWithClickedButtonIndex:0 animated:YES];
}
- (IBAction)yellow:(id)sender
{
statusImage.image = [UIImage imageNamed:#"etat_attention.png"];
[MyTicketAlert dismissWithClickedButtonIndex:0 animated:YES];
}
- (IBAction)red:(id)sender
{
statusImage.image = [UIImage imageNamed:#"etat_danger.png"];
[MyTicketAlert dismissWithClickedButtonIndex:0 animated:YES];
}
#end

The UIWindow rootViewController property doesn't exist before iOS4. If you're trying to run this code on an device with iOS 3 or older, it will crash.
In your AppDelegate, you can use addSubview instead.
//self.window.rootViewController = self.viewController; // Only iOS >= 4
[self.window addSubview:self.viewController.view];
[self.window makeKeyAndVisible];
return YES;

This also happens if the file's owner view has not been set or set incorrectly. Ensure the File's Owner view property of your ViewController.xib is set to your view. Took me a while to figure that one out!

go to "TheNameOfYourProjetdelegate" in MainWindow.xib.
In "outlets" of "TheNameOfYourProjetdelegate" you must have viewController, then do a "control drag" to your window et run your application

Related

view's frame not releasing its memory

I'm not able to rectify the situation which is arising in
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
where, memory assigned every time and not releasing after dismissing the view.
Well, the situation is that, I have created a simple login screen as rootviewcontroller, then through a button (login button) presenting a tabviewcontroller where, I have added a view as subview on tabviewcontroller, on that subview four buttons are there, through delegate I'm controlling the tabIndex, everything is working fine, but when I check about the leaking memory, above view.frame keeping the memory.
I'm posting the image, please look at this,
Here is my TabView Controller with custome tabbar.
Any help will be appreciable.
UPDATE:
Here is my complete code.
From Login Page:
- (IBAction)clickLoginBtnAction:(id)sender {
tabControler = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarController"];
[self presentViewController:tabControler animated:YES completion:nil];
}
From Tabbar Controller:
#import "TabBarController.h"
#interface TabBarController ()<TabBarIndexProtocol>{
// CustomTabViewController *customTabView;
}
#property (strong) CustomTabViewController *customTabView;
#end
#implementation TabBarController
#synthesize customTabView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
customTabView = [self.storyboard instantiateViewControllerWithIdentifier:#"CustomTabViewController"];
self.tabBar.hidden = YES;
[self frameCustomTabViewOnTabBar];
customTabView.tabBarIndexDelegate = self;
}
-(void)viewDidDisappear:(BOOL)animated{
// customTabView.view.frame = nil;
}
// For Screen Rotation
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self frameCustomTabViewOnTabBar];
}];
}
-(void)frameCustomTabViewOnTabBar{
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
[self.view addSubview:customTabView.view];
}
# pragma mark - TabBarIndexProtocol from CustomTabBarClass
-(void)tabBarIndexSelected :(int)tabNumber{
NSLog(#"%d",(int)tabNumber);
[self setSelectedIndex:tabNumber];
}
In My CustomeTab.h File:
#import <UIKit/UIKit.h>
#protocol TabBarIndexProtocol <NSObject>
#required
-(void)tabBarIndexSelected :(int)tabNumber;
#end
#interface CustomTabViewController : UIViewController
#property(weak) id <TabBarIndexProtocol> tabBarIndexDelegate;
#end
and from its .m file
#import "CustomTabViewController.h"
#interface CustomTabViewController ()
#end
#implementation CustomTabViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)tabNumberOne:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:0];
}
- (IBAction)tabNumberTwo:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:1];
}
- (IBAction)tabNumberThree:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:2];
}
- (IBAction)tabNumberFour:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:3];
}
Thats all, where I'm mistaking :( .

Pushing UIViewController fails with "Application tried to push a nil view controller on target"

So I'm using a NavigationController to do all the navigation of my iOS App. On the main screen there is a 'plus button' to take an image, and on pressing this button the NavigationController pushes the 'CameraViewController' with the method 'displayCamera:'. There the user can take an image and it is supposed to be previewed by another UIViewController which is pushed by the function 'picTaken:'. However overtime I try to push this view controller I get the error "Application tried to push a nil view controller on target". I have no idea what is going wrong. Below is the NavigationController.h and the corresponding .m file
NavigationController.h
#import <UIKit/UIKit.h>
#import "CameraViewController.h"
#import "MainViewController.h"
#import "PictureCheckViewController.h"
#interface NavigationController : UINavigationController {
CameraViewController *camView;
MainViewController *mainView;
PictureCheckViewController *checkView;
}
- (void)displayCamera:(id)sender;
- (void)picTaken:(id)sender;
#end
NavigationController.m
#import "NavigationController.h"
#interface NavigationController () {
}
#end
#implementation NavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
camView = [[CameraViewController alloc] init];
mainView = [[MainViewController alloc] init];
checkView = [[PictureCheckViewController alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)displayCamera:(id)sender {
[self pushViewController:camView animated:YES];
}
- (void)picTaken:(id)sender {
[self pushViewController:checkView animated:YES];
}
#end
If you need anymore information please tell me.
I appreciate your time and help :)
I would recommend you use a lazy instantiation method.
Add a property :
#Property(nonatomic,strong) MainViewController *mainView;
#Property(nonatomic,strong) PictureCheckViewController *checkView;
and override the getters yourself:
-(MainViewController*) mainView {
if(!_mainView) {
_mainView = [[MainViewController alloc] init];
}
return _mainView;
}
And like wise for checkView.
-(PictureCheckViewController*) checkView {
if(!_checkView) {
_checkView = [[PictureCheckViewController alloc] init];
}
return _checkView;
}
then access using:
self.checkView
self.mainView
Aside from I believe fixing your problem, it would also avoid creation unless the resource is actually used.

iOS Popup broken after converting to ARC

Hey I converted my project to ARC automatically and the only thing I had to fix was that Xcode changed array that had a size of [8] to [4] (still don't know what was going on there)
However, I now realized I got another problem: every time I hit a button in my Popup, the app crashed if it is linked to an IBAction. If I remove the reference to the header and main file, the button is clickable, but as soon as i assign it to a method (even if the method is empty), the whole app jut freezes/crashes.
This is how I initiate my popup:
PopUpViewController *popViewController =
[[PopUpViewController alloc] initWithNibName:#"PopUpViewController" bundle:nil];
[popViewController setTitle:#"This is a popup view"];
[popViewController showInView:self.view
animated:YES];
popViewController.view.center=self.view.center;
Pretty basic stuff, I ripped it off a tutorial I found online. My header is this:
#interface PopUpViewController : UIViewController
- (IBAction)baa:(id)sender;
#property (strong, nonatomic) UIButton *ba; // I was trying to add those buttons as properties here
#property (strong, nonatomic) IBOutlet UIButton *kl; //but no luck whatsoever
#property (strong, nonatomic) IBOutlet UIView *popUpView;
#property (strong, nonatomic) IBOutlet UIView *ppp;
- (IBAction)openSomething:(id)sender;
- (IBAction)openYoutube:(id)sender;
- (IBAction)openFacebook:(id)sender;
- (void)showInView:(UIView *)aView animated:(BOOL)animated;
#end
And my main
#import "PopUpViewController.h"
#interface PopUpViewController ()
#end
#implementation PopUpViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)showAnimate
{
// not important for this question
}
- (void)removeAnimate
{
// shortened
}
- (IBAction)baa:(id)sender{
// NSLog(#"asd"); I commented it out, but even an empty method kills the app
// only if I leave a button unassigned to any method, the app "survives" a button click
}
- (IBAction)openSomething:(id)sender
{
// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.test.de"]];
}
- (IBAction)openYoutube:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://www.youtube.com/"]];
}
- (IBAction)openFacebook:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://www.facebook.com/"]];
}
- (void) doSomething{
// also tried an empty method with a "new" name over here to eliminate any caching errors, no luck
}
- (IBAction)closePopup:(id)sender {
[self removeAnimate];
}
- (void)showInView:(UIView *)aView animated:(BOOL)animated
{
[aView addSubview:self.view];
if (animated) {
[self showAnimate];
}
}
- (void)viewDidLoad
{
self.view.backgroundColor=[[UIColor blackColor] colorWithAlphaComponent:.0];
self.popUpView.layer.cornerRadius = 5;
self.popUpView.layer.shadowOpacity = 1.0;
self.popUpView.layer.shadowOffset = CGSizeMake(0.2f, 0.2f);
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Any ideas what I could do different? I referenced everything to everything else possible now and I don't even know how it looked before anymore, but I somehow think it has something to do with the referencing.
Thanks a lot in advance, Alex
I was able to figure out on my own:
In the header of my main ViewController I added
#property (strong, nonatomic) PopUpViewController *popViewController;
Which still fired the crash. But then I adjusted the popup "opening" block to
_popViewController =
[[PopUpViewController alloc] initWithNibName:#"PopUpViewController" bundle:nil];
[_popViewController setTitle:#"This is a popup view"];
[_popViewController showInView:self.view
animated:YES];
_popViewController.view.center=self.view.center;
(added the underscores) and now it's working perfectly again.
Actually I am curious why it worked before but I am not going to investigate this any further.

ARC and autoreleased object

I need a ViewController to be called modally to show some UIButton and other UIView on top of the current window. I want the background to be partially transparent and showing the current window below it - something similar to a UIActionSheet but with a custom design. I coded my VC to do the following: 1) during init the VC sets self.view.frame equals to [[UIApplication sharedApplication]keyWindow].frame 2) when show() is called the VC adds self.view on top of [[UIApplication sharedApplication]keyWindow] subViews 3) when an internal button calls the private method release() the VC remove self.view from its superview. Example with a single release button as follows:
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
//set my view frame equal to the keyWindow frame
self.view.frame = [[UIApplication sharedApplication]keyWindow].frame;
self.view.backgroundColor = [UIColor colorWithWhite:0.3f alpha:0.5f];
//create a button to release the current VC with the size of the entire view
UIButton *releaseMyselfButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[releaseMyselfButton setTitle:#"Release" forState:UIControlStateNormal];
releaseMyselfButton.frame = CGRectMake(0, 0, 90, 20);
[releaseMyselfButton addTarget:self action:#selector(releaseMyself) forControlEvents:UIControlEventTouchDown];
//add the button to the view
[self.view addSubview:releaseMyself];
}
return self;
}
- (void) show
{
//add self.view to the keyWindow to make sure that it will appear on top of everything else
[[[UIApplication sharedApplication]keyWindow] addSubview:self.view];
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
}
#end
If I create an instance of ModalController from another VC and I call show() everything goes as expected:
#interface CurrentVC ()
#property (strong, nonatomic) ModalController *myModalController;
#end
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.myModalController = [[ModalController alloc]init];
[self.myModalController show];
}
#end
To make it work I need to retain the ModalController in a property until release () is called. However I would like to have the same freedom I have with UIActionSheet and simply keep an instance of it in a local variable:
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
ModalController *myModalController = [[ModalController alloc]init];
[myModalController show];
}
#end
If I do this with the current code ARC will release myModalController straight after show() is called and the release button will be pointing to nil. How can I make this work without storing the object in a property? I've identified a work around but I'm not sure it's a good design option:
#interface ModalController ()
#property (strong, nonatomic) ModalController *myselfToAutorelease;
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
... ... ...
self.myselfToAutorelease = self;
}
return self;
}
- (void) show
{
... ... ...
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
self.myselfToAutorelease = nil;
}
What I've done is making ModalController "self sufficient" - it stores a pointer to itself during init and set it to nil when it's ready to release himself. It works but I have the feeling that this is against the ARC design principles! Is this approach correct? If not, how can I handle this differently?
Thanks in advance for your help.
Doesn't work like that.
You don't keep a reference to self.
In the main view controller you just create your object. If you need it to be around longer keep it in a property in the main view controller , when done, set the property to nil in the main view controller.

iOS move database update off the main thread - insert a splash view

I'm new to objective-c so please bear with this long explanation, I hope it will help other beginners. I have been successfully making some changes to an existing iPad app. However, the original install/update routine has hit the failed to launch in time barrier. The posts here have helped me greatly to understand the problem and which direction(s) to research.
I have compiled a solution from different posts here and elsewhere as I did not find a global line-by-line for beginners solution.
I understand that I need to pull the database init/update out of didFinishLaunchingWithOptions and return from here asap with an instantiated UIViewController that will do the DB stuff off the main thread (thanks to all the posters on the subject).
NOTE that the rootVC that is usually called here cannot init if the data is not ready and intact. So just going async on the DB routine doesn't help me because the rootVC gets there first and bombs out when it doesn't find the data it requires.
i.e. I need to delay the rootVC while we do anything we need to do and in peace. I choose to load the UILaunchImage to be seamless and add a spinner.
The question is:
1) Have I done it correctly so that I will never get bitten and 8badf00d again and especially without adding other side effects? Or should I have done it otherwise, maybe in a wrapper init method of the existing rootVC?
2) What about the dealloc, rootViewController or splashViewController? I would think that it is rather rootViewController by this stage. Confused.
3) It works but is this really replacing (and removing) splashViewController by rootViewController as the rootVC? Or am I piling them up...
BEFORE
RAppDelegate.h
#import <UIKit/UIKit.h>
#import "RDataManager.h"
#import "RRootViewController.h"
#import "RScreenViewController.h"
#interface RAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
RRootViewController *rootViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, readonly) RRootViewController *rootViewController;
#end
RAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[RDataManager sharedManager] updateDatabase]; // This is what takes time...
rootViewController = [[RRootViewController alloc] initAtScreen:kScreenTypeIndex withCar:carId];
rootViewController.wantsFullScreenLayout = YES;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
...
- (void)dealloc
{
[rootViewController.view removeFromSuperview];
[rootViewController release];
[window release];
[super dealloc];
}
AFTER
RAppDelegate.h
#import <UIKit/UIKit.h>
#import "RDataManager.h"
#import "RScreenViewController.h"
#import "RSplashViewController.h"
#interface RAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
RSplashViewController *splashViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, readonly) RSplashViewController *splashViewController;
#end
RAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
splashViewController = [[RSplashViewController alloc] init];
splashViewController.wantsFullScreenLayout = YES;
self.window.rootViewController = splashViewController;
[self.window makeKeyAndVisible];
return YES;
}
...
- (void)dealloc
{
[splashViewController.view removeFromSuperview];
[splashViewController release];
[window release];
[super dealloc];
}
RSplashViewController.h
#import <UIKit/UIKit.h>
#import "RRootViewController.h"
#interface RSplashViewController : UIViewController
{
UIImageView *splashImageView;
RRootViewController *rootViewController;
UIActivityIndicatorView *spinner;
}
#property (nonatomic, retain) UIImageView *splashImageView;
#property (nonatomic, readonly) RRootViewController *rootViewController;
#end
RSplashViewController.m
#import "RSplashViewController.h"
#import "RDataManager.h"
#interface RSplashViewController ()
#end
#implementation RSplashViewController
#synthesize splashImageView;
#synthesize rootViewController;
- (void) loadView
{
CGRect appFrame = [UIInterface frame];
UIView *view = [[UIView alloc] initWithFrame:appFrame];
self.view = view;
[view release];
NSString *splashFile = [[NSBundle mainBundle] pathForResource:#"LaunchImage-jaguar-Landscape~ipad" ofType:#"png"];
UIImage *splashImage = [[UIImage alloc] initWithContentsOfFile:splashFile];
splashImageView = [[UIImageView alloc] initWithImage:splashImage];
[self.view addSubview:splashImageView];
[splashImage release];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
CGRect frame = spinner.frame;
frame.origin.x = CGRectGetMidX(self.view.frame) - CGRectGetWidth(spinner.frame) / 2;
frame.origin.y = 650;
spinner.frame = frame;
spinner.hidesWhenStopped = YES;
[self.view addSubview:spinner];
[spinner startAnimating];
}
- (void) viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// how we stop DB refresh from freezing the main UI thread
dispatch_queue_t updateQueue = dispatch_queue_create("updateDB", NULL);
dispatch_async(updateQueue, ^{
// do our long running process here
[[RDataManager sharedManager] updateDatabase];
// do any UI stuff on the main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
[splashImageView removeFromSuperview];
rootViewController = [[RRootViewController alloc] initAtScreen:kScreenTypeGarage withCar:nil needSplashScreen:YES];
[self.view addSubview:rootViewController.view];
});
});
dispatch_release(updateQueue);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) dealloc
{
[super dealloc];
}
#end
To answer my questions.
1) Yes this is safe, either with SplashViewController or rootVC init wrapper. Ran it as ad-hoc for 2 minutes no problems. Double checked that the update was indeed running in a separate thread (Xcode debug window pane).
2) Non issue since I moved to a rootVC init wrapper solution and reverted to dealloc'ing rootViewController.
3) Problem solved by 2).
I also managed to reduce the update time by 90% which is the ideal solution anyway, but good to know that I am also safely out of the way of the watchdog timer.

Resources