项目下载链接
我认为没有必要离开 Quartz 来实现这种复制。您描述的所有内容以及我从与 Impress.js 混淆中收集到的所有内容似乎都可以通过将转换(主要是 2D,一些 3D)应用到添加到容器视图中的一组 UILabel 来复制,这些 UILabel 可以在容器视图中自由移动主视图。
为此,我创建的项目使用了一个UILabel
名为“ImpressLabel”的子类,并带有一个额外的初始化函数,在该函数中,不是将标签传递给框架,而是将大小、中心点和CGFloat
标签在 Z 轴上的旋转传递给它. 此转换在实例化时应用于标签,因此当您设置标签时,它们将出现在屏幕上您指定的位置和转换中。
然后就配置文本而言,您可以传递标签 anNSAttributedString
而不是 a NSString
。这允许你独立修改字符串的不同部分,因此标签中的不同单词可以是不同的大小、字体、颜色、背景颜色等。下面是上面两段的示例:
ImpressLabel *label1 = [[ImpressLabel alloc] initWithSize:CGSizeMake(260.0f, 80.0f) andCenterPointInSuperview:CGPointMake(500.0f, 500.0f) andRotationInSuperview:0.0f andEndingScaleFactor:1.3];
NSMutableAttributedString *firstLabelAttributes = [[NSMutableAttributedString alloc] initWithString:@"then you should try\nimpress.js*\n* no rhyme intended"];
[firstLabelAttributes addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:label1.font.pointSize - 2]
range:NSMakeRange(0, 19)];
[firstLabelAttributes addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:label1.font.pointSize - 8]
range:NSMakeRange(firstLabelAttributes.string.length - 19, 19)];
[firstLabelAttributes addAttribute:NSFontAttributeName
value:[UIFont boldSystemFontOfSize:label1.font.pointSize + 14]
range:NSMakeRange(23, 11)];
[label1 setNumberOfLines:3];
[label1 setAttributedText:firstLabelAttributes];
[label1 setTextAlignment:NSTextAlignmentCenter];
[containmentView addSubview:label1];
现在,更多地了解整个操作。正如我上面提到的,这个子类为每个标签添加了一个点击手势。当点击被识别时,会发生一些事情。包含标签的视图将通过调整其比例来平移/后退/远离。它还将开始旋转并在主视图中调整其锚点,以便当动画停止时,所选标签将以正确的方向在屏幕上居中。然后当然,当这一切都在进行时,所选标签的 alpha 将提高到 1.0f,而其余标签的 alpha 将降低到 0.25f。
- (void)tapForRotationDetected:(UITapGestureRecognizer *)sender {
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
[scale setToValue:[NSNumber numberWithFloat:0.8]];
[scale setAutoreverses:YES];
[scale setDuration:0.3];
//Create animation to adjust the container views anchorpoint.
CABasicAnimation *adjustAnchor = [CABasicAnimation animationWithKeyPath:@"anchorPoint"];
[adjustAnchor setFromValue:[NSValue valueWithCGPoint:self.superview.layer.anchorPoint]];
[adjustAnchor setToValue:[NSValue valueWithCGPoint:CGPointMake(self.center.x / self.superview.frame.size.width, self.center.y / self.superview.frame.size.height)]];
[adjustAnchor setRemovedOnCompletion:NO];
[adjustAnchor setFillMode:kCAFillModeForwards];
//Create animation to rotate the container view within its superview.
CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
//Create the animation group to apply these transforms
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
[animationGroup setAnimations:@[adjustAnchor,rotation]];
[animationGroup setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[animationGroup setDuration:0.6];
//Apply the end results of the animations directly to the container views layer.
[self.superview.layer setTransform:CATransform3DRotate(CATransform3DIdentity, DEGREES_TO_RADIANS(-self.rotationInSuperview), 0.0f, 0.0f, 1.0f)];
[self.superview.layer setAnchorPoint:CGPointMake(self.center.x / self.superview.frame.size.width, self.center.y / self.superview.frame.size.height)];
[self.superview.layer addAnimation:animationGroup forKey:@"animationGroup"];
//Animate the alpha property of all ImpressLabels in the container view.
[self.superview bringSubviewToFront:self];
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
for (ImpressLabel *label in sender.view.superview.subviews) {
if ([label isKindOfClass:[ImpressLabel class]]) {
if (label != self) {
[label setAlpha:0.25f];
}else{
[label setAlpha:1.0f];
}
}
}
} completion:nil];
}
现在,解决您的问题中列出的一些问题。
我在 Instruments 的分配工具中分析了这个项目,它总共只消耗大约 3.2 MB,所以我想说这种方法足够有效。
我提供的示例为 2D 空间中的大多数对象设置动画,但缩放动画除外,这充其量只是一种错觉。我在这里所做的只是作为一个例子来说明如何做到这一点,并不是一个 100% 完整的演示,因为正如我上面所说的,动画真的不是我的专业领域。但是,通过查看文档,似乎在第三维中旋转标签并调整其超级视图以旋转所有标签并使所选标签保持平坦的关键是使用CATransform3DInvert()
. 虽然我还没有时间完全弄清楚它是否有效,但看起来它可能正是这种情况下所需要的。
就镜像而言,我认为正确缩放所有内容不会有任何问题。查看 Apple 的Multiple Display Programming Guide,看起来传递给 的对象NSNotification
UIScreenDidConnectNotification
是一个UIScreen
对象。既然是这种情况,你可以很容易地请求这个显示边界,并相应地调整标签的框架和容器视图。
注意:在此示例中,由于锚点坐标生成不正确,因此只有 0、90、180 和 -90 度的变换动画 100% 正确。看起来解决方案在于CGPointApplyAffineTransform(<#CGPoint point#>, <#CGAffineTransform t#>)
,但我也没有像我希望的那样有太多时间玩它。无论哪种方式,这应该足以让您开始繁殖。
不过,这确实激发了我的兴趣,当我有机会再次研究这个问题时,我很乐意用任何新信息更新这篇文章。希望这可以帮助!