superview subview
Every instance of UIView has a superview property. When you add a view as a subview of another view, the inverse relationship is automatically established. In this case, the BNRHypnosisView’s superview is the UIWindow. (To avoid a strong reference cycle, the superview property is a weak reference.)
视图绘制
视图绘制过程:
- Each view in the hierarchy, including the window, draws itself. It renders itself to its layer, which is an instance of
CALayer
. - The layers of all the views are composited together on the screen.
视图树中的每个视图绘制自身到其图层(Layer)上,然后将所有的图层组合起来。
bounds & frame
UIView
有两个property frame bounds
, 都是距形,可以用CGRect
来表示。
Each view has a coordinate system that it uses when drawing itself. The bounds is a view’s rectangle in its own coordinate system. The frame is the same rectangle in its superview’s coordinate system.
- 视图的frame,是针对其父元素的坐标
- 视图的bounds,是针对其本身的坐标
CGRect
A structure is not an Objective-C object, so you cannot send messages to a CGRect.
A CGRectis small compared to most objects, so instead of passing a pointer to it, you just pass the entire structure.
- CGRect是个C struct,不是OC对象;
- CGRect不大,可以直接放到stack中,所以不需要用指针;
#import "BKAppDelegate.h"
#import "BKHypnosisView.h"
@implementation BKAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
// 将BKHypnosisView设置为全屏,即window的bounds
CGRect firstFrame = self.window.bounds;
BKHypnosisView *firstView = [[BKHypnosisView alloc] initWithFrame:firstFrame];
[self.window addSubview:firstView];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
@end
#import "BKHypnosisView.h"
@implementation BKHypnosisView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
}
return self;
}
// 执行自定义的绘制动作
- (void)drawRect:(CGRect)rect
{
// 找到屏幕中心,self是当前view,由于当前view设置了全屏,可以直接用当前view的bounds
CGRect bounds = self.bounds;
CGPoint center;
center.x = bounds.origin.x + bounds.size.width / 2;
center.y = bounds.origin.y + bounds.size.height / 2;
// 屏幕宽或高的最小值的一半
float maxRadius = MIN(bounds.size.width, bounds.size.height) / 2;
// 屏幕对角线长度的一半
// float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2;
UIBezierPath *path = [[UIBezierPath alloc] init];
for (float currentRadius=maxRadius; currentRadius>0; currentRadius-=20) {
// 移动画笔
[path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];
// 画圆
[path addArcWithCenter:center
radius:currentRadius
startAngle:0.0
endAngle:M_PI * 2.0
clockwise:YES];
}
// 设置画笔的宽度
path.lineWidth=10;
// 设置pen的颜色
[[UIColor purpleColor] setStroke];
// 开始绘图
[path stroke];
CGRect logoFrame = CGRectMake(center.x/2, center.y/2, bounds.size.width/2, bounds.size.height/2);
UIImage *logoImage = [UIImage imageNamed:@"logo.png"];
// 将图片绘制到指定位置
[logoImage drawInRect:logoFrame];
}
@end
Core Graphic framework
所有以CG开头的类名,都是其缩写。Core Graphic 是用C写的2D绘图框架。
在OC中,OC对象调用Core Graphic framework来绘图,像上面代码中的 UIBezierPath UIImage
等。
Core Graphic 中一个最重要的对象是graphics context,一个 CGContextRef
实例。
Right before drawRect: is sent to an instance of UIView, the system creates a CGContextRef for that view’s layer. The layer has the same bounds as the view and some default values for its drawing state. As drawing operations are sent to the context, the pixels in the layer are changed. After drawRect: completes, the system grabs the layer and composites it to the screen.
The Core Graphics functions that operate on the context, like CGContextSetRGBStrokeColor, take a pointer to context that they will modify as their first argument. You can grab a pointer to the current context in drawRect: by calling the function UIGraphicsGetCurrentContext. The current context is an application-wide pointer that is set to point to the context created for a view right before that view is sent drawRect:.
if you create a Core Graphics object with a function that has the word Create or Copy in it, you must call the matching Release function and pass a pointer to the object as the first argument.
下面是直接用Core Graphic来写drawRect方法:
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(currentContext, 1, 0, 0, 1);
CGMutablePathRef path = CGPathCreateMutable(); //有create 或 copy关键字,需要手动释放内存
CGPathMoveToPoint(path, NULL, a.x, a.y);
CGPathAddLineToPoint(path, NULL, b.x, b.y);
CGContextAddPath(currentContext, path);
CGContextStrokePath(currentContext);
CGPathRelease(path); //调用对应的release方法释放内存
CGContextSetStrokeColorWithColor(currentContext, color);
}
可以看到CG类的调用稍显复杂,通常使用OC的对象来画图,只有当OC无法实现时(比如画渐变-gradient),才需要写Core Graphic。
本文是对《iOS Programming The Big Nerd Ranch Guide 4th Edition》第四章的总结。