如何在不使用大量内存的情况下扩展UILabel的转换?

Luk*_*uke 9 core-animation opengl-es cocos2d-iphone ios impress.js

我正在尝试重现Impress.js 在Objective C中的示例演示中的一些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);
}];
Run Code Online (Sandbox Code Playgroud)

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

以下是它在profiler中的外观:

在此输入图像描述

这只是一个UILabel.

有没有办法做到这一点,而不会占用太多的内存,而不会牺牲正在呈现的文本或过渡的质量?

Mic*_*lum 4

项目下载链接

我认为没有必要离开 Quartz 来实现这种再现。您所描述的所有内容以及我从弄乱 Impress.js 中收集到的所有内容似乎都可以通过将变换(主要是 2D,一些 3D)应用到添加到容器视图中的一组 UILabel 来复制,这些 UILabel 可以在容器视图中自由移动。主视图。

为此,我创建的项目使用了一个UILabel名为“ImpressLabel”的子类,并带有一个额外的 init 函数,其中不是向标签传递框架,而是向其传递尺寸、中心点和CGFloat用于标签在 Z 轴上旋转的a 。此变换在实例化时应用于标签,以便当您设置标签时,它们将以您指定的位置和变换显示在屏幕上。

然后就配置文本而言,您可以传递标签 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];
Run Code Online (Sandbox Code Playgroud)

现在,我们来谈谈整个行动的更多内容。正如我上面提到的,这个子类向每个标签添加了一个点击手势。当识别到轻击时,会发生一些事情。包含标签的视图将通过调整其比例来平移/后退/远离。它还将开始旋转并调整其在主视图中的锚点,以便当动画停止时,所选标签将以正确的方向在屏幕上居中。当然,当这一切进行时,所选标签的 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];
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

注意:在此示例中,由于锚点的坐标生成不正确,因此只有 0、90、180 和 -90 度变换动画 100% 正确。看起来解决方案在于CGPointApplyAffineTransform(<#CGPoint point#>, <#CGAffineTransform t#>),但我又没有足够的时间来玩它,因为我希望。不管怎样,这应该足以帮助您开始复制。

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