Navigation controller top layout guide not honored with custom transition

前端 未结 12 1547
暖寄归人
暖寄归人 2020-12-04 11:35

Short version:

I am having a problem with auto layout top layout guide when used in conjunction with custom transition and UINavigationController in iO

12条回答
  •  孤街浪徒
    2020-12-04 11:50

    As @Rob mentioned, topLayoutGuide is not reliable when using custom transitions in UINavigationController. I worked around this by using my own layout guide. You can see the code in action in this demo project. Highlights:

    A category for custom layout guides:

    @implementation UIViewController (hp_layoutGuideFix)
    
    - (BOOL)hp_usesTopLayoutGuideInConstraints
    {
        return NO;
    }
    
    - (id)hp_topLayoutGuide
    {
        id object = objc_getAssociatedObject(self, @selector(hp_topLayoutGuide));
        return object ? : self.topLayoutGuide;
    }
    
    - (void)setHp_topLayoutGuide:(id)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 is the class that will act as a layout guide. It has to be a UIView subclass to avoid crashes (I wonder why this isn't part of the UILayoutSupport interface).

    @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
    

    The UINavigationControllerDelegate is the one responsible for "fixing" the layout guide before the transition:

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

    Finally, the UIViewController uses hp_topLayoutGuide instead of topLayoutGuide in the constraints, and indicates this by overriding hp_usesTopLayoutGuideInConstraints:

    - (void)updateViewConstraints
    {
        [super updateViewConstraints];
        id 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;
    }
    

    Hope it helps.

提交回复
热议问题