ios怎么把一个控件随ios贝塞尔曲线画圆移动

如何画平滑的贝塞尔曲线--LxThroughPointsBezier
查看次数:2756
下载次数:553
上传时间:
大小:559 B
这个库主要通过设定几个点来画出平滑的贝塞尔曲线,可调整曲线的弯曲程度。
测试环境:Xcode 6.2,iOS 6.0以上
您还没有登录!请或
下载过该代码的还下载了
本周热门下载
&2016 Chukong Technologies,Inc.
京公网安备89iOS开发之画图板(贝塞尔曲线)
招聘信息:
贝塞尔曲线,听着挺牛气一词,不过下面我们在做画图板的时候就用到贝塞尔绘直线,没用到绘制曲线的功能。如果会点PS的小伙伴会对贝塞尔曲线有更直观的理解。这篇文章的重点不在于如何用使用贝塞尔曲线,而是利用贝塞尔划线的功能来封装一个画图板。
画图板的截图如下,上面的白板就是我们的画图板,是自己封装好的一个UIView,下面会详细的介绍如何封装这个画图板,下面的控件用来控制我们画图板的属性以及Undo,Redo和保存功能。点击保存时会把绘制的图片保存到手机的相册中。下面是具体的实现方案。
一、 封装画图板
其实上面的白板就是一继承于UiView的一个子类,我们可以在这个子类中添加我们画图板相应的属性和方法,然后实例化成对象添加到 ViewController中,当然为了省事添加白板的时候是通过storyboard来完成的,读者也可以自己实例化然后手动的添加到相应的 ViewController中。
1. 封装白板的第一步是新建一个UIView的子类MyView,然后添加相应的属性和方法。MyView.h中的代码如下,代码具体意思请参照注释:
#import&&&@interface&MyView&:&UIView&&@property&(nonatomic,&strong)&UIColor&*&&@property&(nonatomic,&assign)&CGFloat&lineW&&@property&(nonatomic,&strong)&NSMutableArray&*allL&&&-(void)initMyV&&-(void)backI&&-(void)forwardI&&@end&
2. 上面的代码是对外的接口,有些属性我们是写在MyView.m的延展中以实现私有的目的,MyView延展部分如下:
@interface&MyView()&&@property(nonatomic,&strong)&UIBezierPath&*&&@property(nonatomic,&strong)&NSMutableArray&*cancleA&@end&
3. 下面的代码就是实现部分的代码了,会根据不同功能给出相应的说明。
(1) 初始化我们的白板,给线条指定默认颜色和宽度并且给相应的变量分配内存空间,初始化代码如下:
&-(void)initMyView&{&&&&&self.color&=&[UIColor&redColor];&&&&&self.lineWidth&=&1;&&&&&self.allLine&=&[NSMutableArray&arrayWithCapacity:50];&&&&&self.cancleArray&=&[NSMutableArray&arrayWithCapacity:50];&}&
(2) Undo功能的封装,相当于两个栈,把显示的线条出栈,进入为不显示的线条栈中,每执行一次此操作显示线条栈中的元素会少一条而不显示线条栈中会多一条,大致就这个意思吧,代码如下:
&-(void)backImage&{&&&&&if&(self.allLine.count&>&0)&&&&&{&&&&&&&&&int&index&=&self.allLine.count&-&1;&&&&&&&&&&&&&&&&&&[self.cancleArray&addObject:self.allLine[index]];&&&&&&&&&&&&&&&&&&[self.allLine&removeObjectAtIndex:index];&&&&&&&&&&&&&&&&&&[self&setNeedsDisplay&&&&&&&&&&];&&&&&}&}&
(3) Redo操作和Undo操作相反,从未显示栈中取出元素放入显示的栈中,代码中的栈我们是用数组来表示的,代码如下:
&-(void)forwardImage&{&&&&&if&(self.cancleArray.count&>&0)&&&&&{&&&&&&&&&int&index&=&self.cancleArray.count&-&1;&&&&&&&&&&&&&&&&&&[self.allLine&addObject:self.cancleArray[index]];&&&&&&&&&&&&&&[self.cancleArray&removeObjectAtIndex:index];&&&&&&&&&&&&&&&&&&[self&setNeedsDisplay];&&&&&}&}&
(4) 当开始触摸时我们新建一个BezierPath,把触摸起点设置成BezierPath的起点,并把将要画出的线条以及线条对应的属性封装成字典添加到显示栈中,代码如下
-(void)touchesBegan:(NSSet&*)touches&withEvent:(UIEvent&*)event&{&&&&&&&&&&self.bezier&=&[UIBezierPath&bezierPath];&&&&&&&&&&&&&&&UITouch&*myTouche&=&[touches&anyObject];&&&&&CGPoint&point&=&[myTouche&locationInView:self];&&&&&&&&&&&&&&&[self.bezier&moveToPoint:point];&&&&&&&&&&&&&&&NSMutableDictionary&*tempDic&=&[[NSMutableDictionary&alloc]&initWithCapacity:3];&&&&&[tempDic&setObject:self.color&forKey:@"color"];&&&&&[tempDic&setObject:[NSNumber&numberWithFloat:self.lineWidth]&forKey:@"lineWidth"];&&&&&[tempDic&setObject:self.bezier&forKey:@"line"];&&&&&&&&&&&&&&&[self.allLine&addObject:tempDic];&&}&
(5) 当移动也就是划线的时候把点存储到BezierPath中,代码如下
-(void)touchesMoved:(NSSet&*)touches&withEvent:(UIEvent&*)event&{&&&&&UITouch&*myTouche&=&[touches&anyObject];&&&&&CGPoint&point&=&[myTouche&locationInView:self];&&&&&&&&&&[self.bezier&addLineToPoint:point];&&&&&&&&&&&&&&&[self&setNeedsDisplay];&&&&&&}&
(6) 画出线条
&&-&(void)drawRect:(CGRect)rect&{&&&&&&&&&&for&(int&i&=&0;&i&<&self.allLine.&i&++)&&&&&{&&&&&&&&&NSDictionary&*tempDic&=&self.allLine[i];&&&&&&&&&UIColor&*color&=&tempDic[@"color"];&&&&&&&&&CGFloat&width&=&[tempDic[@"lineWidth"]&floatValue];&&&&&&&&&UIBezierPath&*path&=&tempDic[@"line"];&&&&&&&&&&&&&&&&&&[color&setStroke];&&&&&&&&&[path&setLineWidth:width];&&&&&&&&&[path&stroke];&&&&&}&&}&
二、 画图板的使用
上面是封装画图板要用到的全部代码,下面的代码就是如何在ViewController中使用我们的画图板了,如何实例化控件,以及控件的初始化,注册回调等在这就不做赘述了,下面给出了主要控件的回调方法。
1. 通过Slider来调节线条的宽度
&-&(IBAction)sliderChange:(id)sender&{&&&&&self.myView.lineWidth&=&self.mySlider.&}&
2. 通过SegmentControl来设置线条的颜色
/通过segmentControl来设置线条的颜色&-&(IBAction)tapSegment:(id)sender&{&&&&&&&&&&switch&(self.mySegment.selectedSegmentIndex)&{&&&&&&&&&case&0:&&&&&&&&&&&&&self.myView.color&=&[UIColor&redColor];&&&&&&&&&&&&&break;&&&&&&&&&case&1:&&&&&&&&&&&&&self.myView.color&=&[UIColor&blackColor];&&&&&&&&&&&&&break;&&&&&&&&&case&2:&&&&&&&&&&&&&self.myView.color&=&[UIColor&greenColor];&&&&&&&&&&&&&break;&&&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&&break;&&&&&}&&&&&&}&
3. undo和redo操作
&-&(IBAction)tapBack:(id)sender&{&&&&&[self.myView&backImage];&}&&&&-&(IBAction)tapGo:(id)sender&{&&&&&[self.myView&forwardImage];&}&
4. 保存操作,也许下面的保存操作在处理方式上略显笨拙,如有更好的解决方案请留言。 保存的时候我是先截了个屏,然后把白板进行切割,把切割后图片存入到相册中,代码如下:
&-&(IBAction)tapSave:(id)sender&{&&&&&&&&&&UIGraphicsBeginImageContext(self.view.bounds.size);&&&&&[self.view.layer&renderInContext:UIGraphicsGetCurrentContext()];&&&&&UIImage&*uiImage&=&UIGraphicsGetImageFromCurrentImageContext();&&&&&UIGraphicsEndImageContext();&&&&&&&&&&&&&&&&&&&&CGImageRef&sourceImageRef&=&[uiImage&CGImage];&&&&&CGImageRef&newImageRef&=&CGImageCreateWithImageInRect(sourceImageRef,&CGRectMake(36,&6,&249,&352));&&&&&UIImage&*newImage&=&[UIImage&imageWithCGImage:newImageRef];&&&&&&&&&&&&&&&UIImageWriteToSavedPhotosAlbum(newImage&,&nil,&nil,&nil);&&&&&&&&&&&&&&&UIAlertView&*alert&=&[[UIAlertView&alloc]&initWithTitle:@"存储照片成功"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&message:@"您已将照片存储于图片库中,打开照片程序即可查看。"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&delegate:self&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&cancelButtonTitle:@"OK"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&otherButtonTitles:nil];&&&&&[alert&show];&&}&
以上就是本画图板的主要代码了,有不足之处还望批评指正。转载请注明出处。在本文结束时在来几张截图吧(demo下载地址:/LTQ8):
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量8223点击量6169点击量5453点击量5342点击量4849点击量2993点击量2653点击量2508点击量2190
&2016 Chukong Technologies,Inc.
京公网安备89[ios]我如何做进一步设置动画贝塞尔曲线上的图像
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.
也许有人能告诉我正确的方向。尝试了谷歌,但不知道如何在搜索条件中定义我的问题。
我动画图像沿 UIBezierPath 像这样:
UIBezierPath *trackPath = [UIBezierPath bezierPath];
[trackPath moveToPoint:P(8, 250)];
[trackPath addCurveToPoint:P(318, 250)
controlPoint1:P(156, 86)
controlPoint2:P(166, 412)];
[trackPath addCurveToPoint:P(652, 254)
controlPoint1:P(488, 86)
controlPoint2:P(512, 412)];
[trackPath addCurveToPoint:P(992, 254)
controlPoint1:P(818, 96)
controlPoint2:P(872, 412)];
CALayer *bee = [CALayer layer];
bee.bounds = CGRectMake(0, 0, 69, 71);
bee.position = P(160, 25);
bee.contents = (id)([UIImage imageNamed:@"bee3.png"].CGImage);
[self.view.layer addSublayer:bee];
想做什么是动画图像本身作为它沿路径移动。我可以对图像进行动画处理时其 b 本身与像这样:
NSArray *blueArray = [NSArray array];
blueArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:@"blue1.png"],
[UIImage imageNamed:@"blue2.png"], nil];
blueFly.animationImages = blueA
blueFly.animationDuration = 0.20;
blueFly.animationRepeatCount = -1;
[blueFly startAnimating];
怎么这么说结合两个?
另外我知道如何绘制路径本身像这样显示在屏幕上:
CAShapeLayer *beeline = [CAShapeLayer layer];
beeline.path = trackPath.CGP
beeline.strokeColor = [UIColor whiteColor].CGC
beeline.fillColor = [UIColor clearColor].CGC
beeline.lineWidth = 2.0;
beeline.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:6],
[NSNumber numberWithInt:2], nil];
[self.view.layer addSublayer:beeline];
但如何绘制线只后的图像作为其正在进行动画处理沿路径吗?
解决方法 1:
您可以使用 CAShapeLayer 将显示的路径。形状图层提供两个属性 strokeStart 和 strokeEnd 具有值之间的 0 和 1 ,并确定是可见的路径的小数部分。通过动画 strokeEnd 你可以创建该路径是""在屏幕上绘制的印象。请参阅的示例,动画绘制一条线的那支钢笔。
为了改变形象的蜜蜂可以简单地添加动画来更改您蜜蜂层的内容。例如,若要更改图像的笔在 CAShapeLayer 以上的项目,我们可以添加下面的代码向 startAnimation 方法。
CAKeyframeAnimation *imageAnimation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
imageAnimation.duration = 1.0;
imageAnimation.repeatCount = HUGE_VALF;
imageAnimation.calculationMode = kCAAnimationD
imageAnimation.values = [NSArray arrayWithObjects:
(id)[UIImage imageNamed:@"noun_project_347_1.png"].CGImage,
(id)[UIImage imageNamed:@"noun_project_347_2.png"].CGImage,
[self.penLayer addAnimation:imageAnimation forKey:@"contents"];
这只是添加到一个关键动画 contents 的图层的显示钢笔的属性。请注意,图像 "noun_project_347_1.png" 不是项目的一部分,所以你要添加一个图像以查看效果。我有只是水平翻转的图像的附带了该项目。通过设置 calculationMode 到 kCAAnimationDiscrete 动画不插入由替换一个由其他两个图像之间。Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation
Property Animation中最重要,最基础的一个类就是ValueAnimator了。Property Animation利用ValueAnimator来跟踪记录对象属性已经变化了多长时间及当前这个时间点的&#20540;。
而在ValueAnimator中,又封装了两个类:
1)TimeInterpolator,也称插&#20540;器,是来计算当前动画运动的一个跟时间有关系的比例因子。
2)TypeEvaluator,这个就是利用TimeInterpolator计算出来的因子来算出当前动画运行到的位置。
这样讲太抽象了,我们还是先用自然语言来描述一下整个动画的过程吧。
动画的原理,其实就是一帧帧的画面顺着时间顺序,在我们&#30524;中形成视觉残留的效果。所以在动画中,时间的概念是很重要的,只有时间的变化,才能形成动画效果。
0)动画准备开始,我们在这里设置了一个动画的时长(duration),如果不设置的话,动画的时长就是300毫秒,每个画面显示的时间是10ms。同时也设置了某个属性&#20540;在这个时间段中变化的起始&#20540;start和结束&#20540;end,意思就是说,在duration时间中,属性&#20540;要从start 变化到 end。
1)动画开始了,过了 t 时间,ValueAnimator会根据 t / duration 算出一个时间消逝的比例因子(elapsed fraction),意思就是说,现在时间到 t了,我们假设总的时间的duration就是3t吧,那就是现在已经过了1/3时间了,那这个属性&#20540;也应该要变化到1/3了。
2)动画继续,现在到了2t了,那么现在动画时间已经过了2/3了,那么这个属性&#20540;是不是已经变化到2/3了呢。
3)现在到了3t了,动画结束了,属性&#20540;就已经从start变成end&#20540;了。
那么现在问题来了,如果都是这样算的话,那动画不就一直是很匀速的了吗?是的,如果用的是LinearInterpolator的话。
TimeInterpolator
TimeInterpolator就是用来改变我们这个动画速度的这样一个类了。为什么叫插&#20540;器呢?我理解就是,本来动画踩着时间点,一步一步走的挺好的,它硬生生在中间的插了些&#20540;进去,或者抽了一些&#20540;出去,让整条路变得不好走了,前面变然变上坡了,走起来就慢了,本来过去 t 时间之后,动画的画面也应该在1/3的位置了,但是路不好走,它就走不到1/3,而可能只走了1/4了,而后面是下坡,一激动,步伐就快了许多,又赶上去了,但是不管中间的路怎么变化,时间点一到,一定是刚刚好落在最终的位置上的。
中提供的Interpolator主要有九个:
1)AccelerateDecelerateInterpolator:先加速再减速。
2)AccelerateInterpolator:一直加速。
3)AnticipateInterpolator:先往后一下,再嗖的一声一往无前。
4)AnticipateOvershootInterpolator:先往后一下,再一直往前超过终点,再往回收一下。
5)BounceInterpolator:最后像个小球弹几下。
6)CycleInterpolator:重复几次,感觉就是环形进度条那种,具体我还没试过。
7)DecelerateInterpolator:一直减速。
8)LinearInterpolator:线性,这个就是我们上面讲到的很均匀的了。
9)OvershootInterpolator:到了终点之后,超过一点,再往回走。有个参数可以定义,超过的力度。
这些Interpolator都是实现了TimeInterpolator接口的类,它们只需要实现一个方法:getInterpolation (float input),将这个input根据自己的需求重新计算这个比例
第一步:当到了某时间t之后,ValueAnimator会算出某个比例 fraction = t / duration,而Interpolator会接收这个比例fraction,再调用其getInterpolation方法将这个比例因子重新计算一下,返回一个新的比例因子,比如LinearInterpolator实现的方法就是什么都不变,如下:
public float getInterpolation(float input) {
}而 AccelerateDecelerateInterpolator 则会利用余弦函数的对称性变化计算这个比例因子,如下:
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
如上所述,通过第一步 Interpolator 的插&#20540;,我们会得到一个比例因子,接下来就是要用到我们的TypeEvaluator了。
TypeEvaluator
第二步:TypeEvaluator会接受第一步中算出来的比例因子,然后算出当前的属性的&#20540;,将其返回给ValuaAnimator,由ValueAnimator去设置对应属性的&#20540;。
比如,我自己写了一个BezierTypeEvaluator,根据时间的变化来让一个按钮沿着贝塞尔曲线移动,如下:
class BezierEvaluator implements TypeEvaluator{
public PointF evaluate(float fraction, PointF startValue,
PointF endValue) {
final float t =
float oneMinusT = 1.0f -
PointF point = new PointF();
PointF point0 = (PointF)startV
PointF point1 = new PointF();
point1.set(width, 0);
PointF point2 = new PointF();
point2.set(0, height);
PointF point3 = (PointF)endV
point.x = oneMinusT * oneMinusT * oneMinusT * (point0.x)
+ 3 * oneMinusT * oneMinusT * t * (point1.x)
+ 3 * oneMinusT * t * t * (point2.x)
+ t * t * t * (point3.x);
point.y = oneMinusT * oneMinusT * oneMinusT * (point0.y)
+ 3 * oneMinusT * oneMinusT * t * (point1.y)
+ 3 * oneMinusT * t * t * (point2.y)
+ t * t * t * (point3.y);
自定义TypeEvaluator,我们必须实现其evaluate方法,目的就是计算出目前的对象对应属性的&#20540;,而它会接收三个参数,一个是上文中通过interpolator算出的比例,还有我们在创建动画时设置的起始&#20540;和结束&#20540;。
ValueAnimator.AnimatorUpdateListener
既然我们已经算出了在 t 时刻,对象的某个属性的&#20540;,那么我们要把这个&#20540;重新设置到对象中,才能够起作用啊。所以ValueAnimator也提供了一个内部的Listener接口,其只有一个方法,就是获取TypeEvaluator计算出来的&#20540;,并设置给对应的属性,比如我们Demo中的代码:
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF)animation.getAnimatedValue();
button.setX(pointF.x);
button.setY(pointF.y);
我们在这里改变Button的X坐标和Y坐标,从而改变其具体的位置。至于validate,然后引起重新绘制的过程,对于这些基本的属性,ValueAnimator已经帮我们实现了。
下面,我们看看效果图,然后再总结一下ValueAnimator的实现机制。
系统定义的对象的某个属性没有对应的get/set方法的时候,那我们怎么办呢?
1)最简单的,就是去添加get/set方法,如果我们有权限的话,但很多时候我们没有。
2)给它添加一个包装类,引入一个get/set方法去设置本来的属性,来间接改变其&#20540;。
3)就是利用ValueAnimator,自己去实现其变动的逻辑了。
源代码下载
嗯,这一篇文章大概就是这样了,大家如果有兴趣了解Property Animation的应用的话,可以看一下
Android动画学习Demo(2) 关于Property Animation的用法及总结
最后还是要提醒一下,Property Animation是3.0以后才支持的,如果大家想在3.0之前去应用这些属性的话,可以去下载jake wharton的nineoldandroids包,基本上都可以直接将方法套上,不过据我实验,还是有某些方法,比如 PropertyValuesHolder就会有些bug出现的。我把这个包也放在这里吧,
点击NineoldAndroids下载
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'iOS / iPhone / iPad 实现贝塞尔曲线的绘,可以拖动端点来改变曲线形状 代码 贝塞尔曲线 绘图(Drawing)开源代码 -
贝塞尔曲线
已有 45 人收藏
&&&&实现贝塞尔曲线的绘制。在屏幕点击四个点,绘制出贝塞尔曲线。可以拖动四个点来控制贝塞尔曲线的形状。
&&&&小编注:有个小bug,就是曲线的路径与最后的一个点视图的关联不好,会出现“脱节”的现象。感谢开发者@鸟儿追 发布代码于。
测试环境:
&&[Code4App]编译测试
代码评论:
登录后方可评论
我想增加删除重画的功能,我实现了将点的视图删掉了,请教怎样可以重画?
登录后方可评论
两点小瑕疵,点数没有自增,所以都是显示为0,路径与最后一点的连接不大好,修改ti可以修正,或者可以直接用UIBezierPath绘制
登录后方可评论
-控件分类-
-功能分类-}

我要回帖

更多关于 ios贝塞尔曲线画圆 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信