In Call Status Bar (Unable to Satisfy Constraints)

iOS 9.2.1, Xcode 7.2.1, ARC enabled

UPDATE 3/25/2016: Conflict still exists in Xcode 7.3, iOS 9.3.

Summary: In the window hierarchy for your application there are various windows that get added onto the application window. In my case, this was the UITextEffectsWindow and the UIRemoteKeyboardWindow. These windows come with preconfigured constraints. It seems there is a bug that updates some vertical layout constraints, but not other related constraints for the same window. This throws the constraints conflict in debugger. This happens when a custom window gets added to the window hierarchy or when the in-call status bar gets toggled in or out, on both the simulator and the actual iOS device.

The constraints are priority 1000, this indicates that they are required constraints.

The solution below will remove the conflicting constraint and add it back once the in-call status bar is toggled out.

EDIT 2/25/2016: Neither solution solves the problem of having the in-call status bar already displayed when opening app. The conflict happens before the change in status bar is registered.

It appears that this constraint conflict happens only the first time the in-call status bar is shown (this is most common), or in other scenarios involving the presentation of an additional custom window that would sit on top of the key window. I also tried just creating a blank single view application and with adding nothing toggling in the in-call status bar, and I got the same constraint conflict.

I think it is a bug and is discussed on the Dev forums. The original article from the Dev forums can be found here (as matty pointed out):

https://forums.developer.apple.com/thread/16375

I wanted to expand a little on matty’s answer here. Which I found very helpful. I am not sure what impact removing “all” the constraints would cause, so I removed just the conflicting constraints. My reasoning being that the conflicting constraint will be broken anyway as the debugger informs “Will attempt to recover by breaking constraint”; so the constraint will serve no purpose anyway.

Here are the constraint conflict errors I was receiving:

enter image description here

enter image description here

After interpreting the constraint errors (see this apple document to help with that: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/DebuggingTricksandTips.html) I used the following code to get rid of the conflicting constraints:

Objective-C:

*AppDelegate.h

...

@interface YourAppName : UIResponder <UIApplicationDelegate>
{
    NSMutableDictionary *dictionaryConstraints;
}

...

*AppDelegate.m

...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    dictionaryConstraints = [[NSMutableDictionary alloc] init];

    return true;

}

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
   NSLog(@"newStatusBarFrame: %@", NSStringFromCGRect(newStatusBarFrame));

   if (newStatusBarFrame.size.height > 20.0)
   {
        for (UIWindow *window in [[UIApplication sharedApplication] windows])
        {
            if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
            {
                NSMutableArray *constraints = [[NSMutableArray alloc] initWithCapacity:[window.constraints count]];

                for (NSLayoutConstraint *constraint in window.constraints)
                {
                    if (!([constraint.description rangeOfString:@"V:|-(0)-[UIInputSetContainerView"].location == NSNotFound))
                    {
                        NSLog(@"");
                        NSLog(@"%@: %@, %f, %f", window.class.description, constraint.description, constraint.priority, constraint.constant);
                        NSLog(@"");

                        [constraints addObject:constraint];
                        [window removeConstraint:constraint];
                    }
                    else
                    {
                        nil;
                    }
                }

                if ([constraints count] > 0)
                {
                    [dictionaryConstraints setObject:constraints forKey:[NSString stringWithFormat:@"%p", window]];
                }
                else
                {
                    nil;
                }
            }
            else
            {
                nil;
            }
        }
    }
    else
    {
        nil;
    }
}

- (void)resetConstraints
{
    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
        {
            if (dictionaryConstraints)
            {
                NSArray *keys = [dictionaryConstraints allKeys];

                for (int i = 0; i < [keys count]; i++)
                {
                    if ([[NSString stringWithFormat:@"%p", window] isEqualToString:keys[i]])
                    {
                        [window addConstraints:[dictionaryConstraints objectForKey:keys[i]]];
                    }
                    else
                    {
                        nil;
                    }
                }
            }
            else
            {
                nil;
            }
        }
        else
        {
            nil;
        }
    }
}

- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
    NSLog(@"oldStatusBarFrame: %@", NSStringFromCGRect(oldStatusBarFrame));

    if (oldStatusBarFrame.size.height > 20.0)
    {
        if ([dictionaryConstraints count] > 0)
        {
            [self resetConstraints];
            [dictionaryConstraints removeAllObjects];
        }
        else
        {
            nil;
        }
    }
    else
    {
        nil;
    }

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
        {
            for (NSLayoutConstraint *constraint in window.constraints)
            {
                if (!([constraint.description rangeOfString:@"V:|-(0)-[UIInputSetContainerView"].location == NSNotFound))
                {
                    NSLog(@"");
                    NSLog(@"%@: %@, %f, %f", window.class.description, constraint.description, constraint.priority, constraint.constant);
                    NSLog(@"");
                }
                else
                {
                    nil;
                }

            }
        }
        else
        {
            nil;
        }
    }
}

...

Swift:

*AppDelegate.swift

...

var dictionaryConstraints = [NSString : NSArray]();

...

func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect)
{
    print("newStatusBarFrame: \(newStatusBarFrame)")

    if newStatusBarFrame.size.height > 20.0
    {
        for window in UIApplication.sharedApplication().windows
        {
            if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
            {
                var constraints = [NSLayoutConstraint]()

                for constraint in window.constraints
                {
                    if (constraint.description.containsString("V:|-(0)-[UIInputSetContainerView"))
                    {
                        print("\(window.classForCoder.debugDescription), \(constraint.description), \(constraint.priority), \(constraint.constant)")

                        constraints.append(constraint)
                        window.removeConstraint(constraint)
                    }
                    else
                    {
                        //nil
                    }
                }

                if (constraints.count > 0)
                {
                    dictionaryConstraints[NSString(format: "%p", unsafeAddressOf(window))] = constraints
                }
                else
                {
                    //nil
                }
            }
            else
            {
                //nil
            }
        }
    }
    else
    {
        //nil
    }
}

func resetConstraints()
{
    for window in UIApplication.sharedApplication().windows
    {
        if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
        {
            if (dictionaryConstraints.count > 0)
            {
                let keys = Array(dictionaryConstraints.keys)

                for i in 0 ..< keys.count
                {
                    if (NSString(format: "%p", unsafeAddressOf(window)) == keys[i])
                    {
                        window.addConstraints(dictionaryConstraints[keys[i]] as! [NSLayoutConstraint])
                    }
                    else
                    {
                        //nil
                    }
                }
            }
            else
            {
                //nil
            }
        }
        else
        {
            //nil
        }
    }
}

func application(application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect)
{
    print("oldStatusBarFrame: \(oldStatusBarFrame)")

    if (oldStatusBarFrame.size.height > 20.0)
    {
        if (dictionaryConstraints.count > 0)
        {
            self.resetConstraints()
            dictionaryConstraints.removeAll()
        }
        else
        {
            //nil
        }
    }
    else
    {
        //nil
    }

    for window in UIApplication.sharedApplication().windows
    {
        if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
        {
            for constraint in window.constraints
            {
                if (constraint.description.containsString("V:|-(0)-[UIInputSetContainerView"))
                {
                    print("\(window.classForCoder.debugDescription), \(constraint.description), \(constraint.priority), \(constraint.constant)")
                }
                else
                {
                    //nil
                }
            }
        }
        else
        {
            //nil
        }
    }
}

...

Note: This keeps all constraints that are not conflicting. And adds the removed conflicting constraints back in after the conflicting situation is no more, namely the in-call status bar is toggled out.

UPDATE 2/25/2015: Further testing…

I decided to isolate the changing constraints using two methods in the app. delegate *.m file:

...

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
    NSLog(@"New status bar frame: %@", NSStringFromCGRect(newStatusBarFrame));

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        NSLog(@"%@, %@", window.description, window.constraints);
    }
}

- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
    NSLog(@"Old status bar frame: %@", NSStringFromCGRect(oldStatusBarFrame));

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        NSLog(@"%@, %@", window.description, window.constraints);
    }
}

