6UIView子类与UIScrollView

UIView子类和UIScrollView
视图与视图层次结构
UIWindow也是UIView的子类。任何一个应用都有且只有一个UIWindow对象,UIWindow对象就像一个容器,负责包含应用中的所有视图
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
加入窗口的视图会成为窗口的子视图。窗口的子视图还可以有自己的子视图,从而构成一个UIView对象的层次结构。
应用只会显示加入这个层次结构的视图。
应用会在处理完当前事件后重画屏幕,应用在重画屏幕时,UIWindow对象会先将其图片画在屏幕上。接着,UIWindow对象的所有子视图会将各自的图片画在屏幕上。然后这些子视图的所有子视图会画出各自的图片。
创建用户界面的过程可以总结为:为每个视图创建相应的图片,然后将这些图片加入视图层次结构。Apple提供的视图类(例如:UIButton、MKMapView和UITextField)已经实现了绘图功能,会创建相应的图片。

创建自定义视图Creating a custom view
每一个UIView对象都有一个frame属性。frame属性保存的是某个视图的大小,以及相对于父视图的位置。frame属性的类型是CGRect结构,该结构包括另两个结构:origin和size,origin的类型是CGPoint结构,该结构包含两个float类型的成员:x和y。size的类型是CGSize结构,该结构也包含两个float类型的成员:width和height(结构不是oc对象,不能向结构发送消息,也不能声明为指针)

UIView的drawRect:方法
负责绘制视图的方法是UIView的drawRect:。默认情况下,drawRect:不会做任何事情。UIView的子类可以覆盖drawRect:,完成自定义的绘图任务。所用到的绘图方法和函数都源自Core Graphics框架。
实现drawRect:时:
第一件事是获取一个绘图上下文drawing context。
作用:绘图上下文用于维护各种绘图状态(例如:当前的绘图颜色、线条的粗细),并执行绘图操作。
执行绘图操作时会用到当前的绘图状态。当应用执行完某个视图的drawRect:方法后,这个绘图上下文创建的图片就会成为该视图的图片。
CGContextRef ctx = UIGraphicsGetCurrentContext();
类型CGContextRef的定义是CGContext*,即指向某个CGContext结构的指针。该结构可以代表某个绘图上下文(类型CGContextRef的类型名包含后缀Ref,这是为了帮助区分“指向C结构的指针”和“指向OC对象的指针”),ctx指向当前的绘图上下文。
任何一个视图的图片,其大小都和该视图自身的大小相同,即视图的frame属性所指定的大小。frame属性指定的是某个视图相对于其父视图的大小和位置。但从编程的角度看,只有当应用需要将某个视图画在屏幕上时,才有必要和该父视图的打交道。因此Apple为UIView声明了另一个类型为CGRect的属性,属性名是bounds。任何一个视图的bounds属性所指定的大小,都和该视图的父视图无关。

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGRect bounds = [self bounds];

    

//    根据bounds计算中心点

    CGPoint center;

    center.x = bounds.origin.x + bounds.size.width / 2.;

    center.y = bounds.origin.y + bounds.size.height / 2.;

    NSLog(@"Center:  %f, %f", center.x, center.y);

    

//    计算圆的半径,使其大小能尽可能地撑满整个视图

    float maxRadius = 0.;

    if (bounds.size.width > bounds.size.height) {        

        maxRadius = bounds.size.height / 2.;

    } else {

        maxRadius = bounds.size.width / 2.;

    }

    

//    用10点的宽度绘制所有的线条

    CGContextSetLineWidth(ctx, 10);

    

//    将线条颜色设置为浅灰

    CGContextSetRGBFillColor(ctx, 0.6, .6, .6, 1.);

    

//    将图形加入视图上下文-这个代码不会画出这个图形

//    画一个曲线原点,开始的角度,结束的角度,顺逆时针Bool

    CGContextAddArc(ctx, center.x, 0, maxRadius, 0.0, M_PI * 2., YES)

 //    执行绘图命令;根据当前的绘图状态画出上下文中的图形

