CodeV

4.5-变换路径

Bezier路径的applyTransform:方法通过应用作为方法参数传递的仿射变换矩阵来转换所有路径的点和控制点。此更改发生在适当位置,并将更新调用路径。例如,在应用以下缩放变换后,整个myPath路径缩小:

1
[myPath applyTransform:CGAffineTransformMakeScale(0.5f, 0.5f)];

如果您希望保留原始路径,请创建副本并将变换应用于副本。这使您可以执行多个更改,而原始项目保持原样:

` objc
UIBezierPath *pathCopy = [myPath copy];
[pathCopy applyTransform: CGAffineTransformMakeScale(0.5f, 0.5f)];

1
2
3
4
5
6
7
8

您不能通过应用identity转换来“还原”路径。它产生一个“无操作”结果(路径保持不变),而不是一个复原的结果(路径返回到原来的状态)。

### 原始问题
应用变换不总是产生您期望的结果。以图4-7为例。我创建了一个路径并对其应用了旋转:

``` objc
[path applyTransform:CGAffineTransformMakeRotation(M_PI / 9)];

图4-7中的左侧图像显示了您可能期望发生的情况,其中图像围绕其中心旋转20度。右图显示了当你不控制变换的起点时,实际发生的情况。该路径围绕当前坐标系的原点旋转。

图4-7

图4-7原点未明确设置为路径中心的旋转会产生意外的结果。

幸运的是,确保旋转和缩放比预期的方式更容易。清单4-4详细介绍了ApplyCenteredPathTransform(),该函数用于设置仿射变换,然后重置坐标系。这些额外的步骤产生了您期望的结果。

清单4-4围绕其中心旋转路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Translate path’s origin to its center before applying the transform
void ApplyCenteredPathTransform(UIBezierPath *path, CGAffineTransform transform)
{
CGPoint center = PathBoundingCenter(path);
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformTranslate(t, center.x, center.y);
t = CGAffineTransformConcat(transform, t);
t = CGAffineTransformTranslate(t, -center.x, -center.y);
[path applyTransform:t];
}

// Rotate path around its center
void RotatePath(UIBezierPath *path, CGFloat theta)
{
CGAffineTransform t = CGAffineTransformMakeRotation(theta);
ApplyCenteredPathTransform(path, t);
}

其他变换

与旋转一样,如果要缩放对象,请确保将变换应用于路径bounds的中心。图4-8显示了一个在每个维度上将路径缩放85%的转换。创建此缩放以及许多其他方便的转换的函数如清单4-5所示。

图4-8

图4-8小的绿色的图相对于原始的图缩放了(0.85,0.85)

清单4-4和4-5实现了Quartz绘图任务所需的许多最常见的变换。除了旋转和缩放选项之外,这些功能使您能够将路径移动到各个位置,并将项目镜像到某个位置。原点居中确保每个变换以最常规的预期方式发生。

清单4-5应用变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Scale path to sx, sy
void ScalePath(UIBezierPath *path, CGFloat sx, CGFloat sy)
{
CGAffineTransform t = CGAffineTransformMakeScale(sx, sy); ApplyCenteredPathTransform(path, t);
}

// Offset a path
void OffsetPath(UIBezierPath *path, CGSize offset)
{
CGAffineTransform t = CGAffineTransformMakeTranslation(
offset.width, offset.height);
ApplyCenteredPathTransform(path, t);
}

// Move path to a new origin
void MovePathToPoint(UIBezierPath *path, CGPoint destPoint)
{
CGRect bounds = PathBoundingBox(path);
CGSize vector =PointsMakeVector(bounds.origin, destPoint);
OffsetPath(path, vector);
}

// Center path around a new point
void MovePathCenterToPoint(
UIBezierPath *path, CGPoint destPoint)
{
CGRect bounds = PathBoundingCenter(path);
CGSize vector = PointsMakeVector(bounds.origin, destPoint);
vector.width -= bounds.size.width / 2.0f;
vector.height -= bounds.size.height / 2.0f;
OffsetPath(path, vector);
}

// Flip horizontally
void MirrorPathHorizontally(UIBezierPath *path)
{
CGAffineTransform t = CGAffineTransformMakeScale(-1, 1);
ApplyCenteredPathTransform(path, t);
}

// Flip vertically
void MirrorPathVertically(UIBezierPath *path)
{
CGAffineTransform t = CGAffineTransformMakeScale(1, -1);
ApplyCenteredPathTransform(path, t);
}

本文翻译自《iOS Drawing Practical UIKit Solutions》作者:Erica Sadun,翻译:Cheng Dong。如果觉得本书不错请购买支持正版:亚马逊购买传送门,本书所有源代码可在GitHub上下载。译者虽然力求做到信,达,雅,但是由于时间仓促加之译者水平十分有限,文中难免会出现不正确,不准确,词不达意,难于理解的地方,还望各位批评指正,共同进步,谢谢。转载请注明出处。