Versión corta:

Estoy teniendo un problema con el auto de diseño guía de diseño cuando se utiliza en conjunción con la costumbre de transición y UINavigationController en iOS7. En concreto, la restricción entre la parte superior de diseño de la guía y de la vista de texto no es ser honrado. Alguien ha encontrado con este problema?


Versión larga:

Tengo una escena que, sin ambigüedades, definir restricciones (es decir, superior, inferior, izquierda y derecha) que muestra una vista así:

De navegación superior del controlador guía de diseño no se honra con la costumbre de transición

Pero cuando puedo usar esto con una medida de transición en la navegación de la controladora, la parte superior de la restricción a la parte superior guía de diseño parece apagado y se hace es la siguiente, como si la parte superior guía de diseño fue en la parte superior de la pantalla, en lugar de en la parte inferior de la navegación de controlador:

De navegación superior del controlador guía de diseño no se honra con la costumbre de transición

Parece que el «top guía de diseño» con el controlador de navegación es confundirse cuando se emplea la transición personalizado. El resto de las restricciones que se apliquen correctamente. Y si puedo rotar el dispositivo y girar de nuevo, todo es de repente se procesa correctamente, por lo que no aparecen a no ser un asunto que los límites no están definidos correctamente. Del mismo modo, cuando apago mi costumbre de transición, las vistas procesar correctamente.

Habiendo dicho eso, _autolayoutTrace está informando de que el UILayoutGuide objetos sufren de AMBIGUOUS LAYOUT, cuando ejecuto:

(lldb) po [[UIWindow keyWindow] _autolayoutTrace]

Pero los guías de diseño son siempre informado de como ambiguo cuando miro en ellos a pesar de que me he asegurado de que no hay ninguna falta de restricciones (he hecho la habitual selección de controlador de vista y seleccionando «Agregar» falta de restricciones para la vista controlador» o seleccionar todos los controles y hacer lo mismo por ellos).

En términos de cómo precisamente estoy haciendo la transición, he especificado un objeto que se ajusta a UIViewControllerAnimatedTransitioning en el animationControllerForOperation método:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController*)fromVC
                                                 toViewController:(UIViewController*)toVC
{
    if (operation == UINavigationControllerOperationPush)
        return [[PushAnimator alloc] init];

    return nil;
}

Y

@implementation PushAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    [[transitionContext containerView] addSubview:toViewController.view];
    CGFloat width = fromViewController.view.frame.size.width;

    toViewController.view.transform = CGAffineTransformMakeTranslation(width, 0);

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromViewController.view.transform = CGAffineTransformMakeTranslation(-width /2.0, 0);
        toViewController.view.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        fromViewController.view.transform = CGAffineTransformIdentity;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

También he hecho una versión de la anterior, la configuración de la frame de la vista en lugar de la transform, con el mismo resultado.

También he intentado manualmente asegúrese de que las restricciones se aplican de nuevo llamando a layoutIfNeeded. También he intentado setNeedsUpdateConstraints, setNeedsLayout, etc.

Línea de fondo, alguien tiene éxito en el matrimonio de transición personalizado de navegación controlador con las limitaciones que el uso superior de la guía de diseño?

  • Hola @Rob. Encontraste una solución para esto?
  • No, no es otro que especificar manualmente el desplazamiento de la parte superior de la vista en lugar de la parte superior guía de diseño, o simplemente apagar el extendido de diseño por completo.
InformationsquelleAutor Rob | 2013-12-01

