两个视图交易布局动画(包括 Z 顺序)

Two views trade layouts animated (including Z Order)

我有两个视图,一个 UIImageView 显示用户选择的照片,另一个 MKMapKitView 显示用户选择的对象位置。休息时,视图排列如下:

当用户点击视图 2 时,我希望两者交换位置。反之亦然。两个视图都定位有约束。所以我可以做一个动画,我首先获取两组约束,然后清除它们,然后以翻转顺序应用它们,但 z 顺序不会切换。无论哪个视图处于 "small" 模式,都应该位于另一个视图之上。

我真的很希望能够为它制作动画,使交换显而易见。以某种方式让它看起来像旋转:

完成此任务最简单的方法是什么?我尝试了以下方法:

- (IBAction)swapPreview {
    NSArray *photoConstraints = self.photoView.constraints;
    NSArray *mapConstraints = self.mapView.constraints;
    [self.photoView removeConstraints: photoConstraints];
    [self.mapView removeConstraints: mapConstraints];
    NSLog(@"photoConstraints %@", photoConstraints);
    NSLog(@"mapConstraints %@", mapConstraints);
    CGRect biggerFrame = CGRectUnion(self.photoView.frame, self.mapView.frame);
    CGRect upperFrame = CGRectOffset(RectCenterScaled(biggerFrame, 0.5), 0, biggerFrame.size.height / -4);
    CGRect lowerFrame = CGRectOffset(upperFrame, 0, upperFrame.size.height);
    [UIView animateWithDuration: 0.4 delay: 0 options: UIViewAnimationOptionCurveLinear animations:^{
        self.photoView.frame = upperFrame;
        self.mapView.frame = lowerFrame;
    } completion:^(BOOL finished) {
        UIView *container = self.photoView.superview;
        [self.photoView addConstraints: mapConstraints];
        [self.mapView addConstraints: photoConstraints];
        [container exchangeSubviewAtIndex: [container.subviews indexOfObject: self.photoView] withSubviewAtIndex:[container.subviews indexOfObject: self.mapView]];
        [UIView animateWithDuration: 0.4 delay: 0 options: UIViewAnimationOptionCurveLinear animations:^{
            [container updateConstraints];
        } completion:^(BOOL finished) {

        }];
    }];
}

但是当我尝试交换约束时,这会在第二个动画块上中断。那么更好的方法是什么?我可以遍历视图层次结构中的所有约束,交换引用两个视图的任何 firstItem/secondItems。但是局限于较小视图的约束需要改变局部性。

我开始怀疑我是否不应该只创建一个有两个子类的自定义 UIView 子类并直接进行布局,而不是乱用约束。

UIView 提供这些:

- (void)bringSubviewToFront:(UIView *)view;
- (void)sendSubviewToBack:(UIView *)view;

控制视图的 Z 顺序。

在其中一个动画的完成块中使用。这样做的好时机是视图不重叠时。

我制作了测试项目,class 表现如你所愿:

@interface ViewController ()
                                                                  //initial constants values
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view1Top;//0
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view2Top;//20
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view1Left;//0
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view2Left;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view1Height;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view2Height;//100
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view1Width;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *view2Width;//50
@property (weak, nonatomic) IBOutlet UIView *view1;
@property (weak, nonatomic) IBOutlet UIView *view2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}
- (void)viewWillAppear:(BOOL)animated{
    //setting initial values for constraints related to view size
    self.view1Width.constant = self.self.view.frame.size.width;
    self.view1Height.constant = self.self.view.frame.size.height;
    self.view2Left.constant = self.view.frame.size.width - 20 - self.view2Width.constant;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)animate:(id)sender {

    CGFloat gap = 20.0;
    CGFloat height = (self.view.frame.size.height - 3*gap)/2; //height of views in the middle of animation
    CGFloat width = self.view.frame.size.width - 40; //width of views in the middle of animation
    self.view1Top.constant = gap;
    self.view1Height.constant = height;
    self.view2Top.constant = height + 2*gap;
    self.view2Height.constant = height;
    self.view1Left.constant = self.view2Left.constant = gap;
    self.view1Width.constant = self.view2Width.constant = width;

     [UIView animateWithDuration:4.0 delay:0 options:0 animations:^{
         [self.view layoutIfNeeded];
             } completion:^(BOOL finished) {

         [self.view exchangeSubviewAtIndex: [self.view.subviews indexOfObject: self.view1] withSubviewAtIndex:[self.view.subviews indexOfObject: self.view2]];
                 self.view2Top.constant = 0;
                 self.view2Left.constant = 0;
                 self.view2Height.constant = self.view.frame.size.height;
                 self.view2Width.constant = self.view.frame.size.width;

                 self.view1Top.constant = gap;
                 self.view1Width.constant = 50;
                 self.view1Height.constant = 100;
                 self.view1Left.constant = self.view.frame.size.width - self.view1Width.constant - gap;

         [UIView animateWithDuration:4.0 animations:^{
             [self.view layoutIfNeeded];
                    }];

     }];
}

@end

view1是红色视图,view2是蓝色视图。

animate 是在我的案例中通过点击按钮启动的动作。

关键不在于删除约束,而只是动画常量属性。