UIView

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》第四章的总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • // //UIView.h //UIKit // //Copyright (c) 2005-2015 Apple ...
    李某lkb阅读 5,734评论 0 0
  • ViewsBecause view objects are the main way your applicati...
    梁光飞阅读 3,875评论 0 0
  • 本文为大地瓜原创,欢迎知识共享,转载请注明出处。虽然你不注明出处我也没什么精力和你计较。作者微信号:christg...
    大地瓜123阅读 1,813评论 0 0
  • 文/晨烽 一二线城市到底有多难生存,包租婆会告诉你。 在外走南闯北,说是去看看,实际是无处可去。 虽然辛苦也不落魄...
    晨烽视野阅读 2,707评论 2 0
  • 一 酒馆之外灯笼似火连成一片,好像要将银装素裹的大地烧起来一样。 酒馆之内一人正在自饮自酌停不下来...
    sakuracat阅读 3,604评论 1 2