//    画完图后,必须:

//    先用CGContextStrokePath来描线,即形状

//    后用CGContextFillPath来填充形状内的颜色.

    CGContextStrokePath(ctx);

//    CGContextFillPath(ctx);

}

视图的背景色和drawRect:无关。通常情况下,可以将视图的背景色设置为clearColor,从而只显示drawRect:的绘图结果,通常情况下,bounds的origin会是(0,0),bounds的size会和frame的size相同,但是也有例外,所以在绘图时,切忌使用常量,而应该根据bounds的origin和size,设置绘图内容的坐标。

Core Graphics

带有CG前缀的函数和类型均源自Core Graphics框架。该框架是一套提供绘图功能的C语言API。该框架的枢纽时CGContext结构(通常会在代码中使用指向CGContext结构的指针CGContextRef):Core Graphics中的所有函数和类型,或多或少都会用到CGContext结构,并最终由上下文创建视图的图片。

CGContextAddArc的作用是将某个路径加入上下文。路径是一组点,这些点合在一起可以定义一个图形。路径可以定义任何形状的图形,例如正方形、圆形或者手绘图形。

将路径加入上下文后,还需要执行绘图操作,才能将相应的图形画出来。core Graphic提供了三个函数,可以分别用来执行不同类型的绘图操作:

CGContextStrokePath可以沿着路径画出一根线条

CGContextFillPath可以根据路径填充一个封闭的图形

CGContextClip可以定义一个裁剪区域。定义裁剪区域后,无论是哪种绘图操作,Core Graphic都只会保留裁剪区域内的绘图结果。

UIKit中的绘图扩展UIKit Drawing Additions

在Foundation框架和UIKit框架中,有很多类可以配合上下文(CGContext结构)一起工作例如:

UIColor类,可以用[[UIColor redColor]setStroke]来代替CGContextSetRGBStrokeColor函数

NSString类也提供了针对上下文的绘图功能。向NSString对象发送drawInRect:withFont:消息,可以在当前上下文的特定矩形区域中,根据指定的字体绘制当前对象所包含的字符串。

//    创建NSString对象

    NSString *string = @"You are getting sleepy";

//    获取用于绘制字符串的字体

    UIFont *font = [UIFont systemFontOfSize:16];

//    根据之前获取的字体,计算绘制字符串所需的矩形大小、

    CGRect textRect;

    textRect.size = [string sizeWithAttributes:@{NSFontAttributeName : font}];

//    要将字符串绘制在视图正中

    textRect.origin.x = center.x - textRect.size.width / 2.;

    textRect.origin.y = center.y - textRect.size.height / 2.;

//    将当前上下文的填充设置为黑色

    [[UIColor blackColor] setFill];

//    根据字符串的绘制位置,创建阴影的偏移量,数值为向右偏4点,向下偏3点

    CGSize offset = CGSizeMake(4, 3);

//    阴影的颜色是灰色

    CGColorRef color = [[UIColor darkGrayColor] CGColor];

//    将以上得到的结构设置为当前上下文的阴影参数,

//    后续的绘图操作都会附带阴影效果

    CGContextSetShadowWithColor(ctx, offset, 2.0, color);

//    绘制字符串

    [string drawInRect:textRect withAttributes:@{NSFontAttributeName : font}];

    UIImage *image = [UIImage imageNamed:@"C141F6123FBE0AC5711EADD908533A49"];

    [image drawInRect:CGRectMake(20, 0, 100, 100) blendMode:kCGBlendModeHardLight alpha:0.8];

}

