在通话状态栏中(无法满足约束条件)

Wil*_*Hua 32 statusbar uiviewcontroller ios

就像这个问题:自动布局和通话中状态栏和这个问题:调整通话状态栏的大小?,我在调用状态栏中遇到问题,搞砸了我的视图布局.

这是我的嵌套结构.我有一个嵌套在另一个ViewController中的自定义模态ViewController.每当显示In Call Status Bar(然后关闭)时,会发生以下情况:

在此输入图像描述

下面是显示In Call Status Bar之前应该是什么样子的图片:

在此输入图像描述

发生错误后状态栏的背景蓝色是根视图控制器的背景颜色.

每当显示"呼叫状态栏"时,将打印出以下错误:

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fdac6192320 V:|-(20)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>",
    "<NSLayoutConstraint:0x7fdac608ebb0 'UIInputWindowController-top' V:|-(0)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fdac6192320 V:|-(20)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fc60b03d230 V:|-(20)-[UIInputSetContainerView:0x7fc608d22020]   (Names: '|':UITextEffectsWindow:0x7fc60b171720 )>",
    "<NSLayoutConstraint:0x7fc60b03d2d0 UIInputSetContainerView:0x7fc608d22020.bottom == UITextEffectsWindow:0x7fc60b171720.bottom>",
    "<NSLayoutConstraint:0x7fc60b17c4b0 'UIInputWindowController-height' UIInputSetContainerView:0x7fc608d22020.height == UITextEffectsWindow:0x7fc60b171720.height>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fc60b03d2d0 UIInputSetContainerView:0x7fc608d22020.bottom == UITextEffectsWindow:0x7fc60b171720.bottom>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Run Code Online (Sandbox Code Playgroud)

使用FLEX调试工具,我可以看到

UINavigationBarBackground并且UIStatusBarForegroundView在"呼叫状态栏"之前重叠,但之后UINavigationBarBackground是以下UIStatusBarForegroundView.

此错误仅在我呈现模态视图控制器后发生.如果我显示"呼叫状态栏",则不会发生此问题.在显示模态视图控制器后,您无法返回到根视图控制器.

我该怎么做才能解决这个问题?

ser*_*e-k 12

iOS 9.2.1,Xcode 7.2.1,ARC启用

更新2016年3月25日: Xcode 7.3,iOS 9.3中仍然存在冲突.

简介:在应用程序的窗口层次结构中,有各种窗口可以添加到应用程序窗口中.在我的情况下,这是UITextEffectsWindowUIRemoteKeyboardWindow.这些窗口具有预配置的约束.似乎有一个错误更新了一些垂直布局约束,但没有更新同一窗口的其他相关约束.这会在调试器中抛出约束冲突.当自定义窗口添加到窗口层次结构中或者在模拟器和实际iOS设备上调用状态栏进入或退出时,会发生这种情况.

约束是优先级1000,这表明它们是必需的约束.

下面的解决方案将删除冲突的约束,并在切换呼入状态栏后将其添加回来.

编辑2/25/2016: 两种解决方案都没有解决打开应用程序时已经显示通话中状态栏的问题.冲突发生在状态栏的更改注册之前.

似乎这种约束冲突仅在第一次显示通话中状态栏时发生(这是最常见的),或者在涉及呈现位于键窗口顶部的附加自定义窗口的其他场景中发生. 我还尝试创建一个空白的单一视图应用程序,并在调用状态栏中添加任何切换,我得到了相同的约束冲突.

我认为这是一个bug,并在Dev论坛上进行了讨论.Dev论坛的原始文章可以在这里找到(正如matty指出的那样):

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

我想在这里扩展一下matty的答案.我觉得非常有帮助.我不确定删除"所有"约束会导致什么影响,所以我只删除了冲突的约束.我的理由是,当调试器通知"将试图通过破坏约束来恢复"时,冲突的约束将被打破; 所以这个约束无论如何都没有用.

以下是我收到的约束冲突错误:

在此输入图像描述

在此输入图像描述

解释约束错误后(请参阅此苹果文档以帮助解决此问题:https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/DebuggingTricksandTips.html)我使用以下代码来摆脱冲突的约束:

Objective-C的:

*AppDelegate.h

...

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

...
Run Code Online (Sandbox Code Playgroud)

*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;
        }
    }
}

