我正在尝试在使用AutoLayout的窗口(xib)中执行一些非常简单的视图动画,并且我得到了一些非常不可预测的结果。显然,自动布局引擎正在做一些事情,我不知道如何解决它。
首先,我的窗口有一个3x3 NSImageViews网格,设置为NSImageScaleProportionallyUpOrDown。将内容压缩设置为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可以在全视图中正确地设置动画,并具有正确的宽度和高度。但回去时有一些非常奇怪的东西......还有另一个 ghost 视图,从原点到同一目的地的动画。我不知道如何有另一个动画视图。我甚至检查了self.view的子视图的数量,它是10,但我看到了11 .... 9个网格视图,独奏视图和这个鬼视图。是的,我在创建主视图时有self.view.wantsLayer = YES。
我一定是疯了。两个代码都不起作用。我讨厌使用约束来完成这么简单的任务。我宁愿使用基于帧的代码......日夜都要简单得多,但我不知道为什么会有另一种观点向后移动。
我对UIView动画有比NSView动画更多的经验,所以也许我缺少一些基本的东西。
非常感谢任何帮助。