重画视图Drawing Views
当某个UIView对象收到setNeedsDisplay消息后,就会重画其图片。当某个UIView对象的实例的数据发生变化,并且这些变化和外观有关时,就需要像该对象发送setNeedsDisplay消息。(UILabel的text发生变化时,会将自己标记为需要重画)
视图不会收到在收到setNeedsDisplay消息后立刻重画图片,而会将收到该消息的视图加入一个列表,等待更新。需要先了解运行循环run loop
iOS应用是一个成为“运行循环”的机制驱动的,这个循环会一直运行下去,直到应用终止。运行循环的工作是检查输入(轻按事件、Core Location更新、通过网络接口得到的数据等),然后为相应的事件找到合适的处理方法(例如某个对象的动作方法或委托方法),这些处理方法会调用其他方法,而这些方法会调用其他更多的方法,以此类推。只有当这些方法都执行完毕,控制权才会再次回到运行循环。只有当控制权回到运行循环,视图才会有机会执行重画工作。
运行循环等待事件-> 运行循环收到事件,调用方法-> buttonTapped(setText)-> setText设置新的字符串-> 做些其他事情-> 运行循环重画->排空NSAutoreleasePool对象-> 运行循环等待事件
当应用将控制权交回给运行循环时,运行循环会先检查是否有等待重画的视图,然后创建并设置绘图所需的上下文,最后向所有等待重画的视图(即在当前循环收到过setNeedsDisplay消息的视图)发送drawRect:消息。
当某个视图重画了其图片后,屏幕中的其他视图不需要因此执行重画操作,只需要将其现有的图片在此图像合成composite至屏幕即可。这种性能优化机制可使iOS高效的完成绘图和动画操作。