...
Run Code Online (Sandbox Code Playgroud)

迅速:

*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
        }
    }
}

...
Run Code Online (Sandbox Code Playgroud)

注意:这将保留所有不冲突的约束.并且在冲突情况不再发生后添加已删除的冲突约束,即切换掉呼叫状态栏.

2015年2月25日更新: 进一步测试......

我决定在应用程序中使用两种方法来隔离更改的约束.委托*.m文件:

...

- (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);
    }
}

...
Run Code Online (Sandbox Code Playgroud)

当呼叫状态栏切换时,冲突的约束从以下变化:

UITextEffectsWindow:

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

("<NSLayoutConstraint:0x7fbf99667eb0 V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略

"<NSLayoutConstraint:0x7fbf9966c800'UIInputWindowController-top'V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略)

UIRemoteKeyboardWindow:

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

("<NSLayoutConstraint:0x7fbf994cfb20 V:| - (0) - [UIInputSetContainerView:0x7fbf99744ec0](名称:'|':UIRemoteKeyboardWindow:0x7fbf994ceb80)>",

... 省略

"<NSLayoutConstraint:0x7fbf9966c800'UIInputWindowController-top'V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略)

......并改为:

UITextEffectsWindow:

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

("<NSLayoutConstraint:0x7fbf99667eb0 V:| - (20) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略

"<NSLayoutConstraint:0x7fbf9966c800'UIInputWindowController-top'V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略)

UIRemoteKeyboardWindow:

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

("<NSLayoutConstraint:0x7fbf994cfb20 V:| - (20) - [UIInputSetContainerView:0x7fbf99744ec0](名称:'|':UIRemoteKeyboardWindow:0x7fbf994ceb80)>",

... 省略

"<NSLayoutConstraint:0x7fbf9966c800'UIInputWindowController-top'V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:'|':UITextEffectsWindow:0x7fbf994cc810)>",

... 省略)

要了解可视化格式化语言,请阅读https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html

看起来"垂直布局V:[topField] -XX- [bottomField]"格式的垂直布局约束从......改变

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

至...

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

...对于两个窗口:UITextEffectsWindow和UIRemoteKeyboardWindow; 但是,......

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

...才不是.

因此,从我可以推断出,窗口调整其约束以考虑添加的通话中状态栏,但UIInputWindowController不会.因此,约束冲突就产生了.

但情节变浓了......

在由于呼叫中状态栏切换导致的初始约束冲突之后,约束不会改变,并且优先级相同,但是进一步切换呼入状态栏进入或退出时不会出现约束冲突. 但是,这只是因为最初的冲突已经被抛出.

希望这可以帮助!干杯.

感谢所有原始贡献者.

  • 请不要因为浪费这么多时间来排除很可能是错误的警告而烦恼.无论如何,我需要了解NSLayoutConstraints和布局格式化语言. (3认同)

jfg*_*ang 9

来自@matty的Swift版本答案:

func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
    for window in UIApplication.sharedApplication().windows {
        if window.dynamicType.self.description().containsString("UITextEffectsWindow") {
            window.removeConstraints(window.constraints)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


mat*_*tty 3

类似的问题也可以在这里找到:https ://forums.developer.apple.com/thread/20632

我通过在我的 AppDelegate 中实现下面的代码片段尝试了该主题中建议的解决方法。它似乎摆脱了约束错误,但我非常不愿意发布一个带有此的应用程序,因为它肯定是一个苹果错误。

重现步骤:

  1. 在 Xcode 7.1 中创建新的单视图应用程序
  2. 在 iOS 9.1 模拟器中运行应用程序
  3. 切换通话中栏 (CMD+Y)
  4. 您将看到“无法同时满足约束”。错误。

要消除约束错误:

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame {
    for(UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if([window.class.description isEqual:@"UITextEffectsWindow"])
        {
            [window removeConstraints:window.constraints];
        }  
    }
}
Run Code Online (Sandbox Code Playgroud)

就我个人而言,我仅使用上面的代码片段来确保约束错误不会导致我遇到的其他约束问题,即:应用程序在后台启动时出现黑屏。事实证明并非如此。

  • 我不建议这样做。你正在消除苹果创造的限制。这个问题似乎是一个错误,你不知道苹果什么时候会修复它,而当他们修复它时,你可能会删除应该存在的限制。 (2认同)