eonil
eonil

Reputation: 85975

How to prevent a UIWindow to be a key window?

When I show an alert with UIAlertController, the alert itself presented in a new window. (for now at least) And when the alert window dismisses, system seems to set a random window key-window.

I am presenting a new "banner" window to render some banners over status-bar (AppStore compatibility is out of topic here), and usually, this "banner" window becomes next key window, and causes many problems on user input and first responder management.

So, I want to prevent this "banner" window to become a key window, but I cannot figure out how. For now, as a workaround, I am just re-setting my main window to be a key window again as soon as that "banner" window becomes key window. But it doesn't feel really good.

How can I prevent a window to become a key window?

Upvotes: 6

Views: 1967

Answers (3)

makaron
makaron

Reputation: 1615

Faced with this too. It seems that it's enough to just make:

class BannerWindow: UIWindow {
    override func makeKey() {
        // Do nothing
    }
}

This way you don't need to keep a reference to a previous keyWindow, which is especially cool if it might get changed.

For Objective-C it's:

@implementation BannerWindow

- (void)makeKeyWindow {
    // Do nothing
}

@end

Upvotes: 4

nevyn
nevyn

Reputation: 7122

I've been trying to solve this problem for years. I finally reported a Radar for it: http://www.openradar.me/30064691

My workaround looks something like this:

// a UIWindow subclass that I use for my overlay windows
@implementation GFStatusLevelWindow

...

#pragma mark - Never become key
// http://www.openradar.me/30064691

// these don't actually help
- (BOOL)canBecomeFirstResponder
{
    return NO;
}

- (BOOL)becomeFirstResponder
{
    return NO;
}


- (void)becomeKeyWindow
{
    LookbackStatusWindowBecameKey(self, @"become key window");
    [[self class] findAndSetSuitableKeyWindow];
}

- (void)makeKeyWindow
{
    LookbackStatusWindowBecameKey(self, @"make key window");
}

- (void)makeKeyAndVisible
{
    LookbackStatusWindowBecameKey(self, @"make key and visible window");
}

#pragma mark - Private API overrides for status bar appearance

// http://www.openradar.me/15573442
- (BOOL)_canAffectStatusBarAppearance
{
    return NO;
}

#pragma mark - Finding better key windows

static BOOL IsAllowedKeyWindow(UIWindow *window)
{
    NSString *className = [[window class] description];
    if([className isEqual:@"_GFRecordingIndicatorWindow"])
        return NO;
    if([className isEqual:@"UIRemoteKeyboardWindow"])
        return NO;
    if([window isKindOfClass:[GFStatusLevelWindow class]])
        return NO;

    return YES;
}

void LookbackStatusWindowBecameKey(GFStatusLevelWindow *self, NSString *where)
{
        GFLog(GFError, @"This window should never be key window!! %@ when in %@", self, where);
        GFLog(GFError, @"To developer of %@: This is likely a bug in UIKit. If you can get a stack trace at this point (by setting a breakpoint at LookbackStatusWindowBecameKey) and sending that stack trace to [email protected] or [email protected], I will report it to Apple, and there will be rainbows, unicorns and a happier world for all :) thanks!", [[NSBundle mainBundle] gf_displayName]);
}

+ (UIWindow*)suitableWindowToMakeKeyExcluding:(UIWindow*)notThis
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    NSInteger index = windows.count-1;

    UIWindow *nextWindow = [windows objectAtIndex:index];
    while((!IsAllowedKeyWindow(nextWindow) || nextWindow == notThis) && index >= 0) {
        nextWindow = windows[--index];
    }
    return nextWindow;
}

+ (UIWindow*)findAndSetSuitableKeyWindow
{

    UIWindow *nextWindow = [[self class] suitableWindowToMakeKeyExcluding:nil];
    GFLog(GFError, @"Choosing this as key window instead: %@", nextWindow);
    [nextWindow makeKeyWindow];
    return nextWindow;
}

Upvotes: 1

eonil
eonil

Reputation: 85975

As a workaround, we can set main window key again as soon as the "banner" window becomes a key like this.

class BannerWindow: UIWindow {
    weak var mainWindow: UIWindow?
    override func becomeKeyWindow() {
        super.becomeKeyWindow()
        mainWindow?.makeKeyWindow()
    }
}

Upvotes: 2

Related Questions