How to programmatically animate the alignment prop

2019-09-06 19:58发布

问题:

I want to know how can I animate a change in the alignment property of an horizontal UIStackView say from UIStackViewAlignmentCenter to UIStackViewAlignmentTop and vice versa, and I want to visually reflects this change at the same time.

EDIT:

What I have:

This is my hierarchy of views:

  • two buttons named b1 and b2; b1 has an action named "changeAlignmentToTop" and b2 has an action named "changeAlignmentToCenter", which are activated Upon touch Up Inside in the respective button.
  • an UIView named Pview with frame (0,100,414,100).
    • an horizontal UIStackView named S1, with the alignment property set as "center". This stackView has been pinned to the borders of pView.
      • an horizontal UIStackView named S2, with the alignment property set as "Fill" and which has inside two labels.

What I want to do:

I want to change the alignment of S1 from "Center" to "Top" when I touch the button b1 and I want to animate that change.

What I tried

- (void) changeAlignmentToTop {
  [UIView animationWithduration 0.3 animations : ^{
    S1.alignment = UIStackViewAlignmentTop;
    // here I'd prove several options
    // option - 1
    [S1 setNeedsDisplay];
    [S1 layoutIfNeeded];
    // option - 2
    //[S1 setNeedsLayout];
    //[S1 layoutIfNeeded];
    // option - 3
    //[S1 setNeedsDisplay];
    //[pView layoutIfNeeded];
    // option - 4
    //[pView setNeedsDisplay];
    //[pView layoutIfNeeded];
    // option - 5
    //[pView setNeedsDisplay];
    //[self.view layoutIfNeeded];
  } completion: nil];
}

- (void) changeAlignmentToCenter {
  [UIView animationWithduration 0.3 animations : ^{
    S1.alignment = UIStackViewAlignmentCenter;
    // here I'd prove several options
    // option - 1
    [S1 setNeedsDisplay];
    [S1 layoutIfNeeded];
    // option - 2
    //[S1 setNeedsLayout];
    //[S1 layoutIfNeeded];
    // option - 3
    //[S1 setNeedsDisplay];
    //[pView layoutIfNeeded];
    // option - 4
    //[pView setNeedsDisplay];
    //[pView layoutIfNeeded];
    // option - 5
    //[pView setNeedsDisplay];
    //[self.view layoutIfNeeded];
  } completion: nil];
}

None of these options work. The aligment change but the UI does not reflects it neither it animates anything. If I change the alignment for axis (trying to change the axis from horizontal to vertical and vice versa) every things goes well. If I change animatedly the size of the font used in both label every thing goes well, the problem is with the alignment of the stackView. but according to the apple documentation the alignment property is animatable, so what I'm doing wrong??

Can anybody please help me??

Thanks in advance

回答1:

So hopefully I understand your question and if not let me know and I will edit.

First I will offer what I think is happening and why and then I will try to give you a solution although I don't know your use case.

The way UIStackView works is to create constraints for you. I am sure you know this but let's take your stack and analyze it together. Adding a horizontal stackview inside another horizontal stackview as I read the question. At run time the outer stackview sizes the inner stackview with intrinsic height then adds constraints to the centerY of the outer(s1). See image from visual debugger.

.

You can see it is vertically centered. To do this it must add some constraints to deal with a missing Y value of some type horizontal holding horizontal. So now think of the animation. It is not directly changing any constraints and apparently changing the alignment of a stackview must not completely clear the original constraints as the inner stackview is unable to move. At least this is what I think is going on.

So interestingly enough if you make the outer stackview(s1) a vertical stackview this is what it looks like at run time. see image

You can see that the inner stackview(s2) is stretched vertically to fill the entire outer stackview(s1) before it was horizontally filled. Much different as far as layout and room to animate vertically.

Now running the animation But on the inner stackview(s2) with this code

@IBAction func changeLayoutDidPress(_ sender: Any) {
    if s2.alignment == .center{
        UIView.animate(withDuration: 10.0, animations: {
            self.s2.alignment = .top
        })
    }else{
        UIView.animate(withDuration: 10.0, animations: {
            self.s2.alignment = .center
        })
    }
}

You will see the inner stackview(s2) move to the top of s1. I think this is because of the content view of the stackview allowing it. I hope this solves your issue. Sorry I cannot fully explain the constraints created by the different stackviews but I know that whatever constraints are created from Horizontal holding a horizontal must keep it from animating vertically.

Also a possible reason that changing the font size works is that it is probably causing the label view to recalculate its constraints to some content view of the stackview which would allow it to move up. Just my thoughts on it.

If having Horizontal inside Horizontal is still needed you could add equal heights to outer stackview(s1) on the inner s2 and it would most likely work the same as above.