...

When the in-call status bar toggles in, the conflicting constraints change from:

UITextEffectsWindow:

< UITextEffectsWindow: 0x7fbf994cc810; frame = (0 0; 320 568); opaque
= NO; autoresize = W+H; layer = < UIWindowLayer: 0x7fbf994c8470> >,

( “< NSLayoutConstraint:0x7fbf99667eb0 V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0] (Names:
‘|’:UITextEffectsWindow:0x7fbf994cc810 )>”,

omitted

“< NSLayoutConstraint:0x7fbf9966c800 ‘UIInputWindowController-top’ V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0] (Names:
‘|’:UITextEffectsWindow:0x7fbf994cc810 ) >”,

omitted )

UIRemoteKeyboardWindow:

< UIRemoteKeyboardWindow: 0x7fbf994ceb80; frame = (0 0; 320 568);
opaque = NO; autoresize = W+H; layer = < UIWindowLayer: 0x7fbf994cf190> >,

( “< NSLayoutConstraint:0x7fbf994cfb20 V:|-(0)-[UIInputSetContainerView:0x7fbf99744ec0] (Names:
‘|’:UIRemoteKeyboardWindow:0x7fbf994ceb80 ) >”,

omitted

“< NSLayoutConstraint:0x7fbf9966c800 ‘UIInputWindowController-top’ V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0] (Names: ‘|’:UITextEffectsWindow:0x7fbf994cc810 )>”,

omitted )

…and changes to:

UITextEffectsWindow:

< UITextEffectsWindow: 0x7fbf994cc810; frame = (0 0; 320 568); opaque
= NO; autoresize = W+H; layer = < UIWindowLayer: 0x7fbf994c8470> >,

( “< NSLayoutConstraint:0x7fbf99667eb0 V:|-(20)-[UIInputSetContainerView:0x7fbf99668ce0] (Names:
‘|’:UITextEffectsWindow:0x7fbf994cc810 )>”,

omitted

“< NSLayoutConstraint:0x7fbf9966c800 ‘UIInputWindowController-top’ V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0] (Names:
‘|’:UITextEffectsWindow:0x7fbf994cc810 ) >”,

omitted )

UIRemoteKeyboardWindow:

< UIRemoteKeyboardWindow: 0x7fbf994ceb80; frame = (0 0; 320 568);
opaque = NO; autoresize = W+H; layer = < UIWindowLayer: 0x7fbf994cf190> >,

( “< NSLayoutConstraint:0x7fbf994cfb20 V:|-(20)-[UIInputSetContainerView:0x7fbf99744ec0] (Names:
‘|’:UIRemoteKeyboardWindow:0x7fbf994ceb80 ) >”,

omitted

“< NSLayoutConstraint:0x7fbf9966c800 ‘UIInputWindowController-top’ V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0] (Names: ‘|’:UITextEffectsWindow:0x7fbf994cc810 )>”,

omitted )

To understand the visual formatting language read https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html

It appears that the vertical layout constraints in the format “Vertical Layout V:[topField]-XX-[bottomField]” changes from…

NSLayoutConstraint:0x7fbf99667eb0
V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0]

to…

NSLayoutConstraint:0x7fbf99667eb0
V:|-(20)-[UIInputSetContainerView:0x7fbf99668ce0]

…for both windows: UITextEffectsWindow and UIRemoteKeyboardWindow; however, …

NSLayoutConstraint:0x7fbf9966c800 ‘UIInputWindowController-top’
V:|-(0)-[UIInputSetContainerView:0x7fbf99668ce0]

…does not.

So from what I can deduce, the window adjusts its constraint to account for the added in-call status bar, but the UIInputWindowController does not. Therefore, the constraint conflict arrises.

But the plot thickens…

After the initial constraints conflict resulting from the in-calls status bar toggling, the constraints do not change, and the priority is the same, but the constraints conflict does not arise with further toggling of the in-call status bar in or out. But, this just because the initial conflict was already thrown.

Hope this helps! Cheers.

Thanks to all the original contributors.

Leave a Comment