我需要翻译和缩放 UIView 从屏幕的中心到右上角。
_____________________________
| |
| |
| _________ |
| | | | START
| | | |
| |_________| |
| |
| |
|____________________________|
_____________________________
| __ |
| |__| |
| |
| | END
| |
| |
| |
| |
|____________________________|
我验证了我个人对比例和平移的计算是准确的。但是一旦它们放在一起,它们就会相互冲突。下面的代码使视图最终太靠近中心。
CGFloat finalPadding = 10.0f;
CGFloat finalScale = 46.0f / 170.0f;
CGFloat finalX = self.view.frame.size.width
- self.platformProgressView.frame.size.width * finalScale
- finalPadding;
CGFloat finalY = finalPadding;
CGFloat deltaX = finalX - self.platformProgressView.frame.origin.x;
CGFloat deltaY = finalY - self.platformProgressView.frame.origin.y;
[UIView
animateWithDuration:1.0
delay:1.0
options:UIViewAnimationOptionCurveEaseOut
animations:^(void){
self.platformProgressView.transform = CGAffineTransformConcat(
CGAffineTransformMakeTranslation(deltaX, deltaY),
CGAffineTransformMakeScale(finalScale, finalScale)
);
}
completion:^(BOOL finished) {
}
];
最终效果:
_____________________________
| |
| |
| __ |
| |__| |
| |
| |
| |
| |
|____________________________|
更改乘法的顺序会导致视图偏离屏幕的右边缘。
self.platformProgressView.transform = CGAffineTransformConcat(
CGAffineTransformMakeScale(finalScale, finalScale),
CGAffineTransformMakeTranslation(deltaX, deltaY)
);
最终效果(预计):
_____________________________
| |
| | __
| | |__|
| |
| |
| |
| |
| |
|____________________________|
单独应用它们会导致突然跳跃,最终位置甚至更糟。
self.platformProgressView.transform = CGAffineTransformMakeTranslation(deltaX, deltaY);
self.platformProgressView.transform = CGAffineTransformMakeScale(finalScale, finalScale);
最终效果:
_____________________________
| |
| |
| |
| |
| __ |
| |__| |
| |
| |
|____________________________|
了解转换
最主要的是,变换的原点是视图矩形的中心点。
首先我们翻译视图。v1 是其原始位置的视图,v2 是其翻译位置的视图。p 是您想要的填充(代码中的finalPadding
)。c 标记视图的中心点。
+--------------------------------+
| ^ |
| | p |
| v |
| +- v2 --------+ |
| | | |
| | c |<->|
| | | p |
| +-------------+ |
| |
| |
| +- v1 --------+ |
| | | |
| | c | |
| | | |
| +-------------+ |
| |
+--------------------------------+
接下来我们缩放视图。v3 是处于其缩放位置的视图。注意 v3 的中心点如何保持不变,而它周围的视图尺寸缩小。尽管尺寸是正确的,但视图的定位和产生的填充 p '是错误的。
+--------------------------------+
| ^ |
| | p' |
| | |
| v |
| +- v3 --+ |
| | c |<---->|
| +-------+ p' |
| |
| |
| +- v1 --------+ |
| | | |
| | c | |
| | | |
| +-------------+ |
| |
+--------------------------------+
增量计算的修复
现在我们知道了缩放是如何工作的,我们需要修复计算翻译增量的代码。
CGRect windowFrame = self.window.frame;
CGRect viewFrame = self.platformProgressView.frame;
CGPoint finalCenter = CGPointZero;
finalCenter.x = (windowFrame.size.width
- (viewFrame.size.width * finalScale) / 2.0f
- finalPadding);
finalCenter.y = (finalPadding
+ (viewFrame.size.height * finalScale) / 2.0f);
CGPoint viewCenter = self.platformProgressView.center;
CGFloat deltaX = finalCenter.x - viewCenter.x;
CGFloat deltaY = finalCenter.y - viewCenter.y;
转换的顺序
最后,正如您自己所指出的,转换与CGAffineTransformConcat
连接的顺序很重要。在您的第一次尝试中,您有顺序 1)transform + 2)scale。结果是缩放变换-在序列的后面-影响为翻译变换指定的增量。
这里有 2 个解决方案:要么你自己的第二次尝试,你反转序列,使它变成 1)scale + 2)transform。或者你使用我在我的答案的第一个修订版中建议的 helper 函数。所以这
self.platformProgressView.transform = CGAffineTransformConcat(
CGAffineTransformMakeScale(finalScale, finalScale),
CGAffineTransformMakeTranslation(deltaX, deltaY)
);
等于
CGAffineTransform CGAffineTransformMakeScaleTranslate(CGFloat sx, CGFloat sy, CGFloat dx, CGFloat dy)
{
return CGAffineTransformMake(sx, 0.0f, 0.0f, sy, dx, dy);
}
self.platformProgressView.transform = CGAffineTransformMakeScaleTranslate(finalScale, finalScale, deltaX, deltaY);
不同的解决方案:设置锚点
如果你不满意中心点是视图变换的原点,你可以通过设置视图的 CALayer 的anchorPoint
属性来改变这一点。默认的锚点是 0.5 / 0.5,代表视图矩形的中心。显然,锚点不是坐标,而是一种乘法因子。
如果我们假设锚点在视图的左上角,那么您对平移增量的原始计算是正确的。所以如果你这样做
self.platformProgressView.layer.anchorPoint = CGPointMake(0.0f, 0.0f)
你可以保留你原来的计算。
参考
那里可能有更多的信息材料,但是 WWDC 2011 视频Understanding UIKit Rendering是我最近观看的,这极大地帮助我提高了对边界,中心,变换和框架之间关系的理解。
此外,如果您要更改 CALayeranchorPoint
属性,您可能应该首先阅读CALayer cl reference中的属性文档。
变换适用于所有先前的变换。因此,必须考虑到这一点并仔细考虑顺序。
这些值需要配置为-如果一个发生,然后下一个,即使它们连接到一个变换。

这是一个很好的答案,这里是如何在 Swift 4 中做到这一点
let containerFrame: CGRect = self.view.frame
let viewFrame: CGRect = self.containerView.frame
var finalCenter: CGPoint = CGPoint.zero
let finalPadding: CGFloat = 0
let finalScale: CGFloat = 2.0 //whatever scale that you want
finalCenter.x = (containerFrame.size.width - (viewFrame.size.width * finalScale) / 2.0 - finalPadding)
finalCenter.y = (finalPadding + (viewFrame.size.height * finalScale) / 2.0);
let containerCenter: CGPoint = self.containerView.center
let deltaX: CGFloat = finalCenter.x - containerCenter.x
let deltaY: CGFloat = finalCenter.y - containerCenter.y
let scale = CGAffineTransform(scaleX: finalScale, y: finalScale)
//MARK: changing the deltas for positive to negative values will change the origin x and y of the transformed view
let translation = CGAffineTransform(translationX: -deltaX, y: deltaY)
self.containerView.transform = scale.concatenating(translation)

旋转和平移:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let yPos = scrollView.contentOffset.y
if yPos < 0 {
let scaleX = ((yPos * -1) / 200) + 1
let translateY = yPos / 2
var commonTransform = CGAffineTransform.identity
commonTransform = commonTransform.translatedBy(x: 0, y: translateY)
commonTransform = commonTransform.scaledBy(x: scaleX, y: scaleX)
self.customView?.transform = commonTransform
}else{
self.customView?.transform = CGAffineTransform.identity
}
}
本站系公益性非盈利分享网址,本文来自用户投稿,不代表码文网立场,如若转载,请注明出处
评论列表(46条)