UIApplication sharedApplication - keyWindow is nil? - iphone

I want to convert a CGPoint from my UIView to UIWindow coordinates and have realized that UIApplication keyWindow is always nil; why is this?
I have tried the convertPoint:toView: method from UIView.
Please see this sample code I tried in the view controller in a template of Xcode (View application):
- (void)viewDidLoad {
[super viewDidLoad];
UIView *test = [[UIView alloc] initWithFrame:CGRectMake(40,40,250,250)];
[test setBackgroundColor:[UIColor redColor]];
[self.view addSubview:test];
CGPoint p = CGPointMake(100, 100);
CGPoint np;
np = [test convertPoint:p toView:[[UIApplication sharedApplication] keyWindow]];
NSLog(#"p:%# np:%#", NSStringFromCGPoint(p), NSStringFromCGPoint(np));
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
np = [test convertPoint:p toView:[appDel window]];
NSLog(#"p:%# np:%#", NSStringFromCGPoint(p), NSStringFromCGPoint(np));
np = [test convertPoint:p toView:nil];
NSLog(#"p:%# np:%#", NSStringFromCGPoint(p), NSStringFromCGPoint(np));
[test release];
if(![[UIApplication sharedApplication] keyWindow])
NSLog(#"window was nil");
}
and I get:
p:{100, 100} np:{100, 100}
p:{100, 100} np:{140, 160}
p:{100, 100} np:{100, 100}
window was nil
I can convert it but only when I access the window through the app delegate. And not UIApplication. According to the documentation, keyWindow should work here, but is nil.
Why is this?

This code was executed before [window makeKeyAndVisible]; which is inside the app delegate.
So, no wonder why keyWindow was nil yet.

Easiest way is to get the window from the app delegate instead:
UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
// Do something with the window now

I noticed that after having started the Guided Access, the keyWindow property on [UIApplication sharedApplication] appears to be nil.
It happened to me only on iOS7 when starting the Guided Access Mode for the first time after having enabled it in Settings > General > Guided Access, so the starting GAM view is actually displayed and not by-passed.
Since this Apple API seems buggy, I solved using the following code to retrieve the window I'm looking for.
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count]) {
return windows[0];
}
return nil;
Instead of
[[UIApplication sharedApplication] keyWindow];
maybe you could also try using
[[[UIApplication sharedApplication] delegate] window];
as iWasRobbed pointed out but it wasn't working for me as the rootViewController property isn't reachable this way.

Try this, first get the UINavigationController handle, and then the topViewController
let navController = window?.rootViewController as! UINavigationController
let yourMainViewController = navController.topViewController as! ItemsViewController
or
let yourMainViewController = navController.viewControllers.first as! ItemsViewController

Related

Why MBProgressHUD not show, when an alert show before HUD show?

I use this code to init MBProgressHUD
UIWindow *window = [[UIApplication sharedApplication] keyWindow]
_hud = [[MBProgressHUD alloc]initWithWindow:window];
_hud.dimBackground = bDim;
_hud.labelText = message;
[window addSubview:_hud];
[_hud show:YES];
but sometimes _hud not show in window ?
Anyone let me know where im lack here??
Thanks!
Click here!
This works for me .Just use this window.
UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
Or
you can use
self.navigationController.view
to add you HUD.

Game Center Leaderboards with cocos2d Cause Unresponsive App

OK, so I'm trying to show Apple Game Center Leaderboards called from within my cocos2d game.
I've had some trouble doing so.
I did eventually stumble upon this and I implemented the following in one of my CCScene classes (I slightly modified the original code to prevent a compiler warning).
- (void)showLeaderboardForCategory:(NSString *)category
{
// Create leaderboard view with default Game Center style
leaderboardController = [[GKLeaderboardViewController alloc] init];
// If view controller was successfully created...
if (leaderboardController != nil)
{
// Leaderboard config
leaderboardController.leaderboardDelegate = self; // leaderboardController will send messages to this object
leaderboardController.category = category;
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
// Create an additional UIViewController to attach the GKLeaderboardViewController to
vc = [[UIViewController alloc] init];
// Add the temporary UIViewController to the main view
[[CCDirector sharedDirector].view.window addSubview:vc.view];
// Tell UIViewController to present the leaderboard
[vc presentModalViewController:leaderboardController animated:YES];
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
[vc dismissViewControllerAnimated:YES completion:nil];
}
And, it works! At least when I call it, it does display the Leaderboard properly.
The only problem is, when I tap "Done" on the Leaderboard and the modal view dismisses, my CCScene no longer responds to tap events.
What do I need to do to regain responsiveness?
Refer sample plain cocos2d project:
-(void)showLeaderboard
{
GKLeaderboardViewController *leaderboardViewController = [[GKLeaderboardViewController alloc] init];
leaderboardViewController.leaderboardDelegate = self;
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] presentModalViewController:leaderboardViewController animated:YES];
[leaderboardViewController release];
}
Delegate Function:
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}

