9

我正在尝试在 Objective C 中重现 Impress.js 的示例演示中的一些 2D 转换。特别是旋转、平移和缩放 - 现在我专注于缩放。

我尝试将 UILabel 缩放到它“通过屏幕”的程度,就像示例演示中的“可视化你的大想法”->“和小想法”一样。

这是我迄今为止尝试过的:

UILabel *label = [[UILabel alloc] init];

label.text = @"Hello World!";
label.textColor = [UIColor blackColor];
label.font = [UIFont fontWithName:@"Arial" size:18.f];
[label sizeToFit];
label.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
[self.view addSubview:label];

label.contentScaleFactor *= 80;

[UIView animateWithDuration:5 animations:^{
    label.transform = CGAffineTransformScale(label.transform, 80, 80);
}];

不幸的是,这会占用大约 30-60 MB 的 RAM,具体取决于 contentScaleFactor 和初始字体大小。如果我不增加 contentScaleFactor 文本看起来很模糊。增加字体大小似乎也消耗了同样多的内存。

这是它在分析器中的样子:

在此处输入图像描述

而这只是一个单一的 UILabel。

有没有什么办法可以做到这一点,而不会消耗太多内存,又不会牺牲正在渲染的文本或过渡的质量?

4

2 回答 2

4

项目下载链接

我认为没有必要离开 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];
}

现在,解决您的问题中列出的一些问题。

  1. 我在 Instruments 的分配工具中分析了这个项目,它总共只消耗大约 3.2 MB,所以我想说这种方法足够有效。

  2. 我提供的示例为 2D 空间中的大多数对象设置动画,但缩放动画除外,这充其量只是一种错觉。我在这里所做的只是作为一个例子来说明如何做到这一点,并不是一个 100% 完整的演示,因为正如我上面所说的,动画真的不是我的专业领域。但是,通过查看文档,似乎在第三维中旋转标签并调整其超级视图以旋转所有标签并使所选标签保持平坦的关键是使用CATransform3DInvert(). 虽然我还没有时间完全弄清楚它是否有效,但看起来它可能正是这种情况下所需要的。

  3. 就镜像而言,我认为正确缩放所有内容不会有任何问题。查看 Apple 的Multiple Display Programming Guide,看起来传递给 的对象NSNotification UIScreenDidConnectNotification是一个UIScreen对象。既然是这种情况,你可以很容易地请求这个显示边界,并相应地调整标签的框架和容器视图。

注意:在此示例中,由于锚点坐标生成不正确,因此只有 0、90、180 和 -90 度的变换动画 100% 正确。看起来解决方案在于CGPointApplyAffineTransform(<#CGPoint point#>, <#CGAffineTransform t#>),但我也没有像我希望的那样有太多时间玩它。无论哪种方式,这应该足以让您开始繁殖。

不过,这确实激发了我的兴趣,当我有机会再次研究这个问题时,我很乐意用任何新信息更新这篇文章。希望这可以帮助!

于 2013-03-11T20:53:44.360 回答
1

我认为如果您在 CALayer 上绘制文本会有所帮助。一个可以帮助解决这个问题的库是:https ://github.com/ZaBlanc/CanvasKit 在新的 CALayer 上绘制动画的下一步并转换为它。您可以使用此库链接所需的动画:https ://github.com/yangmeyer/CPAnimationSequence或此库:https ://github.com/clayallsopp/Walt

于 2013-03-11T07:09:31.867 回答