Подтвердить что ты не робот

В строке состояния звонка (не удается удовлетворить ограничения)

Также как этот вопрос: Авто макет и строка состояния вызова и этот вопрос: Изменить размер для статуса вызова bar?, у меня возникают проблемы с панелью состояния In Call, прикручивающей мой макет представления.

Вот моя вложенная структура. У меня есть Custom Modal ViewController, который вложен в другой ViewController. Всякий раз, когда отображается строка состояния In Call (а затем закрыта), это происходит:

введите описание изображения здесь

Вот изображение того, как оно должно выглядеть, прежде чем появится строка состояния вызова:

введите описание изображения здесь

Синим цветом строки состояния после ошибки является цвет фона контроллера корневого представления.

Когда отображается строка состояния In Call, выводится следующая ошибка:

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.

Используя инструмент отладки FLEX, я вижу, что

UINavigationBarBackground и UIStatusBarForegroundView перекрываются до строки состояния In Call, однако UINavigationBarBackground ниже UIStatusBarForegroundView.

Эта ошибка возникает только после того, как я представляю Modal View Controller. Если я покажу строку состояния In Call, проблема не возникает. После просмотра диспетчера Modal View вы не можете вернуться в контроллер корневого представления.

Что я могу сделать, чтобы исправить это?

4b9b3361

Ответ 1

iOS 9.2.1, Xcode 7.2.1, ARC включен

ОБНОВЛЕНИЕ 3/25/2016: Конфликт все еще существует в Xcode 7.3, iOS 9.3.

Резюме: В иерархии окон вашего приложения есть несколько окон, которые добавляются в окно приложения. В моем случае это были UITextEffectsWindow и UIRemoteKeyboardWindow. Эти окна имеют предварительно сконфигурированные ограничения. Кажется, есть ошибка, которая обновляет некоторые ограничения вертикальной компоновки, но не другие связанные ограничения для одного и того же окна. Это вызывает конфликт ограничений в отладчике. Это происходит, когда пользовательское окно добавляется в иерархию окон или когда строка состояния в вызове становится включенной или отключенной как на симуляторе, так и на самом устройстве iOS.

Ограничения - это приоритет 1000, это означает, что они являются обязательными.

В приведенном ниже решении удаляется конфликтное ограничение и добавляется обратно после выключения строки состояния вызова.

РЕДАКТИРОВАТЬ 2/25/2016: Ни одно из решений не решает проблему того, что индикатор состояния вызова уже отображается при открытии приложения. Конфликт происходит до того, как будет зарегистрировано изменение строки состояния.

Похоже, что этот конфликт ограничений происходит только в первый раз, когда отображается строка состояния в вызове (это чаще всего) или в других сценариях, связанных с представлением дополнительного пользовательского окна, которое будет сидеть поверх ключевого окна, Я также попытался просто создать пустое приложение с одним представлением и не добавил ничего, что переключается в строке состояния вызова, и я получил тот же конфликт ограничений.

Я думаю, что это ошибка и обсуждается на форумах Dev. Оригинальную статью на форумах Dev можно найти здесь (как указано выше):

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

Я хотел немного рассказать о матовом ответе здесь. Который я нашел очень полезным. Я не уверен, какое влияние удастся устранить "все" ограничения, поэтому я удалил только конфликтующие ограничения. Мое рассуждение состоит в том, что конфликтующее ограничение будет все равно нарушено, поскольку отладчик сообщает "Будет пытаться восстановить, нарушая ограничение" ; поэтому ограничение не будет иметь никакой цели.

Вот ошибки конфликта ограничений, которые я получал:

введите описание изображения здесь

введите описание изображения здесь

После интерпретации ошибок ограничения (см. этот документ в яблоке, чтобы помочь с этим: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/DebuggingTricksandTips.html) Я использовал следующий код, чтобы избавиться противоречивых ограничений:

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

...

Примечание. Это сохраняет все ограничения, которые не конфликтуют. И добавляет удаленные конфликтующие ограничения обратно после того, как конфликтная ситуация больше не возникает, а именно: строка состояния в вызове отключена.

ОБНОВЛЕНИЕ 2/25/2015: Дальнейшая проверка...

Я решил изолировать изменяющиеся ограничения, используя два метода в приложении. делегат *.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:

< UITextEffectsWindow: 0x7fbf994cc810; frame = (0 0; 320 568); непрозрачный = НЕТ; 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); непрозрачный = НЕТ; 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 В: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0]

чтобы...

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

... для обоих окон: UITextEffectsWindow и UIRemoteKeyboardWindow; однако...

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

... не делает.

Итак, из того, что я могу вывести, окно настраивает его ограничение для учета добавленной строки состояния вызова, но UIInputWindowController этого не делает. Поэтому возникает конфликт ограничений.

Но сюжет сгущается...

После того, как начальные ограничения конфликтуют в результате переключения строки состояния входящих вызовов, ограничения не изменяются, а приоритет один и тот же, но конфликт ограничений не возникает при дальнейшем переключении строки состояния вызова в или вне. Но это только потому, что начальный конфликт уже был брошен.

Надеюсь, это поможет! Приветствия.

Спасибо всем оригинальным вкладчикам.

Ответ 2

Быстрая версия из ответа @matty:

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

Ответ 3

Аналогичную проблему можно найти и здесь: https://forums.developer.apple.com/thread/20632

Я попытался предложить обходное решение из этой темы, выполнив фрагмент ниже в моем AppDelegate. Кажется, он избавляется от ошибок ограничения, но я очень неохотно выпускаю приложение с этим, поскольку это, безусловно, Apple Bug.

Шаги для воспроизведения:

  • Создайте новое приложение с одним представлением в Xcode 7.1
  • Запустите приложение в симуляторе iOS 9.1
  • Переключить панель вызова (CMD + Y)
  • Вы увидите "Невозможно одновременно удовлетворить ограничения". ошибка.

Чтобы избавиться от ошибки ограничения:

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

Лично я использовал только этот фрагмент, чтобы убедиться, что ошибки ограничения не вызвали другие проблемы ограничения, которые у меня были именно: черные экраны, когда приложение было запущено в фоновом режиме. Это оказалось не так.

Ответ 4

Все вышеизложенное представлялось не простым решением. Я столкнулся с той же проблемой, когда добавил subview в viewDidLoad. Я обошел его, переместив код в viewDidAppear. FYI.