通过 TouchMoved 进行缩放:
我有一个使用 UIBezierPath 制作的 饼图 。我现在需要这些单独的路径(饼图)是可扩展的。我相信您需要一个视图才能使用捏缩放,所以我认为 TouchMoved: 是可行的方法(除非有解决方法)。
任何建议或帮助表示赞赏!
更新/进度代码
MySliceClass.m
+ (UIBezierPath *)sliceRadius:(float)radius andStartingAngle:(float)startingAngle andFinishingAngle:(float)finishingAngle
{
static UIBezierPath *path = nil;
path = [UIBezierPath bezierPath];
CGPoint center = {300,300};
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:radians(startingAngle) endAngle:radians(finishingAngle) clockwise:YES];
[path closePath];
path.lineWidth = 1;
[[UIColor redColor] setFill];
[path fill];
return path;
}
MySliceView.m
- (void)drawRect:(CGRect)rect
{
NSArray *arrayOfSlices = [NSArray arrayWithObjects:
slice01 = [WordplaySlice sliceRadius:200 andStartingAngle:0.5 andFinishingAngle:29.5],
slice02 = [WordplaySlice sliceRadius:200 andStartingAngle:30.5 andFinishingAngle:59.5],
slice03 = [WordplaySlice sliceRadius:200 andStartingAngle:60.5 andFinishingAngle:89.5],
slice04 = [WordplaySlice sliceRadius:200 andStartingAngle:90.5 andFinishingAngle:119.5],
slice05 = [WordplaySlice sliceRadius:200 andStartingAngle:120.5 andFinishingAngle:149.5],
slice06 = [WordplaySlice sliceRadius:200 andStartingAngle:150.5 andFinishingAngle:179.5],
slice07 = [WordplaySlice sliceRadius:200 andStartingAngle:180.5 andFinishingAngle:209.5],
slice08 = [WordplaySlice sliceRadius:200 andStartingAngle:210.5 andFinishingAngle:239.5],
slice09 = [WordplaySlice sliceRadius:200 andStartingAngle:240.5 andFinishingAngle:269.5],
slice10 = [WordplaySlice sliceRadius:200 andStartingAngle:270.5 andFinishingAngle:299.5],
slice11 = [WordplaySlice sliceRadius:200 andStartingAngle:300.5 andFinishingAngle:329.5],
slice12 = [WordplaySlice sliceRadius:200 andStartingAngle:330.5 andFinishingAngle:359.5], nil];
}
I have a pie chart which is made using UIBezierPath's. I now need those individual paths (pie pieces) to be scalable. I believe you need a view to be able to use pinch scaling, so I think touchesMoved: is the way to go (unless there's a workaround).
Any advice or help is appreciated!
Updated/Progress code
MySliceClass.m
+ (UIBezierPath *)sliceRadius:(float)radius andStartingAngle:(float)startingAngle andFinishingAngle:(float)finishingAngle
{
static UIBezierPath *path = nil;
path = [UIBezierPath bezierPath];
CGPoint center = {300,300};
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:radians(startingAngle) endAngle:radians(finishingAngle) clockwise:YES];
[path closePath];
path.lineWidth = 1;
[[UIColor redColor] setFill];
[path fill];
return path;
}
MySliceView.m
- (void)drawRect:(CGRect)rect
{
NSArray *arrayOfSlices = [NSArray arrayWithObjects:
slice01 = [WordplaySlice sliceRadius:200 andStartingAngle:0.5 andFinishingAngle:29.5],
slice02 = [WordplaySlice sliceRadius:200 andStartingAngle:30.5 andFinishingAngle:59.5],
slice03 = [WordplaySlice sliceRadius:200 andStartingAngle:60.5 andFinishingAngle:89.5],
slice04 = [WordplaySlice sliceRadius:200 andStartingAngle:90.5 andFinishingAngle:119.5],
slice05 = [WordplaySlice sliceRadius:200 andStartingAngle:120.5 andFinishingAngle:149.5],
slice06 = [WordplaySlice sliceRadius:200 andStartingAngle:150.5 andFinishingAngle:179.5],
slice07 = [WordplaySlice sliceRadius:200 andStartingAngle:180.5 andFinishingAngle:209.5],
slice08 = [WordplaySlice sliceRadius:200 andStartingAngle:210.5 andFinishingAngle:239.5],
slice09 = [WordplaySlice sliceRadius:200 andStartingAngle:240.5 andFinishingAngle:269.5],
slice10 = [WordplaySlice sliceRadius:200 andStartingAngle:270.5 andFinishingAngle:299.5],
slice11 = [WordplaySlice sliceRadius:200 andStartingAngle:300.5 andFinishingAngle:329.5],
slice12 = [WordplaySlice sliceRadius:200 andStartingAngle:330.5 andFinishingAngle:359.5], nil];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为如果为每个切片创建一个视图并使用
UIPinchGestureRecognizer
,您会发现更容易。方法如下。首先,我们需要一个 UIView 子类来绘制一个切片。它还应该重写 pointInside:withEvent: 来忽略落在切片外部的触摸(即使触摸位于视图的矩形边界内)。
因此,我们将创建一个名为
SliceView
的类。它使用CAShapeLayer
进行切片绘制:我们通过覆盖
layerClass< 来告诉它使用
CAShapeLayer
而不是普通的CALayer
/代码> 方法。我们还将添加一个方便的方法,以 CAShapeLayer 的形式返回视图层。我们将在
layoutSubviews
中计算切片的路径,因为每当其大小发生更改时,视图都会收到layoutSubviews
消息。我们将布置每个切片视图以覆盖整个饼图,但只绘制饼图的楔形部分。每个切片的框架将覆盖整个屏幕(如果饼图是全屏的)。这意味着切片视图知道其圆弧的中心位于其边界的中心。但随后我们使用一点三角函数来在相邻切片之间添加填充。
我们还调整图层的锚点;这是当您缩放或旋转图层时图层中不会移动的点。我们希望锚点位于最靠近中心的切片的角处。
当与切片相关的任何视图属性发生更改时,我们需要重新计算概述切片的路径。当切片的填充颜色更改时,我们需要将该更改传递到图层。所以我们将重写属性设置器。
最后,我们重写 pointInside:withEvent: ,以便命中测试仅在触摸实际上位于切片路径内部时才将触摸分配给切片视图。这很重要,因为所有切片视图都将有一个覆盖整个屏幕的框架。
现在我们有了一个方便的
SliceView
类,我们可以使用它来绘制带有可缩放切片的饼图。在 iPhone 屏幕上很难将两根手指放入切片中,因此我们将让用户点击切片来选择它,然后在任意位置捏合以缩放所选切片。 (该界面还使其可以在模拟器中进行测试。)我们将用红色绘制未选定的切片,用蓝色绘制选定的切片。
当用户点击切片时,我们需要更改先前选择和新选择的颜色,并记录新选择。
当用户捏合时,我们会调整所选切片(如果有)的变换。
最后,我们需要实际创建切片视图和手势识别器。我们为每个切片创建一个点击识别器,并在背景视图上附加一个“全局”捏合识别器。
它看起来像这样:
您可以在此处下载我的测试项目:http://dl.dropbox.com/u/26919672/pie.zip
更新
为了回应您询问限制规模的评论,我建议向
SliceView:
重要:您需要将
initWithFrame:
和initWithCoder:
中的所有三个属性初始化为 1。然后,实现
scale
设置器来实际执行限制并设置比例:在
pinched:
中,您可以更新视图的scale
属性直接设置视图的transform
属性:I think you will find it easier if you create a view for each slice, and use a
UIPinchGestureRecognizer
. Here's how.First, we need a
UIView
subclass that draws one slice. It should also overridepointInside:withEvent:
to ignore a touch that lands outside the slice (even if the touch is inside the view's rectangular bounds).So we'll make a class called
SliceView
. It usesCAShapeLayer
to do the slice drawing:We tell it to use a
CAShapeLayer
instead of a plainCALayer
by overriding thelayerClass
method. We'll also add a handy method that returns the view's layer as aCAShapeLayer
.We'll compute the path of the slice in
layoutSubviews
, because the view receives thelayoutSubviews
message any time its size is changed.We're going to lay out each slice view to cover the entire pie, but only draw its wedge of the pie. Each slice's frame will cover the entire screen (if the pie is full-screen). That means the slice view knows that the center of its arc is at the center of its bounds. But then we use a little trigonometry to put in the padding between adjacent slices.
We also adjust the anchor point of the layer; this is the point in the layer that doesn't move when you scale or rotate the layer. We want the anchor point to be at the corner of the slice nearest the center.
When any of the view's properties relating to the slice are changed, we need to recompute the path outlining the slice. And when the fill color of the slice is changed, we need to pass that change along to the layer. So we'll override the property setters.
Finally, we override
pointInside:withEvent:
so that hit-testing will only assign a touch to a slice view if the touch is actually inside the path of the slice. This is critical since all of the slice views will have a frame that covers the whole screen.Now that we have a handy
SliceView
class, we can use it to draw a pie chart with zoomable slices. It's hard to fit two fingers into a slice on an iPhone screen, so we'll let the user tap a slice to select it, and pinch anywhere to scale the selected slice. (This interface also makes it testable in the simulator.)We'll draw unselected slices in red and the selected slice in blue.
When the user taps a slice, we'll need to change the colors of the prior selection and the new selection, and record the new selection.
When the user pinches, we adjust the transform of the selected slice, if there is one.
Finally, we need to actually create the slice views and the gesture recognizers. We create one tap recognizer for each slice, and one “global” pinch recognizer attached to the background view.
And here's what it looks like:
You can download my test project here: http://dl.dropbox.com/u/26919672/pie.zip
UPDATE
In response to your comment asking about limiting the scale, I would suggest adding some more properties to
SliceView
:Important: You will need to initialize all three properties to 1 in
initWithFrame:
andinitWithCoder:
.Then, implement the
scale
setter to actually enforce the limits and set the scale:In
pinched:
, you update thescale
property of the view instead of setting the view'stransform
property directly:首先,将切片存储在数组中可能是一个更好的主意。
其次,定义一个类
MySliceClass
(可以继承自 UIBezierPath)也是一个更好的主意。此类具有定义切片的属性:startingAngle
、endAngle
。现在,您不仅改进了代码,而且调整切片大小也变得更加容易。
您必须向类
MySliceClass
添加一个参数radius
,每次触摸切片时,您都会更改其半径,并调用[self setNeedsDisplay] 以便调用方法
drawRect
。最后,您还应该将初始化移至视图的初始化方法中,因为这样做时,每次绘制视图时都会创建新的切片。
编辑这是一个示例实现
,
我没有测试此代码,因此可能存在一些错误。但我希望你可以从这里开始。
First, it is probably a better idea to store the slices in an array.
Second, it is also a better idea to define a class
MySliceClass
(that may inherit from UIBezierPath). This class has properties that define a slice:startingAngle
,endAngle
.Now not only you improved your code, but it becomes easier to resize the slices.
You have to add a parameter
radius
to the classMySliceClass
, and every time a slice is touched, you change its radius, and call[self setNeedsDisplay]
so that the methoddrawRect
gets called.Finally, you should also move the initialization to the initialization method of your view because the way you do it, you create new slices every time you draw the view.
EDIT Here is an example implementation
And
I didn't test this code, so there might be some errors. But you can start from this I hope.