Use of MBProgressHUD Globally + make it singleton

In my Project, each of the user interaction events make a network call (Which is TCP, not HTTP). I need Activity Indicator to be global to show from a random UIViewController and hide from NetworkActivityManager Class (a custom class to handle network activities, Which is not a subclass of UIViewController or UIView).
After searching the web I found out that MBProgressHUD is used for the same purpose, but I wasn't able to find out an example on how would I use it globally. (By saying global I mean a singleton object of MBProgressHUD and class methods to SHOW and HIDE it.)
Following is what I have tried yet, but, failed:
In AppDelegate.h:
#property (nonatomic, retain) MBProgressHUD *hud;
In AppDelegate.m:
#synthesize hud;
In some random UIViewController object:
appDelegate.hud = [MBProgressHUD showHUDAddedTo:appDelegate.navigationController.topViewController.view animated:YES];
appDelegate.hud.labelText = #"This will take some time.";
And while hiding it, from NetworkActivityManager Class:
[MBProgressHUD hideHUDForView:appDelegate.navigationController.topViewController.view animated:YES];
This makes the project to crash after some time (due to memory issues.)
I am using ARC in my project and also, I am using the ARC version of MBProgressHUD.
Am I missing something?
Important Question:
Can I make MBProgressHUD work like UIAlertView? (Saying that I mean implementation of MBProgressHUD independent of UIView -- sa it uses showHUDAddedTo: to present itself) ???
Please Note: In the above code of hiding MBProgressHUD, View may be changed from what it was when showing MBProgressHUD.
Any Help greatly appreciated.
You could add this to a class of your liking:
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
+ (void)dismissGlobalHUD {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideHUDForView:window animated:YES];
}
This can be than called on any class. You don't need to keep a strong reference to the HUD when using those class convenience methods.
Depending on your specific situation you'll probably also want to handle cases where a new hud is requested before the other one is hidden. You could eater hide the previous hud when a new comes in or come up with some sort of queueing, etc.
Hiding the previous HUD instance before showing a new one is pretty straightforward.
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideAllHUDsForView:window animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
NOTE...
as with many iOS issues, this is now drastically, totally out of date.
These days you certainly just use a trivial
Container view
for any issue like this.
Full container view tutorial for beginners .. tutorial!
MBProgressHUD was a miraculous solution back in the day, because there was a "drastic hole" in Apple's pipeline.
But (as with many wonderful things from the past), this is only history now. Don't do anything like this today.
Just FWIW, 2014, here's a very simple setup we use. Per David Lawson...
UIWindow *window = [[UIApplication sharedApplication] delegate].window
as Matej says, just use AppDelegate...
#define APP ((AppDelegate *)[[UIApplication sharedApplication] delegate])
AppDelegate.h
// our convenient huddie system (messages with a hud, spinner)
#property (nonatomic, strong) MBProgressHUD *hud;
-(void)huddie;
AppDelegate.m
-(void)huddie
{
// centralised location for MBProgressHUD
[self.hud hide:YES];
UIWindow *windowForHud = [[UIApplication sharedApplication] delegate].window;
self.hud = [MBProgressHUD showHUDAddedTo:windowForHud animated:YES];
self.hud.dimBackground = YES;
self.hud.minShowTime = 0.1;
self.hud.labelText = #"";
self.hud.detailsLabelText = #"";
}
Set the titles in your code where you are using it - because you very often change them during a run. ("Step 1" ... "Step 2" etc)
-(void)loadBlahFromCloud
{
[APP huddie];
APP.hud.labelText = #"Connecting to Parse...";
APP.hud.detailsLabelText = #"step 1/2";
[blah refreshFromCloudThen:
^{
[... example];
}];
}
-(void)example
{
APP.hud.labelText = #"Connecting to the bank...";
APP.hud.detailsLabelText = #"step 2/2";
[blah sendDetailsThen:
^{
[APP.hud hide:YES];
[... showNewDisplay];
}];
}
Change huddle to take the texts as an argument if you wish
You always want self.hud.minShowTime = 0.1; to avoid flicker
Almost always self.hud.dimBackground = YES; which also blocks UI
Conceptually of course you usually have to "slightly wait" to begin work / end work when you bring up such a process, as with any similar programming with the UI.
So in practice code will usually look like this...
-(void)loadActionSheets
{
[APP huddie];
APP.hud.labelText = #"Loading json from net...";
dispatch_after_secs_on_main(0.1 ,
^{
[STUBS refreshNowFromCloudThen:
^{
[APP.hud hide:YES];
dispatch_after_secs_on_main(0.1 , ^{ [self buildActionsheet]; });
}];
}
);
}
Handy macro ..
#define dispatch_after_secs_on_main( SS, BB ) \
dispatch_after( \
dispatch_time(DISPATCH_TIME_NOW, SS*NSEC_PER_SEC), \
dispatch_get_main_queue(), \
BB \
)
This is all history now :) https://stackoverflow.com/a/23403979/294884
This answer is what I've been using for 5-6 Apps now because it works perfectly inside blocks too. However I found a problem with it. I can make it shown, but can't make it disappear if a UIAlertView is also present. If you look at the implementation you can see why. Simply change it to this:
static UIWindow *window;
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
window = [[[UIApplication sharedApplication] windows] lastObject];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
+ (void)dismissGlobalHUD {
[MBProgressHUD hideHUDForView:window animated:YES];
}
This will make sure you're removing the HUD from the same windows as it was shown on.
I found #Matej Bukovinski 's answer very helpful, since I just started using Swift and my purpose using his methods was to set a global font for the MBProgressHUD, I have converted the code to swift and am willing to share the code below:
class func showGlobalProgressHUDWithTitle(title: String) -> MBProgressHUD{
let window:UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
hud.labelText = title
hud.labelFont = UIFont(name: FONT_NAME, size: 15.0)
return hud
}
class func dismissGlobalHUD() -> Void{
let window:UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
MBProgressHUD.hideAllHUDsForView(window, animated: true)
}
The above code is put into a global file where I keep all my global helpers and constants.
I've used it as below..Hope it helps you..
in appDelegate.m
-(void)showIndicator:(NSString *)withTitleString currentView:(UIView *)currentView
{
if (!isIndicatorStarted) {
// The hud will dispable all input on the view
self.progressHUD = [[[MBProgressHUD alloc] initWithView:currentView] autorelease];
// Add HUD to screen
[currentView addSubview:self.progressHUD];
self.progressHUD.labelText = withTitleString;
[window setUserInteractionEnabled:FALSE];
[self.progressHUD show:YES];
isIndicatorStarted = TRUE;
}
}
-(void)hideIndicator
{
[self.progressHUD show:NO];
[self.progressHUD removeFromSuperview];
self.progressHUD = nil;
[window setUserInteractionEnabled:TRUE];
isIndicatorStarted = FALSE;
}
From Random Views:-
[appDel showIndicator:#"Loading.." currentView:presentView.view];
Note: Considering the views this Question is getting I decided to post the the way I did choose as a solution. This is NOT an answer to my question. (Hence, the accepted answer remains accepted)
At that time I ended up using SVProgressHUD as it was very simple to integrate and use.
All you need to do is just drag the SVProgressHUD/SVProgressHUD folder into your project. (You may choose to go for cocoapods OR carthage, as well)
In Objective-C:
[SVProgressHUD show]; // Show
[SVProgressHUD dismiss]; // Dismiss
In Swift:
SVProgressHUD.show() // Show
SVProgressHUD.dismiss() // Dismiss
Additionally, Show and hide HUD needs to be executed on main thread. (Specifically you would need this to hide the HUD in some closure in background)
e.g.:
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss]; // OR SHOW, whatever the need is.
});
There are additional methods for displaying custom messages with HUD, showing success/failure for short duration and auto dismiss.
MBProgressHUD still remains a good choice for developers. It's just that I found SVProgressHUD to suit my needs.
I was using the code from #Michael Shang and having all kinds of inconsistent behavior with showing HUDs. Turns out using the last window is unreliable as the iOS keyboard may just hide it. So in the majority of cases you should get the window using the AppDelegate as mentioned by #David Lawson.
Here's how in Swift:
let window = UIApplication.sharedApplication().delegate!.window!!
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
However, with the above your HUD will show up behind the iOS keyboard (if they overlap). If you need your HUD to overlay the keyboard use the last window method.
In my case, what was happening is I would show the HUD then call resignFirstResponder() immediately hiding the window the HUD was added to. So this is something to be aware of, the only window guaranteed to stick around is the first one.
I ended up creating a method that could optionally add the HUD above the keyboard if needed:
func createHUD(size: CGSize, overKeyboard: Bool = false) -> MBProgressHUD {
let window = overKeyboard ? UIApplication.sharedApplication().windows.last!
: UIApplication.sharedApplication().delegate!.window!!
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
hud.minSize = size
hud.bezelView.style = .SolidColor
hud.bezelView.color = UIColor(white: 0, alpha: 0.8)
return hud
}
To show the one MBProgressHUD at one time, you can check weather HUD is already added in same view or not. If not, then add the HUD otherwise do not add new HUD.
-(void)showLoader{
dispatch_async(dispatch_get_main_queue(), ^{
BOOL isHudAlreadyAdded = false;
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
NSEnumerator *subviewsEnum = [window.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:[MBProgressHUD class]]) {
isHudAlreadyAdded = true;
}
}
if(isHudAlreadyAdded == false){
[MBProgressHUD showHUDAddedTo:window animated:YES];
}
});
}
-(void)hideLoader{
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideHUDForView:window animated:YES];
});
}
Add these two methods to show or hide loader in your singleton class
- (void)startLoaderWithText:(NSString *)title View:(UIView *)view{
progressHud = [MBProgressHUD showHUDAddedTo:view animated:YES];
progressHud.labelText = title;
progressHud.activityIndicatorColor = [UIColor grayColor];
progressHud.color = [UIColor clearColor];
[progressHud show:YES];
}
- (void)stopLoader{
[progressHud hide:YES];
}

