我正在尝试在使用 AutoLayout 的窗口(xib)中做一些非常简单的视图动画,但我得到了一些非常不可预测的结果。显然,自动布局引擎正在做一些事情,我不知道如何修复它。
首先,我的窗口有一个设置为 NSImageScaleProportionallyUpOrDown 的 3x3 NSImageViews 网格。将内容压缩设置为 10 后,视图在调整窗口大小时会做正确的事情......一切都保持在正确的位置和正确的比例。一个漂亮的 3x3 网格图像。
当用户单击视图时,我希望该视图占据整个窗口并淡出其他视图。然后,当再次触摸时,动画回到原来的位置。
为了让事情变得简单,约束明智,我决定保留所有 NSImageViews 并在触摸视图之上创建另一个并使用它来制作动画。这就是问题之一。
我尝试以两种方式为该视图设置动画:通过为约束设置动画,以及在该视图上不使用约束而仅为其框架设置动画。无论哪种方式,我都会得到疯狂的结果。
这是代码:
使用约束
- (void) moveToFullView:(NSInteger)which
{
self.soloIndex = which;
NSImageView *tmpView = self.imageviews[which];
// create a view directly on top of the touched view
self.soloView = [[NSImageView alloc] initWithFrame:tmpView.frame];
[self.soloView.translatesAutoresizingMaskIntoConstraints = NO;
self.soloView.image = tmpView.image;
self.soloView.imageScaling = NSImageScaleProportionallyUpOrDown;
// without this, the window changes size in moveBackFromFullView
[self.soloView setContentCompressionResistancePriority:10 forOrientation:NSLayoutConstraintOrientationHorizontal];
[self.soloView setContentCompressionResistancePriority:10 forOrientation:NSLayoutConstraintOrientationVertical];
[self.view addSubview:self.soloView];
// create constraints with animatable constants
CGFloat leftSpace = tmpView.frame.origin.x;
CGFloat topSpace = tmpView.frame.origin.y;
CGFloat width = tmpView.frame.size.width - self.view.bounds.size.width;
CGFloat height = tmpView.frame.size.height - self.view.bounds.size.height;
self.soloView.leftConstraint = [NSLayoutConstraint constraintWithItem:self.soloView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:leftSpace];
self.soloView.topConstraint = [NSLayoutConstraint constraintWithItem:self.soloView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:topSpace];
self.soloView.widthConstraint = [NSLayoutConstraint constraintWithItem:self.soloView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:width];
self.soloView.heightConstraint = [NSLayoutConstraint constraintWithItem:self.soloView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:height];
[self.view addConstraint:self.soloView.leftConstraint];
[self.view addConstraint:self.soloView.topConstraint];
[self.view addConstraint:self.soloView.widthConstraint];
[self.view addConstraint:self.soloView.heightConstraint];
[self.view layoutSubtreeIfNeeded];
// animate the view to occupy the entire self.view
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = YES;
context.duration = 0.75;
// fade out the other views for now
for(NSImageView *p in self.imageviews){
p.alphaValue = 0;
}
// these values should make the soloView in the same place and size as the self.view
self.soloView.leftConstraint.constant = 0;
self.soloView.topConstraint.constant = 0;
self.soloView.widthConstraint.constant = 0;
self.soloView.heightConstraint.constant = 0;
[self.view layoutSubtreeIfNeeded];
} completionHandler:nil];
}
- (void) moveBackFromFullView
{
// this is where the solo view should animate back to
NSRect destR = [self.imageviews[self.soloIndex] frame];
CGFloat leftSpace = destR.origin.x;
CGFloat topSpace = destR.origin.y;
CGFloat width = destR.size.width - self.view.bounds.size.width;
CGFloat height = destR.size.height - self.view.bounds.size.height;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = YES;
context.duration = 0.75;
for(NSImageView *p in self.imageviews){
p.alphaValue = 1.00;
}
self.soloView.leftConstraint.constant = leftSpace;
self.soloView.topConstraint.constant = topSpace;
self.soloView.widthConstraint.constant = width;
self.soloView.heightConstraint.constant = height;
[self.view layoutSubtreeIfNeeded];
} completionHandler:^{
[self.soloView removeFromSuperview];
self.soloView = nil;
}];
}
使用上述基于约束的代码,soloView 的左侧和顶部正确地移动到全视图和返回,但是当进入全视图时,soloView 的宽度和高度没有动画并且似乎只是停留在目标大小。但是......它可以正确返回......宽度和高度也是如此。
使用框架
- (void) moveToFullView:(NSInteger)which
{
self.soloIndex = which;
NSImageView *tmpView = self.imageviews[which];
self.soloView = [[NSImageView alloc] initWithFrame:tmpView.frame];
[self.soloView.translatesAutoresizingMaskIntoConstraints = NO;
self.soloView.imageScaling = NSImageScaleProportionallyUpOrDown;
self.soloView.image = tmpView.image;
self.soloView.autoresizingMask = NSViewHeightSizable | NSViewWidthSizable;
[self.view addSubview:self.soloView];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = YES;
context.duration = 0.75;
for(NSImageView *p in self.imageviews){
p.alphaValue = 0;
}
self.soloView.frame = NSMakeRect(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
} completionHandler:nil];
}
- (void) moveBackFromFullView
{
NSRect destR = [self.imageviews[self.soloIndex] frame];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = YES;
context.duration = 0.95;
for(NSImageView *p in self.imageviews){
p.alphaValue = 1.00;
}
self.soloView.frame = destR;
} completionHandler:^{
[self.soloView removeFromSuperview];
self.soloView = nil;
}];
}
使用上述基于帧的代码,soloView 以正确的宽度和高度在全视图之间正确设置动画。但是当回去的时候有一些非常奇怪的事情......还有另一个鬼视图,从原点动画到同一个目的地。我不知道如何有另一个动画视图。我什至检查了 self.view 的子视图数量,它是 10,但我看到 11.... 9 个网格视图,soloview 和这个幽灵视图。是的,创建主视图时我有 self.view.wantsLayer = YES 。
我一定要疯了。两种代码都不起作用。我讨厌为这样一个简单的任务使用约束。我宁愿使用基于框架的代码……白天和黑夜都简单得多,但我不知道为什么会有另一个视图向后移动。
与 NSView 动画相比,我对 UIView 动画有更多的经验,所以也许我缺少一些基本的东西。
任何帮助将不胜感激。