12 Comentarios

  1. 15

    Lo resuelto por la fijación de la altura de la restricción de la topLayoutGuide. Ajuste de edgesForExtendedLayout no era una opción para mí, ya que necesitaba la vista de destino para underlap la barra de navegación, pero también para ser capaces de diseño de subvistas utilizando topLayoutGuide.

    Directamente la inspección de las restricciones en el juego muestra que iOS añade una altura de restricción a la topLayoutGuide con valor igual a la altura de la barra de navegación de la navegación controlador. Excepto, en iOS 7, mediante una animación personalizada de transición de las hojas de la restricción con una altura de 0. Se fija este en iOS 8.

    Esta es la solución que se me ocurrió para corregir la restricción (es rápido, pero el equivalente debe trabajar en Obj-C). Yo he probado y que funciona en iOS 7 y 8.

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromView = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!.view
        let destinationVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
        destinationVC.view.frame = transitionContext.finalFrameForViewController(destinationVC)
        let container = transitionContext.containerView()
        container.addSubview(destinationVC.view)
    
        //Custom transitions break topLayoutGuide in iOS 7, fix its constraint
        if let navController = destinationVC.navigationController {
            for constraint in destinationVC.view.constraints() as [NSLayoutConstraint] {
                if constraint.firstItem === destinationVC.topLayoutGuide
                    && constraint.firstAttribute == .Height
                    && constraint.secondItem == nil
                    && constraint.constant == 0 {
                    constraint.constant = navController.navigationBar.frame.height
                }
            }
        }
    
        //Perform your transition animation here ...
    }
    
    • Es bueno ver que fija esta en iOS 8. Yo no había notado eso. Todo esto tiene sentido. Con respecto a su fragmento de código, arriba, tres cosas: 1. Las pruebas en iOS 7, tuve que quitar constraint.constant criterio porque la mía fue 20, no 0; 2. Creo que quiso decir ...frame.size.height, no ...frame.height. 3. Creo que también tenemos que interceptar la rotación de los sucesos y cambiar este constant para el cambio de altura de la barra de navegación. Pero la idea de ajustar manualmente la parte superior de la restricción tiene sentido.
    • gracias por los comentarios. Interesante que la restricción constante fue de 20, la mía era 0 en ambos iOS 7 el dispositivo y la tarjeta sim que he probado en (actualización: ah! Tengo la barra de estado ocultos. Totalmente de ella). frame.height es un auxiliar que proporciona Swift :). Yo esperaría que la restricción para que se actualicen automáticamente en la rotación, pero mi aplicación no admite la rotación. Lo has probado?
    • Ah, sí, por supuesto que altera la limitación de la rotación. De hecho, incluso cuando se porta mal, si gira en el paisaje y en la espalda, la parte superior de la restricción se fijan automáticamente. El problema es sólo el ajuste de la restricción inicial al primero de la transición a la nueva vista y esto lo arregla.
    • Esta respuesta me funciona! Traté de editarlo para honrar a la altura de la barra de estado & navigationBarHidden de la propiedad. Pero bueno a la gente ¿por qué rechazarlo sin razonamiento?
  2. 27

    Conseguido solucionar mi problema mediante la adición de esta línea:

    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
    

    A:

    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
    
        //Add the toView to the container
        UIView* containerView = [transitionContext containerView];
        [containerView addSubview:toView];
        [containerView sendSubviewToBack:toView];
    
    
        //animate
        toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
        NSTimeInterval duration = [self transitionDuration:transitionContext];
        [UIView animateWithDuration:duration animations:^{
            fromView.alpha = 0.0;
        } completion:^(BOOL finished) {
            if ([transitionContext transitionWasCancelled]) {
                fromView.alpha = 1.0;
            } else {
                //reset from- view to its original state
                [fromView removeFromSuperview];
                fromView.alpha = 1.0;
            }
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    
    }
    

    De la Documentación de Apple para [finalFrameForViewController] : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerContextTransitioning_protocol/#//apple_ref/occ/intfm/UIViewControllerContextTransitioning/finalFrameForViewController:

    • Esta es la única que ha funcionado correctamente, para mí! [transitionContext finalFrameForViewController:toVC] ya tiene la correcta topLayoutGuide. Usted ha hecho mi día! Gracias!
    • Impresionante! esta es la única respuesta válida a la pregunta! el resto son sólo muy sucio hacks! Gracias!
    • Tenga en cuenta que si la barra de navegación es translúcido, usted todavía necesita para seleccionar » auto.edgesForExtendedLayout = .Ninguno’ también en su toViewController del viewDidLoad() para que esto funcione.
    • Tuve un problema persistente con esto. Resulta que mi transición no hacer esto correctamente. Mis otros no. Gracias!
    • Tenía este problema con Xcode 9, pero sólo afecta a iOS 10 y por debajo. Esto parece haber funcionado para mí, y parece ser la más «correcta» / menos chapucero solución. Gracias!
  3. 10

    Luché con el mismo problema. Poner esto en el viewDidLoad de mi toViewController realmente me ayudó:

    self.edgesForExtendedLayout = UIRectEdgeNone;
    

    Esto no resuelve todos mis problemas y todavía estoy en busca de un mejor enfoque, pero esto sin duda hizo un poco más fácil.

    • De acuerdo, si usted está dispuesto a perder la translúcido de la barra de navegación, esto funciona muy bien. Espero que alguien encuentre la solución y/o Apple va a solucionar esto.
    • Hey rob, ¿de archivo de un radar con Apple con esto?
    • Sí, tengo.
  4. 5

    Sólo hay que poner el siguiente código paraviewDidLoad

    self.extendedLayoutIncludesOpaqueBars = YES;
    
  5. 4

    FYI, terminé el empleo de una variación de Alex respuesta, cambiar mediante programación la parte superior guía de diseño de la altura de la restricción constante en el animateTransition método. Sólo estoy publicando esto para compartir el Objetivo-C entregas (y eliminar la constant == 0 de la prueba).

    CGFloat navigationBarHeight = toViewController.navigationController.navigationBar.frame.size.height;
    
    for (NSLayoutConstraint *constraint in toViewController.view.constraints) {
        if (constraint.firstItem == toViewController.topLayoutGuide
            && constraint.firstAttribute == NSLayoutAttributeHeight
            && constraint.secondItem == nil
            && constraint.constant < navigationBarHeight) {
            constraint.constant += navigationBarHeight;
        }
    }
    

    Gracias, Alex.

    • la mejor solución, gracias!
  6. 3

    Como @Rob mencionado, topLayoutGuide no es fiable cuando se utiliza transiciones personalizadas en UINavigationController. He trabajado de todo esto usando mi propia guía de diseño. Usted puede ver el código en acción en este proyecto de demostración. Destacados:

    Una categoría personalizada guías de diseño:

    @implementation UIViewController (hp_layoutGuideFix)
    
    - (BOOL)hp_usesTopLayoutGuideInConstraints
    {
        return NO;
    }
    
    - (id<UILayoutSupport>)hp_topLayoutGuide
    {
        id<UILayoutSupport> object = objc_getAssociatedObject(self, @selector(hp_topLayoutGuide));
        return object ? : self.topLayoutGuide;
    }
    
    - (void)setHp_topLayoutGuide:(id<UILayoutSupport>)hp_topLayoutGuide
    {
        HPLayoutSupport *object = objc_getAssociatedObject(self, @selector(hp_topLayoutGuide));
        if (object != nil && self.hp_usesTopLayoutGuideInConstraints)
        {
            [object removeFromSuperview];
        }
        HPLayoutSupport *layoutGuide = [[HPLayoutSupport alloc] initWithLength:hp_topLayoutGuide.length];
        if (self.hp_usesTopLayoutGuideInConstraints)
        {
            [self.view addSubview:layoutGuide];
        }
        objc_setAssociatedObject(self, @selector(hp_topLayoutGuide), layoutGuide, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    @end
    

    HPLayoutSupport es la clase que actuará como una guía de diseño. Tiene que ser un UIView subclase para evitar accidentes (me pregunto por qué esto no es parte de la UILayoutSupport interfaz).

    @implementation HPLayoutSupport {
        CGFloat _length;
    }
    
    - (id)initWithLength:(CGFloat)length
    {
        self = [super init];
        if (self)
        {
            self.translatesAutoresizingMaskIntoConstraints = NO;
            self.userInteractionEnabled = NO;
            _length = length;
        }
        return self;
    }
    
    - (CGSize)intrinsicContentSize
    {
        return CGSizeMake(1, _length);
    }
    
    - (CGFloat)length
    {
        return _length;
    }
    
    @end
    

    La UINavigationControllerDelegate es el responsable de la «fijación» de la guía de diseño antes de la transición:

    - (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                       animationControllerForOperation:(UINavigationControllerOperation)operation
                                                    fromViewController:(UIViewController *)fromVC
                                                      toViewController:(UIViewController *)toVC
    {
        toVC.hp_topLayoutGuide = fromVC.hp_topLayoutGuide;
        id <UIViewControllerAnimatedTransitioning> animator;
        //Initialise animator
        return animator;
    }
    

    Finalmente, el UIViewController utiliza hp_topLayoutGuide en lugar de topLayoutGuide en las limitaciones, e indica que esta reemplazando hp_usesTopLayoutGuideInConstraints:

    - (void)updateViewConstraints
    {
        [super updateViewConstraints];
        id<UILayoutSupport> topLayoutGuide = self.hp_topLayoutGuide;
        //Example constraint
        NSDictionary *views = NSDictionaryOfVariableBindings(_imageView, _dateLabel, topLayoutGuide);
        NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topLayoutGuide][_imageView(240)]-8-[_dateLabel]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:views];
        [self.view addConstraints:constraints];
    }
    
    - (BOOL)hp_usesTopLayoutGuideInConstraints
    {
        return YES;
    }
    

    Espero que ayude.

  7. 2

    he encontrado la manera. Primero desactive la casilla «Extender las Aristas de la propiedad de la controladora. después de que la barra de navegación de llegar de color oscuro. Agregar una vista del programador y set top y la parte inferior LayoutConstraint -100. A continuación, hacer la vista clipsubview propiedad no (para navigaionbar transculent efecto). Mi inglés mala asesor para que. 🙂

    • Gracias hombre. Protector de la vida!
  8. 1

    Aquí está el simple solución que estoy utilizando un gran trabajo para mí: durante la fase de configuración de - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext, ajustar manualmente su «de» y «a» viewController.vista.marco.origen.y = navigationController.navigationBar.marco.tamaño.altura. Que va a hacer de su auto de vista de la presentación, se posicionan verticalmente como se esperaba.

    Menos el pseudo-código (por ejemplo, usted probablemente tiene su propia manera de determinar si un dispositivo se está ejecutando iOS7), esto es lo que mi método es similar:

    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIView *container = [transitionContext containerView];
    
        CGAffineTransform destinationTransform;
        UIViewController *targetVC;
        CGFloat adjustmentForIOS7AutoLayoutBug = 0.0f;
    
        //We're doing a view controller POP
        if(self.isViewControllerPop)
        {
            targetVC = fromViewController;
    
            [container insertSubview:toViewController.view belowSubview:fromViewController.view];
    
            //Only need this auto layout hack in iOS7; it's fixed in iOS8
            if(_device_is_running_iOS7_)
            {
                adjustmentForIOS7AutoLayoutBug = toViewController.navigationController.navigationBar.frame.size.height;
                [toViewController.view setFrameOriginY:adjustmentForIOS7AutoLayoutBug];
            }
    
            destinationTransform = CGAffineTransformMakeTranslation(fromViewController.view.bounds.size.width,adjustmentForIOS7AutoLayoutBug);
        }
        //We're doing a view controller PUSH
        else
        {
            targetVC = toViewController;
    
            [container addSubview:toViewController.view];
    
            //Only need this auto layout hack in iOS7; it's fixed in iOS8
            if(_device_is_running_iOS7_)
            {
                adjustmentForIOS7AutoLayoutBug = toViewController.navigationController.navigationBar.frame.size.height;
            }
    
            toViewController.view.transform = CGAffineTransformMakeTranslation(toViewController.view.bounds.size.width,adjustmentForIOS7AutoLayoutBug);
            destinationTransform = CGAffineTransformMakeTranslation(0.0f,adjustmentForIOS7AutoLayoutBug);
        }
    
        [UIView animateWithDuration:_animation_duration_
                              delay:_animation_delay_if_you_need_one_
                            options:([transitionContext isInteractive] ? UIViewAnimationOptionCurveLinear : UIViewAnimationOptionCurveEaseOut)
                         animations:^(void)
         {
             targetVC.view.transform = destinationTransform;
         }
                         completion:^(BOOL finished)
         {
             [transitionContext completeTransition:([transitionContext transitionWasCancelled] ? NO : YES)];
         }];
    }
    

    Un par de bonus cosas acerca de este ejemplo:

    • Para el controlador de vista empuja, esta costumbre de transición de las diapositivas de la empujó toViewController.ver en la parte superior de la inmóvil fromViewController.vista. De los cop, fromViewController.la vista se desliza a la derecha y revela una constante toViewController.ver bajo la misma. Con todo, es sólo un sutil giro en el mercado de iOS7+ controlador de vista de la transición.
    • La [UIView animateWithDuration:...] finalización de bloque muestra la manera correcta de manejar completado & cancela transiciones personalizadas. Este pequeño bocado era un clásico de la cabeza de la palmada del momento; espero que ayude a alguien más ahí fuera.

    Por último, me gustaría señalar que, como lo que puedo decir, este es un iOS7-único problema que se ha solucionado en iOS8: mi costumbre de controlador de vista de la transición que se ha roto en iOS7 funciona bien en iOS8 sin modificación. Dicho esto, usted debe comprobar que esto es lo que estamos viendo demasiado, y si es así, sólo ejecutar la revisión en los dispositivos con iOS7.x. Como se puede ver en el ejemplo de código anterior, y-el valor de ajuste es 0.0 f, a menos que el dispositivo se está ejecutando iOS7.x.

    • Gracias. Se asemeja a una combinación de ajuste de y abajo así como la aplicación de un transform que desplaza la vista hacia abajo. Que las direcciones de la barra de navegación problema, pero no que sólo entonces el tornillo de la parte inferior de la guía?
  9. 0

    tratar :

    self.edgesforextendedlayout=UIRectEdgeNone
    

    O de fijar navigationbar opaco y establecer la imagen de fondo o fondo para navigationbar

  10. 0

    Me encontré con este mismo problema, pero sin el uso de un UINavigationController y sólo el posicionamiento de una vista de la topLayoutGuide. El diseño sería correcto cuando aparece por primera vez, una transición tendría lugar a otro punto de vista, y luego al salir y regresar a la primera vista, el diseño sería roto como que topLayoutGuide ya no estaría allí.

    He resuelto este problema mediante la captura de la zona segura de los márgenes de antes de la transición y, a continuación, reimplementing ellos, no por el ajuste de mis limitaciones, pero colocándolos en el viewController del additionalSafeAreaInsets.

    He encontrado esta solución para que funcione bien como yo, no tienes que ajustar cualquiera de mi diseño, el código y la búsqueda a través de las limitaciones y sólo puedo reimplementing el espacio que estaba allí antes. Esto podría ser más difícil si usted está utilizando realmente la additionalSafeAreaInsets propiedad.

    Ejemplo

    He añadido una variable a mis clases transitionManager para capturar el fuerte de los márgenes que existen cuando las clases transitionManager es creado.

    class MyTransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
    
        private var presenting = true
        private var container:UIView?
        private var safeInsets:UIEdgeInsets?
    
        ...
    

    A continuación, durante la transición entrante puedo guardar los márgenes.

        let toView = viewControllers.to.view
        let fromView = viewControllers.from.view
    
        if #available(iOS 11.0, *) {
            safeInsets = toView.safeAreaInsets
        }
    

    En el caso del iPhone, X esto parece algo como UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)

    Ahora al salir, los recuadros en esa misma vista de que la transición a partir de la entrada será .zero así que añadimos nuestra capturado márgenes para la additionalSafeAreaInsets en el viewController, que se fijará en nuestra opinión, para nosotros, así como una actualización del diseño. Una vez que nuestra animación se hace, se restablece la additionalSafeAreaInsets de vuelta a .zero.

        if #available(iOS 11.0, *) {
            if safeInsets != nil {
                viewControllers.to.additionalSafeAreaInsets = safeInsets!
            }
        }
    
        ...then in the animation completion block
    
        if #available(iOS 11.0, *) {
            if self.safeInsets != nil {
                viewControllers.to.additionalSafeAreaInsets = .zero
            }
        }
    
        transitionContext.completeTransition(true)
    
  11. -1

    En storyboard agregar otra restricción vertical a la vista principal de la parte superior. Tengo el mismo problema pero añadiendo que la restricción que me ayude a evitar el manual de restricciones. Vea la captura de pantalla aquí enlace

    Otra solución es calcular toVC marco… algo como esto:

    float y = toVC.navigationController.navigationBar.frame.origin.y + toVC.navigationController.navigationBar.frame.size.height;
    toVC.view.frame = CGRectMake(0, y, toVC.view.frame.size.width, toVC.view.frame.size.height - y);
    

    Hágamelo saber si usted ha encontrado una solución mejor. He estado luchando con este problema y me vino con las ideas previas.

Dejar respuesta

Please enter your comment!
Please enter your name here