Pregunta En la barra de estado de llamada (no se pueden satisfacer las restricciones)


Al igual que esta pregunta: Diseño automático y barra de estado de llamada entrante y esta pregunta: Cambiar el tamaño de la barra de estado durante la llamada?, Estoy teniendo problemas con la barra de estado de llamadas que arruina mi diseño de vista.

Aquí está mi estructura anidada. Tengo un ViewController modal personalizado que está anidado dentro de otro ViewController. Cada vez que se muestra la barra de estado de llamadas entrantes (y luego se cierra), esto es lo que sucede:

enter image description here

Aquí hay una imagen de cómo debería verse antes de que se muestre la barra de estado de llamadas:

enter image description here

El color azul de fondo de la barra de estado después de que se produce el error es el color de fondo del controlador de vista raíz.

Cada vez que se muestra una barra de estado en llamada, se imprime el siguiente error:

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.

Usando la herramienta de depuración FLEX puedo ver que

UINavigationBarBackground y UIStatusBarForegroundView se superponen antes de la barra de estado de llamadas, sin embargo después UINavigationBarBackground Esta abajo UIStatusBarForegroundView.

Este error solo ocurre DESPUÉS de presentar el Controlador de Vista Modal. Si muestro la barra de estado en llamada, entonces el problema no ocurre. No puede volver al controlador de vista raíz después de que se muestre el Controlador de vista modal.

¿Qué puedo hacer para arreglar esto?


32
2017-10-13 21:15


origen


Respuestas:


iOS 9.2.1, Xcode 7.2.1, ARC habilitado

ACTUALIZACIÓN 25/3/2016: El conflicto aún existe en Xcode 7.3, iOS 9.3.

Resumen: En la jerarquía de ventanas para su aplicación, hay varias ventanas que se agregan a la ventana de la aplicación. En mi caso, este fue el UITextEffectsWindow y el UIRemoteKeyboardWindow. Estas ventanas vienen con restricciones preconfiguradas. Parece que hay un error que actualiza algunas restricciones de diseño vertical, pero no otras restricciones relacionadas para la misma ventana. Esto arroja el conflicto de restricciones en el depurador. Esto sucede cuando se agrega una ventana personalizada a la jerarquía de la ventana o cuando la barra de estado de la llamada entra en acción se activa o desactiva, tanto en el simulador como en el dispositivo iOS real.

Las restricciones son prioridad 1000, esto indica que son restricciones requeridas.

La siguiente solución eliminará la restricción en conflicto y la volverá a agregar una vez que la barra de estado de llamada en curso se desactive.

EDITAR 25/2/2016:  Ninguna de las soluciones resuelve el problema de tener la barra de estado de llamada ya mostrada al abrir la aplicación. El conflicto ocurre antes de que se registre el cambio en la barra de estado.

Parece que este conflicto de restricciones ocurre solo la primera vez que se muestra la barra de estado durante la llamada (esto es más común), o en otros escenarios que involucran la presentación de una ventana personalizada adicional que se ubicaría en la parte superior de la ventana de la llave. También intenté simplemente crear una aplicación de vista única en blanco y sin agregar nada alternar en la barra de estado de la llamada entrante, y tuve el mismo conflicto de restricción.

Creo que es un error y se discute en los foros de Dev. El artículo original de los foros de Dev se puede encontrar aquí (como matty señaló):

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

Yo quería expandirme un poco la respuesta de Matty aquí. Lo cual encontré muy útil. No estoy seguro de qué impacto eliminaría "todas" las restricciones, así que eliminé solo las restricciones conflictivas. Mi razonamiento es que la restricción conflictiva se romperá de todos modos a medida que el depurador informa "Intentará recuperarse rompiendo la restricción"; entonces la restricción no servirá para nada de todos modos.

Aquí están los errores de conflicto de restricción que estaba recibiendo:

enter image description here

enter image description here

Después de interpretar los errores de restricción (consulte este documento de Apple para ayudar con eso: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/DebuggingTricksandTips.html) Utilicé el siguiente código para eliminar las restricciones conflictivas:

C objetivo:

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

...

Rápido:

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

...

Nota: Esto mantiene todas las restricciones que no son conflictivas. Y agrega las restricciones conflictivas eliminadas una vez que la situación conflictiva ya no existe, es decir, la barra de estado de llamada entrante se desactiva.

ACTUALIZACIÓN 25/2/2015:  Pruebas adicionales...

Decidí aislar las limitaciones cambiantes utilizando dos métodos en la aplicación. delegar * .m archivo:

...

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

...

Cuando la barra de estado durante la llamada se alterna, las restricciones conflictivas cambian de:

UITextEffectsWindow:

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

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

...omitido

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

...omitido )

UIRemoteKeyboardWindow:

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

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

...omitido

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

...omitido )

... y cambios en:

UITextEffectsWindow:

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

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

...omitido

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

...omitido )

UIRemoteKeyboardWindow:

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

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

...omitido

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

...omitido )

Para entender el lenguaje de formato visual, lea https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html

Parece que las restricciones de diseño vertical en el formato "Vertical Layout V: [topField] -XX- [bottomField]" cambia de ...

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

a...

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

... para ambas ventanas: UITextEffectsWindow y UIRemoteKeyboardWindow; sin embargo, ...

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

...no.

Entonces, por lo que puedo deducir, la ventana ajusta su restricción para dar cuenta de la barra de estado adicional en la llamada, pero el UIInputWindowController no lo hace. Por lo tanto, el conflicto de restricción llega.

Pero la trama se complica ...

Después de que las restricciones iniciales entran en conflicto como resultado de la conmutación de la barra de estado de las llamadas entrantes, las restricciones no cambian y la prioridad es la misma, pero el conflicto de restricciones no se produce cuando se activa o desactiva la barra de estado de la llamada entrante. Pero esto solo porque el conflicto inicial ya fue lanzado.

¡Espero que esto ayude! Aclamaciones.

Gracias a todos los colaboradores originales.


12
2018-02-24 17:35



Swift versión de @matty respuesta:

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

9
2017-12-20 13:16



Un problema similar también se puede encontrar aquí: https://forums.developer.apple.com/thread/20632

Intenté la solución alternativa sugerida de ese tema implementando el siguiente fragmento en mi AppDelegate. Parece deshacerse de los errores de restricción, pero soy muy reacio a lanzar una aplicación con esto, ya que es sin duda un error de Apple.

Pasos para reproducir:

  1. Crear una nueva aplicación de vista única en Xcode 7.1
  2. Ejecuta la aplicación en un simulador de iOS 9.1
  3. Alternar la barra de llamada entrante (CMD + Y)
  4. Verás 'No se pueden cumplir las restricciones al mismo tiempo'. error.

Para deshacerse del error de restricción:

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

Personalmente, solo utilicé el fragmento de arriba para asegurarme de que los errores de restricción no estaban causando otros problemas de restricción que tenía, a saber: pantallas negras cuando se lanzó la aplicación en segundo plano. Eso resultó no ser el caso.


3
2017-10-29 10:15



Todas las soluciones anteriores no parecían simples. Me encontré con el mismo problema cuando agregué la subvista en viewDidLoad. Me enteré moviendo el código en viewDidAppear. FYI.


0
2018-05-29 04:16