Wikitude SDK remove from superview

I have implement Wikitude SDK and I manage to initialize using this code:
-(IBAction)launchAR:(id)sender{
wikitudeAR = [[WikitudeARViewController alloc] initWithDelegate:self applicationPackage:nil applicationKey:nil applicationName:nil developerName:nil];
}
- (void) verificationDidSucceed {
BuddyFinderAppDelegate *appDel = (BuddyFinderAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDel.window addSubview: [wikitudeAR start]];
[window makeKeyAndVisible];
}
How I will go back to my MainViewController View?
(I want to call it from object "custombutton")
I guess your WikitudeARViewController delegate is application delegate.
1) Implement your own View controller , from where you want to launch AR Browser (on button click , preferably).
2) Instantiate WikitudeARViewController from your ViewController(button event) and making ViewController as delegate.
3) make view returned from [wikitudeAR start] to the ViewController.view (and not window.view).
Hope this helps.
you can use CustomMenuButtonDelegateImpl1.m
#implementation CustomMenuButtonDelegateImpl1
- (void) customMenuButtonPressed:(WTPoi *)currentSelectedPoi {
//NSLog(#"addpoi");
[[WikitudeARViewController sharedInstance] hide];
[UIApplication sharedApplication].statusBarHidden = NO;
}
#end
and it woroks very well

How to access delegate method from another class?

I have a method in my delegate that does this:
-(void)showAddingPersonalDetails; {
personal = [[AddingPersonalDetails alloc] initWithNibName:#"AddingWithPersonalDetails" bundle:nil];
[window addSubview:personal.view];
[window makeKeyAndVisible];
mainscreen.view.hidden = YES;
NSLog(#"Called");
}
I don't want this view initialized until I need it. That's why put in in a method.
The problem is, I can't seem to access this code from another class.
I even tried this:
BitWiseAppDelegate *appDelegate = (BitWiseAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.showAddingPersonalDetails;
But it doesn't work. Any ideas?
try with following code;
BitWiseAppDelegate *appDelegate = (BitWiseAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate showAddingPersonalDetails];
or
[(BitWiseAppDelegate *)[[UIApplication sharedApplication] delegate] showAddingPersonalDetails];