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

时间:2015-10-13 21:15:35

标签: ios uiviewcontroller statusbar

就像这个问题:Auto Layout and in-call status bar和这个问题:Resize for in-call status bar?,我在调用状态栏时遇到问题搞砸了我的视图布局。

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

enter image description here

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

enter image description here

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

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<nav id="custom-bootstrap-menu" class="navbar navbar-default navbar-static-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>

      </button>
      <div id="navlogo"> <a class="navbar-brand" href="#">CHEEZ-IT!</a>

      </div>
    </div>
    <div id="navbar" class="navbar-collapse collapse">
      <ul class="nav navbar-nav">
        <li><a href="#">Home</a>

        </li>
        <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">About Us <span class="caret"></span></a>

          <ul class="dropdown-menu">
            <li><a href="#">Who are Cheez-it's</a>

            </li>
            <li><a href="#">Credentials</a>

            </li>
          </ul>
        </li>
        <li><a href="#contact">Our Services</a>

        </li>
        <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Industries <span class="caret"></span></a>

          <ul class="dropdown-menu">
            <li><a href="#">Cheeze-it Solar</a>

            </li>
            <li><a href="#">Cheeze-it Infrastructure</a>

            </li>
            <li><a href="#">Cheeze-it Harvest</a>

            </li>
          </ul>
        </li>
        <li><a href="#contact">Partners</a>

        </li>
        <li><a href="#contact">Contact</a>

        </li>
      </ul>
    </div>
    <!--/.nav-collapse -->
  </div>
</nav>

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

在通话状态栏前

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. UINavigationBarBackground重叠,但之后UIStatusBarForegroundView低于UINavigationBarBackground

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

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

4 个答案:

答案 0 :(得分:12)

iOS 9.2.1,Xcode 7.2.1,已启用ARC

更新3/25/2016: Xcode 7.3,iOS 9.3中仍然存在冲突。

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

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

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

EDIT 2/25/2016: 这两个解决方案都解决了打开应用时已经显示通话中状态栏的问题。冲突发生在状态栏更改之前。

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

我认为这是一个错误,并在Dev论坛上进行了讨论。 Dev论坛的原始文章可以在这里找到(如matty所指出的):

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

我想在matty's answer here上稍微扩展一下。我觉得非常有帮助。我不确定删除&#34;所有&#34;约束会导致,所以我只删除了冲突的约束。我的理由是,当调试器通知&#34;将试图通过违反约束来恢复时,冲突的约束将被打破&#34; ;所以这个约束无论如何都没有用。

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

enter image description here

enter image description here

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

<强>目标-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;
        }
    }
}

...

<强>夫特:

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

...

注意:这会保留所有不冲突的约束。并且在冲突情况不再发生后,将已删除的冲突约束添加回来,即调用状态栏。

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

...

当呼入状态栏切换时,冲突的约束从:

变为

UITextEffectsWindow:

  

&LT; UITextEffectsWindow:0x7fbf994cc810; frame =(0 0; 320 568);不透明   = NO; autoresize = W + H; layer =&lt; UIWindowLayer:0x7fbf994c8470&gt; &gt;,

     

(&#34;&lt; NSLayoutConstraint:0x7fbf99667eb0 V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](姓名:   &#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

     

&#34;&LT; NSLayoutConstraint:0x7fbf9966c800&#39; UIInputWindowController-top&#39; V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:   &#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

UIRemoteKeyboardWindow:

  

&LT; UIRemoteKeyboardWindow:0x7fbf994ceb80; frame =(0 0; 320 568);   opaque = NO; autoresize = W + H; layer =&lt; UIWindowLayer:0x7fbf994cf190&gt; &gt;,

     

(&#34;&lt; NSLayoutConstraint:0x7fbf994cfb20 V:| - (0) - [UIInputSetContainerView:0x7fbf99744ec0](姓名:   &#39; |&#39;:UIRemoteKeyboardWindow:0x7fbf994ceb80)&gt;&#34;,

     

... 省略

     

&#34;&LT; NSLayoutConstraint:0x7fbf9966c800&#39; UIInputWindowController-top&#39; V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:&#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

...并更改为:

UITextEffectsWindow:

  

&LT; UITextEffectsWindow:0x7fbf994cc810; frame =(0 0; 320 568);不透明   = NO; autoresize = W + H; layer =&lt; UIWindowLayer:0x7fbf994c8470&gt; &gt;,

     

(&#34;&lt; NSLayoutConstraint:0x7fbf99667eb0 V:| - ( 20 ) - [UIInputSetContainerView:0x7fbf99668ce0](姓名:   &#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

     

&#34;&LT; NSLayoutConstraint:0x7fbf9966c800&#39; UIInputWindowController-top&#39; V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:   &#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

UIRemoteKeyboardWindow:

  

&LT; UIRemoteKeyboardWindow:0x7fbf994ceb80; frame =(0 0; 320 568);   opaque = NO; autoresize = W + H; layer =&lt; UIWindowLayer:0x7fbf994cf190&gt; &gt;,

     

(&#34;&lt; NSLayoutConstraint:0x7fbf994cfb20 V:| - ( 20 ) - [UIInputSetContainerView:0x7fbf99744ec0](姓名:   &#39; |&#39;:UIRemoteKeyboardWindow:0x7fbf994ceb80)&gt;&#34;,

     

... 省略

     

&#34;&LT; NSLayoutConstraint:0x7fbf9966c800&#39; UIInputWindowController-top&#39; V:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0](名称:&#39; |&#39;:UITextEffectsWindow:0x7fbf994cc810)&gt;&#34;,

     

... 省略

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

垂直布局约束的格式为&#34;垂直布局V:[topField] -XX- [bottomField]&#34;来自...的变化

  

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

为...

  

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

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

  

NSLayoutConstraint:0x7fbf9966c800&#39; UIInputWindowController-top&#39;   五:| - (0) - [UIInputSetContainerView:0x7fbf99668ce0]

......没有。

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

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

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

希望这有帮助!欢呼声。

感谢所有原始贡献者。

答案 1 :(得分: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)
        }
    }
}

答案 2 :(得分:3)

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

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

重现的步骤:

  1. 在Xcode 7.1中创建一个新的单一视图应用程序
  2. 在iOS 9.1模拟器中运行应用
  3. 切换通话栏(CMD + Y)
  4. 你会看到'无法同时满足约束'。错误。
  5. 摆脱约束错误:

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

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

答案 3 :(得分:0)

以上所有似乎都不是简单的解决方案。 当我在viewDidLoad中添加子视图时遇到了同样的麻烦。 通过将代码移动到viewDidAppear中我得到了它。 FYI。