Why does Instruments detect a leak in the following lines of code in the AppDelegate class? Here is a screenshot from the "leaks" instrument, and below is source code.
AppDelegate implementation file:
#import "AppDelegate.h"
#import "Strings.h"
#implementation AppDelegate
#synthesize window = hWindow;
- (void)dealloc
{
[hWindow release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// [[UIApplication sharedApplication] setStatusBarHidden:YES];
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{ // The iOS device = iPhone or iPod Touch
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
UIViewController *initialViewController = nil;
if (iOSDeviceScreenSize.height == 480)
{ // iPhone 3GS, 4, and 4S and iPod Touch 3rd and 4th generation: 3.5 inch screen (diagonally measured)
// Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone35
UIStoryboard *iPhone35Storyboard = [UIStoryboard storyboardWithName:#"Storyboard_Retina3.5" bundle:nil];
// Instantiate the initial view controller object from the storyboard
initialViewController = [iPhone35Storyboard instantiateInitialViewController];
}
if (iOSDeviceScreenSize.height == 568)
{ // iPhone 5 and iPod Touch 5th generation: 4 inch screen (diagonally measured)
// Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone4
UIStoryboard *iPhone4Storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
// Instantiate the initial view controller object from the storyboard
initialViewController = [iPhone4Storyboard instantiateInitialViewController];
}
// Instantiate a UIWindow object and initialize it with the screen size of the iOS device
[self setWindow:[[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]];
// Set the initial view controller to be the root view controller of the window object
self.window.rootViewController = initialViewController;
// Set the window object to be the key window and show it
} else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{ // The iOS device = iPad
UIViewController *initialViewController = nil;
UIStoryboard *iPhone4Storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
// Instantiate the initial view controller object from the storyboard
initialViewController = [iPhone4Storyboard instantiateInitialViewController];
[self setWindow:[[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]];
// Set the initial view controller to be the root view controller of the window object
self.window.rootViewController = initialViewController;
// Set the window object to be the key window and show it
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
[[self window] makeKeyAndVisible];
NSLog(#"%d",[[self window] retainCount]);
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000
//iOS 7 only stuff here
#endif
return YES;
}
.....
#end
AppDelegate header file:
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
UIWindow *hWindow;
}
#property (strong, nonatomic) UIWindow *window;
#end
May the reason for this be that the app is still running and not terminated in order to deallocate? I would like to mention that in Xcode 4 I was not getting such a leak, it started to appear in Xcode 5.
The leaks tool is only telling you the line of code that caused the leak to be allocated and not why it was leaked. In this case, since it is a run-once method and, assumedly, it is a single object designed to stick around for the lifetime of your app, it isn't a problem.
It is, however, something you should fix, but there isn't enough information here to suggest a fix.
What object(s) is(are) claimed to be leaked?
Also, it is atypical to do things like #synthesize window = hWindow. Don't declare the ivar and just let the compiler automatically synthesize _window. This will result in code much closer to standard pattern.
Related
I'm want to make an app that uses a UISplitViewControler on the iPad (as far as I understand it's only available on the iPad) but I want the app to be universal.
The setup is like this:
I have a UITableView (as a master view) and when I select a row it should display a detail view of that cell. I am using a Storyboard and I can't figure out how to implement the split view only for the iPad.
What would be the easiest way to achieve that? Thanks.
You don't need two storyboards to do this.You can use them both in a single storyboard.For iphone ,we normally use a class SWRevealViewController(if you are new to iOS coding ..:)) for side menu and splitviewcontroller for ipad.We can also use SWRevealViewController for ipad as well.It depends on your requirement.
For universal apps,create viewcontrollers using size Classes(usually we use any height any width for universal apps ).
change these size classes and create different viewcontrollers for ipad and iphones as required.In most cases any height any width will do the job.
After creating the viewcontrollers,in the appdelegate ,using the instantiateViewcontrollerWithIdentifier method, load the required viewcontroller.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// The device is an iPad running ios 3.2 or later.
}
else {
// The device is an iPhone or iPod touch.
}
For ipad load the splitviewcontroller. and swrevealviewcontroller for iPhone.
This is the core basics.If you need any more information,let me know.
EDIT
Have you seen an arrowmark ath the initial VC(viewcontroller) in the storyboard?This vc is loaded first after the launch screen.In my app,I have a home screen which is common to both iphone and ipad(using size classes as mentioned above).So I can set this vc as the initial VC.In this case I don't have to do anything in the appdelegate.But if I have a different home screen for ipad,then I can make a condition check in the appdelegate didFinishLaunchingWithOptions
You can load the First screen like this.You should follow through splitVC tutorilal and swrevealcontroller tutorial to set the side menu.You should load the SWrevealVC or splitViewcontroller only if the first screen contains the side menu.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UISplitViewController *split = [storyboard instantiateViewControllerWithIdentifier:#"SplitViewController"];
[AppDelegate setRootController:split storyboard:storyboard actiontype:0];
}
else if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *split = [storyboard instantiateViewControllerWithIdentifier:#"SWrevealVC"];
[AppDelegate setRootController:split storyboard:storyboard actiontype:-1];
}
return YES;
}
+(void)setRootController:(UIViewController*)controller
storyboard:(UIStoryboard*)storyboard actiontype:(int) actiontype;
{
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && actiontype == 0)
{
UISplitViewController *splitViewController = (UISplitViewController *)controller;
//splitViewController.presentsWithGesture = false;
UINavigationController *masterNavigationController = [splitViewController.viewControllers objectAtIndex:0];
SideMenuViewController *controller = (SideMenuViewController *)masterNavigationController.topViewController;
controller.splitViewController = splitViewController;
splitViewController.delegate = (id)controller;
}
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[UIView
transitionWithView:appDelegate.window
duration:0.5
options:UIViewAnimationOptionAllowAnimatedContent
animations:^(void) {
BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
appDelegate.window.rootViewController = controller;
[UIView setAnimationsEnabled:oldState];
}
completion:nil];
}
The code may look lengthy,but take it simple.You can only understand the logic if u do things.
my app up to iOS 7 works correctly. I tried it today with Xcode 6 and when I run it I have a nasty surprise :(:
You can see that Xcode now draw my viewController as if it is in portrait mode but as you can see is in landscape mode.
Do you know what is changed in iOS 8? :/
I used the following orientation methods:
(NSUInteger)supportedInterfaceOrientations
(BOOL)shouldAutorotate
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
EDIT:
I just discover this method:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskLandscape;
}
And now my first viewcontrollers works correctly :). the problem is when I show a modal (that change the set of method in UIInterfaceOrientationMaskAll) remake the ugly effect of screenshot.
Precisely i change my methods in this mode:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
{
return UIInterfaceOrientationMaskLandscape;
}
else
{
return UIInterfaceOrientationMaskAll;
}
}
// THIS IS A DELEGATE OF MODAL VIEW CONTROLLER
- (void)updateInterfaceAfterDocumentPreviewViewController
{
NSLog(#"updateInterfaceAfterDocumentPreviewViewController");
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = YES;
}
Please try the following code
In the didFinishLaunchingWithOptions of AppDelegate
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]]; //Add
The issue seems to be the order of calls when you set up the window. You need to call makeKeyAndVisible before you assign the rootViewController in your didFinishLaunchingWithOptions method on the app delegate. The following works:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[self.window makeKeyAndVisible];
self.window.rootViewController = self.myMainViewController;
But if you change the order to:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = self.myMainViewController;
[self.window makeKeyAndVisible];
You get the behavior you are experiencing.
I had the same exact problem with a "Loading screen". This is what worked for me. (Please note: my App only supports iOS 7 or later, landscape mode.) :
CGRect theFrame = self.view.frame;
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
CGRect screenBounds = [[UIScreen mainScreen] bounds];
theFrame.origin = CGPointZero;
theFrame.size.width = screenBounds.size.height;
theFrame.size.height = screenBounds.size.width;
}
NSLog(#"%#", [NSNumber valueWithCGRect:theFrame]);
self.loadingScreen = [[UIView alloc] initWithFrame:theFrame];
Please refer to Mike Hay's answer if your App supports portrait orientation and for the "long way to calculate the correct applicationFrame":
https://stackoverflow.com/a/18072095/4108485
Hope this helps.
Have a look at your .plist file.
Make sure you have "Initial interface orientation" set as the orientation you wanted.
In the "Supported interface orientations", have the preferred orientation as the first entry on the list (looks like apple changed this order).
I was having a similar problem, I couldn't get the right Launch Images to show (I had two for iPad, Default-Portrait.png and Default-Landscape.png) and I also couldn't get the App itself to auto orient after the Launch Image disappeared.
To solve the problem for the Launch Images:
Go to the Custom iOS Target Properties section of the project (Click on your Project's Icon at the top of the list of project files, and it should be the first section you see).
Add a row to that table called "Launch image" (should autofill) and enter "Default" as the string in the right column.
Make sure your Launch Images follow Apple's filename guidelines (ie: Default-568#2x, Default#2x, etc) check this SO answer
To solve the app orientation issue at first ViewController:
Go to your projects plist file (should be "YourApp-Info.plist")
Add a row and enter "Supported interface orientations"
Add the orientations that your app supports
That approach to detecting orientation was deprecated in iOS8 (but still allowed for now).
It was replaced with the concept of Size Classes and Trait Collections.
(Oddly, I can't seem to find a Size Class Reference in Apple's docs online right now - would swear it was there before - maybe in XCode). See here for WWDC videos.
Anyway, there's a checkbox in Interface Builder that says "Use Size Classes."
Could be you have that checked. Turn it off & see if that helps?
Oldschool solution:
BOOL portrait;
BOOL iOS8OrLater = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0);
if (iOS8OrLater) {
CGFloat height = CGRectGetHeight(self.view.frame);
CGFloat width = CGRectGetWidth(self.view.frame);
if (width / height > 1.0) {
portrait = NO;
} else {
portrait = YES;
}
} else {
portrait = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);
}
if(portrait) {
[self layoutPortrait];
} else {
[self layoutLandscape];
}
You can call this in viewWillLayoutSubviews
UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
if( UIDeviceOrientationIsLandscape( currentOrientation ) )
{
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
// -- Fix for rotation issue for ios 8
if( IS_IOS_8_OR_LATER )
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad"
bundle: nil];
// -- Set frame of nav controller
UINavigationController *nc = [storyboard instantiateViewControllerWithIdentifier:#"NavController"];
self.window.rootViewController = nc;
[self.window setFrame:[[UIScreen mainScreen] bounds]];
// -- Set fame of home view controller
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"Home"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]];
}
}
}
I wish to create an app with multiple storyboards to support iPhone5, iPhone4 (and below) and iPad screens.
I did the following:
I created 3 storyboards, one per each setting.
I cleared the "main storyboard" field in the project interface.
I cleared the "Main storyboard file base name" field in the app info.plist file.
I entered the following code to the AppDelegate "didFinishLaunchingWithOptions" method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIStoryboard* appStoryboard = nil;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
if (IS_IPHONE_5) //a macro capturing the screen size
{
appStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone5" bundle:nil];
}
else
{
appStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone4" bundle:nil];
}
}
else if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
appStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
}
UIViewController* viewController = [appStoryboard instantiateInitialViewController];
[self.window setRootViewController:viewController];
[self.window makeKeyAndVisible];
return YES;
}
The application is running and not crashing, but I get a black screen.
What am I missing / doing wrong?
You didn't initialize the window.
Just add
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Currently, I have a working app with a single storyBoard. The working app is based on master-detail view layout. I decided to add a new storyboard, and set it to become the main storyboard. When I click run, it will run and display a blank page, which is what I expected as I have not added any view controllers.
I am still new to objective-C, as such, there are many things that I still do not understand, Here comes the interesting part.
When I add a UIViewController(and associated it to a subclass of UIViewController) inside my newly changed Main storyboard, and then tried to run it, it displays an error.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIViewController viewControllers]: unrecognized selector sent to instance 0x7569a80'
Under my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
return YES;
}
I am not so sure what do I need to add to the code above. I did try the below line without success too,
UIViewController *viewController = (UIViewController *)self.window.rootViewController;
As such, how do I solve this problem?
Take storyboard file from New file-->ios-->user interface---> storyboard
create #property storyboard in appdelegate
like
#property (nonatomic, retain) UIStoryboard* storyboard ;
before implementation in .m file write:
#interface AppDelegate ()
#property (strong, nonatomic) UIViewController *initialViewController;
#end
This is to take refrence of initial viewcontroller.
then #synthesize storyboard in appdelegate.m
and then for navigation you can write in didFinishLaunchingWithOptions
//init storyboard
storyboard = nil;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"6.0"))
{
CGSize result = [[UIScreen mainScreen] bounds].size;
if(result.height == 480)
{
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhoneIOS5" bundle:nil];
NSLog(#"Version < 6");
// iPhone Classic
}
if(result.height == 568)
{
// iPhone 5
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
NSLog(#"Version 6");
}
//NSLog(#"IOS 6");
}
else
{
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhoneIOS5" bundle:nil];
//NSLog(#"IOS 5");
}
self.initialViewController = [storyboard instantiateInitialViewController];
//instantiateInitialViewController means it returns the arrow pointed view-controller on storyboard that is root view controller.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = self.initialViewController;
[self.window makeKeyAndVisible];
On Xcode 7 you can go to info.plist and set the key "Main storyboard file base name" to the name of your new Storyboard .
Example: You create a new storyboard file by going to file -> new -> file -> Storyboard file and you save it as "MyAwesomeUI.storyboard" then in info.plist you change the key of "Main storyboard file base name" to "MyAwesomeUI" .
Cheers!
I want to display interface UITabBar when login succeeds.
I declare interface UITabBar in AppDelegate, but after login success I don't know how to call the interface.
Here is my code:
appdelegate.m
-(void)loadInterface
{
[self configureiPhoneTabBar];
}
-(void)configureiPhoneTabBar
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UIViewController *controller1 = [[tabBarController viewControllers] objectAtIndex:0];
[self configureTabBarItemWithImageName:#"home_ON.png" : #"home.png" andText:#"Trang chủ" forViewController:controller1];
UIViewController *controller2 = [[tabBarController viewControllers] objectAtIndex:1];
[self configureTabBarItemWithImageName:#"channel_ON.png" : #"tvChannel.png" andText:#"Kênh" forViewController:controller2];
}
and loginviewcontroller.m
- (IBAction)btnLogin:(id)sender {
[self performSegueWithIdentifier:#"idenLogin" sender:self];
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate loadInterface];
}
Secondly, when you touch on button "play", layout video shows and it works ok, but I want to auto rotate
note: This is interface on iphone and I fix Portrait in Summary, I'm still show landscape, How to do?
Can u download my code demo is here
in couple of words you need modal view for login screen.
Here is how I did it (from app delegate class). Note that I have my login view designed in storytboard.
- (void) showLoginView
{
assert(loginController == nil);
assert(activityView == nil);
UITabBarController *tabbar = (UITabBarController *)self.window.rootViewController;
loginController = [tabbar.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
loginController.delegate = self;
[tabbar presentModalViewController:loginController animated:YES];
}
I create an object in my AppDelegate called WindowState or similar that manages what should be the rootViewController of the window. Initially it would be a sign in or splash, then you can run checks in your WindowState class and listen for notifications eg. MyAppDidSignInNotification then change the rootViewController of your app to a UITabBarController or whatever there.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.windowState = [[FASWindowState alloc] initWithWindow:self.window];
[self.window makeKeyAndVisible];
return YES;
}