Ответ 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 этого не делает. Поэтому возникает конфликт ограничений.
Но сюжет сгущается...
После того, как начальные ограничения конфликтуют в результате переключения строки состояния входящих вызовов, ограничения не изменяются, а приоритет один и тот же, но конфликт ограничений не возникает при дальнейшем переключении строки состояния вызова в или вне. Но это только потому, что начальный конфликт уже был брошен.
Надеюсь, это поможет! Приветствия.
Спасибо всем оригинальным вкладчикам.