运动事件Motion Events
UIView的父类UIResponder。它的实例可以成为某个窗口的第一响应对象。
becomeFirstResponder方法会返回一个布尔值,表示收到该消息的对象是否成功成为了相应窗口的第一响应对象[view becomeFirstResponder];
大多数UIResponder对象在收到becomeFirstResponder消息后,都会返回NO。这是因为大多数的视图默认只关心和其自身有关联的事件,并且几乎总是有机会来处理这些事件,以UIButton为例,当用户单击某个UIButton对象时,无论当前的第一响应者对象是哪个视图,该对象都会接收到指定的动作消息。因此只有在某个UIResponder对象明确地“表示”自己愿意成为第一响应者对象。在View .m文件中覆盖UIResponder的canBecomeFirstResponder方法,返回YES。
此外,UIResponder也实现了多个方法,专门接收事件。UIResponder的子类必须覆盖这些方法,才能使其实例响应相应的事件,以摇动事件为例,UIResponder子类必须为此覆盖一组称为运动事件的方法motion event method.在事件中改变circleColor的颜色,摇晃事件发生后,颜色不会改变,因为要进行重画setNeedsDisplay。对于参数UIEventSubtype保存的是触发当前方法的运动事件类型,所以为了应对可能发生的变化,要在代码中加入判断motion==UIEventSubtypeMotionShake(摇动)。
  1 //
  2 //  MyView.m
  3 //  MyView
  4 //
  5 //  Created by 58 on 15/9/17.
  6 //  Copyright © 2015年 58. All rights reserved.
  7 //
  8 
  9 #import "MyView.h"
 10 
 11 @implementation MyView
 12 
 13 /*
 14 // Only override drawRect: if you perform custom drawing.
 15 // An empty implementation adversely affects performance during animation.
 16 - (void)drawRect:(CGRect)rect {
 17     // Drawing code
 18 }
 19 */
 20 - (instancetype)init{
 21     self = [super init];
 22     if (self) {
 23          [self setBackgroundColor:[UIColor clearColor]];
 24         [self setCircleColor:[UIColor lightGrayColor]];
 25     }
 26     return self;
 27 }
 28 - (void)drawRect:(CGRect)rect {
 29     CGContextRef ctx = UIGraphicsGetCurrentContext();
 30     CGRect bounds = [self bounds];
 31     
 32 //    根据bounds计算中心点
 33     CGPoint center;
 34     center.x = bounds.origin.x + bounds.size.width / 2.;
 35     center.y = bounds.origin.y + bounds.size.height / 2.;
 36     NSLog(@"Center:  %f, %f", center.x, center.y);
 37     
 38 //    计算圆的半径,使其大小能尽可能地撑满整个视图
 39     float maxRadius = 0.;
 40     if (bounds.size.width > bounds.size.height) {
 41         
 42         maxRadius = bounds.size.height / 2.;
 43     } else {
 44         maxRadius = bounds.size.width / 2.;
 45     }
 46     
 47 //    用10点的宽度绘制所有的线条
 48     CGContextSetLineWidth(ctx, 1);
 49     
 50 //    将线条颜色设置为浅灰
 51 //    CGContextSetRGBFillColor(ctx, 0.6, .6, 1, 1.);
 52 //    [[UIColor redColor] setStroke];
 53     [[self circleColor]setStroke];
 54     
 55 //    将图形加入视图上下文-这个代码不会画出这个图形
 56 //    画一个曲线原点,开始的角度,结束的角度,顺逆时针Bool
 57 //    CGContextAddArc(ctx, center.x, center.y, maxRadius / 2, 0.0, M_PI * 2., YES);
 58 //  CGContextAddArc(ctx, center.x, center.y, 2, 0.0, M_PI * 2.0, YES);
 59     
 60 //    执行绘图命令;根据当前的绘图状态画出上下文中的图形
 61 //    画完图后,必须
 62 //    先用CGContextStrokePath来描线,即形状
 63 //    后用CGContextFillPath来填充形状内的颜色.
 64     for (float currentRadius = maxRadius; currentRadius > 0 ; currentRadius  -= 20) {
 65         CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2.0, YES);
 66 //        执行绘图操作;移除路径
 67         CGContextStrokePath(ctx);
 68         NSLog(@"%f", currentRadius);
 69     }
 70 //    CGContextStrokePath(ctx);
 71 //    CGContextFillPath(ctx);
 72     
 73 //    创建NSString对象
 74     NSString *string = @"You are getting sleepy";
 75 //    获取用于绘制字符串的字体
 76     UIFont *font = [UIFont systemFontOfSize:16];
 77 //    根据之前获取的字体,计算绘制字符串所需的矩形大小、
 78     CGRect textRect;
 79     textRect.size = [string sizeWithAttributes:@{NSFontAttributeName : font}];
 80 //    要将字符串绘制在视图正中
 81     textRect.origin.x = center.x - textRect.size.width / 2.;
 82     textRect.origin.y = center.y - textRect.size.height / 2.;
 83 //    将当前上下文的填充设置为黑色
 84     [[UIColor blackColor] setFill];
 85 //    根据字符串的绘制位置,创建阴影的偏移量,数值为向右偏4点,向下偏3点
 86     CGSize offset = CGSizeMake(43);
 87 //    阴影的颜色是灰色
 88     CGColorRef color = [[UIColor darkGrayColor] CGColor];
 89 //    将以上得到的结构设置为当前上下文的阴影参数,
 90 //    后续的绘图操作都会附带阴影效果
 91     CGContextSetShadowWithColor(ctx, offset, 2.0, color);
 92 //    绘制字符串
 93     [string drawInRect:textRect withAttributes:@{NSFontAttributeName : font}];
 94     
 95     UIImage *image = [UIImage imageNamed:@"C141F6123FBE0AC5711EADD908533A49"];
 96     [image drawInRect:CGRectMake(200100100) blendMode:kCGBlendModeDarken alpha:0.8];
 97 }
 98 
 99 - (BOOL)canBecomeFirstResponder {
100     return YES;
101 }
102 
103 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
104     if (motion == UIEventSubtypeMotionShake) {
105     NSLog(@"Device shaking");
106     [self setCircleColor:[UIColor whiteColor]];
107     [self setNeedsDisplay];
108     }
109 }
110 
111 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
112     
113 }
114 
115 //例如用户在摇动设备时接到的电话或消息
116 - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {
117     
118 }
119 

120 @end

 

使用UIScrollView 


posted @ 2015-09-17 20:08  captivity  阅读(95)  评论(0)    收藏  举报