转载自:http://www.tuicool.com/articles/JzQZbqj
图形上下文栈
遵守栈 "先进后出" 的特点,当我们在画一条线时设置了这条线的状态,当再次画第二条线的时候,这时第二条线的状态也会和第一次一样。如下图所示:
想要用最开始的的状态又要重新设置,所以这时用图形上下文栈操作起来要好很多。先发一下效果图,代码和解析后上。效果图如下图所示:
在写画线段的时候要先获取上下文,下面贴的代码块的第二句是将获取的上下文拷贝一份保存到栈中,栈中的 me 和 图形上下文的 me 不再存在关联。
CGContextRef me = UIGraphicsGetCurrentContext();
CGContextSaveGState(me);
在内存的效果图如下所示:
设置第一个线段的状态,线段宽度为5、颜色为绿色、首尾为圆形,并根据设置的两个坐标点将线段渲染出。内存中的效果图就是将线段的状态保存到图形上下文的 me 中,与栈中的 me 无关。代码如下:
// 设置线段状态
CGContextSetLineWidth(me,5);
[[UIColor greenColor]set];
CGContextSetLineCap(me,kCGLineCapRound);
// 设置线段位置
CGContextMoveToPoint(me, 50, 50);
CGContextAddLineToPoint(me, 100, 100);
CGContextStrokePath(me);
当过掉当前代码块中第一句的代码时,将栈中保存的 me 出栈,来替换当前图形上下文中的 me,也可以理解成清空或赋值,设置第二条线的位置并渲染,最终两条线的结果如图中 View 框中所示。
CGContextRestoreGState(me);
CGContextMoveToPoint(me, 10, 100);
CGContextAddLineToPoint(me, 150, 150);
CGContextStrokePath(me);
内存中的效果图如下:
裁剪
将图片在制定反范围内,裁剪出相应的尺寸大小。
用图形来模拟一下效果如下图所示:
代码:
CGContextRef me = UIGraphicsGetCurrentContext(); // 获取上下文
CGContextAddEllipseInRect(me, CGRectMake(50, 50, 100, 100)); // 画园
CGContextClip(me); // 裁剪
CGContextFillPath(me); // 渲染
UIImage *meImage = [UIImage imageNamed:@"meImage"]; // 定义并初始化
[meImage drawAtPoint:CGPointMake(50, 50)]; // 展示的位置
原图和裁剪后的效果图如下所示:
重绘(刷帧)
- (void)drawRect:(CGRect)rect
方法只能系统调用一次,手动不能调用,所以想要重新在绘制图形就需要在进行系统的调用,需要调用系统的 [self setNeedsDisplay];
方法来实现。
新建一个圆类,设置圆属性半径 radius ,并且在 - (void)drawRect:
方法中画一个圆,根据半径 self.radius
的值来改变圆的大小。代码如下:
@property(nonatomic,assign) CGFloat radius; // 圆的半径
- (void)drawRect:(CGRect)rect
{
CGContextRef arc = UIGraphicsGetCurrentContext();
CGContextAddArc(arc, 100, 100, self.radius, 0, M_PI * 2, 0);
[[UIColor orangeColor]set];
CGContextFillPath(arc);
}
将图中蓝色的 View 拖到 控制器中,代码如下:
@property (weak, nonatomic) IBOutlet SZXdraw *circleView;
并且将图中滑块进行监听,通过滑动滑块获取的值,来赋值给圆的半径,从而来进行重绘。 代码和示意图如下:
- (IBAction)Slide:(UISlider *)sender
{
self.circleView.radius = sender.value;
}
通过重写半径的set方法给予赋值和重绘,代码如下:
-(void)setRadius:(CGFloat)radius
{
_radius = radius;
[self setNeedsDisplay];
}
来看一下最终的效果动态图:
刷帧多用于做动画特效,下面也是一个简单的小例子,设置一个下落的Y值属性 catY 用来累计下落的大小。通过在 - (void)drawRect
方法中设置动画,在根据 View 的尺寸来判断猫头是否越界,如果越界从头开始。
- (void)drawRect:(CGRect)rect
{
self.catY += 1;
if (self.catY >= rect.size.height)
{
self.catY = -50;
}
UIImage *me = [UIImage imageNamed:@"me"];
[me drawAtPoint:CGPointMake(0, self.catY)];
}
在 - (void)awakeFromNib
系统方法中写,CADisplayLink刷帧,每秒刷新60次,此时调用 setNeedsDisplay
系统方法,将 link 加入到消息循环中。
- (void)awakeFromNib
{
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];
[link addToRunLoop:[NSRunLoop mainRunLoop]forMode:NSDefaultRunLoopMode];
}
效果示意图: