Transcript
Page 1: 06 Subclassing UIView and UIScrollView

⼦子类化 UIView 和 UIScrollView

范圣刚,[email protected],www.tfan.org

Page 2: 06 Subclassing UIView and UIScrollView

什么是 view?•我们在前⾯面创建过 UIButton,UILabel等,但是究

竟什么是 view?•⼀一个 view 是 UIView 或者它的某⼀一⼦子类的实例

• view 知道如何把它⾃自⼰己绘制到应⽤用程序窗⼝口上(⼀一个 UIWindow 的实例)

• view 存在于 view 的层次结构中 view hierarchy。view hierarchy 的根是应⽤用程序窗⼝口

• view 要处理事件,⽐比如触控事件

Page 3: 06 Subclassing UIView and UIScrollView

⾃自定义 UIView ⼦子类•使⽤用同⼼心圆填满屏幕

•绘制⽂文本

•启⽤用滚动和放⼤大缩⼩小

Page 4: 06 Subclassing UIView and UIScrollView

Hypnosister(催眠应⽤用)•创建⼀一个新的 iOS 项⺫⽬目,空项⺫⽬目

•产品名:Hypnosister

•类前缀:Hypnosister

•勾选 “Use Automatic Reference Counting”

Page 5: 06 Subclassing UIView and UIScrollView

视图和视图层次结构

Page 6: 06 Subclassing UIView and UIScrollView

•视图构成了应⽤用的⽤用户界⾯面

•每个视图维护⼀一个⽤用于表⽰示它的图像,例如 UIButton 的图像就是⼀一个在中间带有标题的圆⾓角矩形;⼀一个 UILabel 的图像就是简单的⽂文本

•当关于 view 的⼀一些东⻄西变化时,例如 UILabel 的 text 属性或 UIButton 的 title,view 的图像被重绘以便这些变更能够在屏幕上变得可⻅见

Page 7: 06 Subclassing UIView and UIScrollView

UIWindow

•UIWindow 是 UIView 的⼦子类

•每个应⽤用程序恰好有⼀一个 UIWindow 的实例作为在应⽤用中所有视图的容器,当应⽤用启动的时候窗⼝口被创建

•HypnosisterAppDelegate.m 中的 application:didFinishLaunchingWithOptions: ⽅方法中创建 UIWindow 对象并发送消息 makeKeyAndVisible

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 8: 06 Subclassing UIView and UIScrollView

subview

•window 被放到屏幕上以后,就可以在它上⾯面增加其他 view,这样的 view 就叫做 window 的 subview

•作为 window 的 subview 的 view 也可以具有 subviews,结果就形成了⼀一个视图对象的层次结构

•当且仅当⼀一个视图被添加到这个层次结构以后才会在屏幕上显⽰示,⽆无论它是作为 window 的⼀一个 subview,还是作为另外⼀一个已经添加到 window 的 view 的 subview

•因此,window 是 view hierarchy 的 root

Page 9: 06 Subclassing UIView and UIScrollView

redrawn(重绘)•当屏幕被重绘的时候,⾸首先是 window 的图像被

绘制到屏幕上

•然后,window 的所有 subview 把它们⾃自⼰己的图像绘制到屏幕上

•接着,subviews 的 subviews 再绘制它们的图像,依此类推

Page 10: 06 Subclassing UIView and UIScrollView

绘制 view hierarchy 到屏幕

Page 11: 06 Subclassing UIView and UIScrollView

⽤用户界⾯面⽣生成•创建每⼀一个 view 的图像并且把每⼀一个 view 加到

view hierarchy

•类似 UIButton,MKMapView 以及 UITextField 这些(Apple本⾝身提供的)已经知道它们的图像看起来是什么样⼦子

•另外⼀一种情况是我们需要创建⼀一个⾃自定义的视图对象并且编写代码来创建它的图像

Page 12: 06 Subclassing UIView and UIScrollView

创建⼀一个⾃自定义视图

Page 13: 06 Subclassing UIView and UIScrollView

•⼦子类化 UIView,并且⾃自定义⼦子类的图像

•创建⼀一个类 HypnosisView, 从 UIView 继承

•在 HypnosisterAppDelegate.m 中引⼊入 HypnosisView.h 头⽂文件

•在 application:didFinishLaunchingWithOptions: 中创建 HypnosisView 的实例并把它作为 window 的 subview 添加到 view hierarchy 中

Page 14: 06 Subclassing UIView and UIScrollView

创建 HypnosisView 的实例并添加到UIWindow

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; CGRect viewFrame = CGRectMake(160, 240, 100, 150); HypnosisView *view = [[HypnosisView alloc] initWithFrame:viewFrame]; [view setBackgroundColor:[UIColor redColor]]; [[self window] addSubview:view]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 15: 06 Subclassing UIView and UIScrollView

subview 和 superview

•红⾊色的 HypnosisView 实例在⽩白⾊色的 UIWindow 之上绘制;HypnosisView 实例是 UIWindow 的 subview

•把⼀一个 view 作为另外⼀一个 view 的 subview 添加时,反向关系同时也⾃自动建⽴立了,HypnosisView 的 superview 是 UIWindow

• XIB ⽂文件和编程创建

Page 16: 06 Subclassing UIView and UIScrollView

initWithFrame:

•当编程创建⼀一个 view 时,我们使⽤用 alloc 和⼀一个 initializer message,就像我们创建任何其他对象⼀一样

•UIView 的 designated initializer,同样也是 HypnosisView的,是:initWithFrame:

• initWithFrame: 采⽤用 CGRect 结构作为参数,这个 CGRect 就是 view 的边框

Page 17: 06 Subclassing UIView and UIScrollView

view 的 frame

• 每个视图实例都有⼀一个 frame 矩形

• 视图的 frame 指定了视图的⼤大⼩小和其相对于它的 superview 的位置

• frame 由 CGRect 结构体表⽰示,并且包含成员 origin 和 size。这些成员也是结构体

• orgin 是 CGPoint 类型,包含两个 !oat 成员:x 和 y

• size 是 CGSize 类型,包含两个 !oat 成员: width 和 height

• 结构体 structure 并不是 objective-c 对象,因此不能给它们发送消息

Page 18: 06 Subclassing UIView and UIScrollView

CGRect

• view 总是⼀一个矩形

Page 19: 06 Subclassing UIView and UIScrollView

再画⼀一个 HypnosisView 作为 Window的 subview

// 再创建⼀一个 HypnosisView CGRect anotherFrame = CGRectMake(20, 30, 50, 50); HypnosisView *anotherView = [[HypnosisView alloc] initWithFrame:anotherFrame]; [anotherView setBackgroundColor:[UIColor blueColor]]; [[self window] addSubview:anotherView];

Page 20: 06 Subclassing UIView and UIScrollView

两个 HypnosisViews 都作为 window 的 subview 的 view hierarchy

Page 21: 06 Subclassing UIView and UIScrollView

将⼀一个 HypnosisView 作为另⼀一个 HypnosisView 的 subview

•⼀一个 view 的 frame 是相对于它的 superview 的,⽽而不是 window

[[self window] addSubview:anotherView];[view addSubview:anotherView];

Page 22: 06 Subclassing UIView and UIScrollView

⼀一个 HypnosisView 作为另⼀一个HypnosisView 的 subview 的 view hierarchy

Page 23: 06 Subclassing UIView and UIScrollView

drawRect: ⽅方法

Page 24: 06 Subclassing UIView and UIScrollView

•截⾄至⺫⽬目前,我们创建了⼀一个 UIView 的⼦子类,创建了两个实例,把它们插⼊入到了 view hierarchy

•我们给这两个实例不同的 backgroundColor 以便区分它们在屏幕上的位置和⼤大⼩小。组成 iOS 所有界⾯面的 view,要能够绘制更多,⽽而不仅仅是带颜⾊色的矩形

•使视图变得有趣的绘制都发⽣生在 UIView 的 drawRect: ⽅方法中

•默认情况下,drawRect: 什么都不做。UIView ⼦子类通过重写这个⽅方法来实现⾃自定义绘制。

Page 25: 06 Subclassing UIView and UIScrollView

•在我们重写 drawRect: 时,我们发出创建 UIView ⼦子类的实例图像的绘制指令,这些绘制指令都来⾃自 Core Graphics framework。

•这个框架在创建新项⺫⽬目时被⾃自动添加到 application target

Core Graphic framework

Page 26: 06 Subclassing UIView and UIScrollView

绘制上下⽂文(drawing context)•重写 drawRect: 的第⼀一步是获取 drawing context

(绘制上下⽂文)的指针

•绘制上下⽂文维护绘制的状态(例如当前绘制的颜⾊色和画笔的厚度)和执⾏行绘制操作

•绘制操作会使⽤用当前的绘制状态进⾏行绘制

•在 drawRect: 结束时,上下⽂文⽣生成的图像就变成了 view 的图像

Page 27: 06 Subclassing UIView and UIScrollView

CGContextRef

• CGContextRef 被定义为 CGContext * - 指向 CGContext 的指针

• Ref 后缀使得很容易区分指向 C 结构体的指针和指向 Objective-C 对象的指针

•这⾥里的 ctx 指向了当前的绘制上下⽂文

- (void)drawRect:(CGRect)rect{ CGContextRef ctx = UIGraphicsGetCurrentContext();}

Page 28: 06 Subclassing UIView and UIScrollView

bounds

• view 的图像和它出现在屏幕上是⼀一样⼤大⼩小,也就是说和 view 的 frame ⼤大⼩小⼀一致

• frame 描述了 view 相对于 view 的 superview 的⼤大⼩小

•UIView 的名为 bounds 的 CGRect 属性给出了视图和它的 superview ⽆无关的⼤大⼩小

•在 CGContextRef 上执⾏行的绘制操作必须落在 bounds 矩形区域内,否则会被剪切到这个矩形区域

Page 29: 06 Subclassing UIView and UIScrollView

在 bounds 矩形中⼼心绘制⼀一个圆形- (void)drawRect:(CGRect)rect{ CGContextRef ctx = UIGraphicsGetCurrentContext(); CGRect bounds = [self bounds]; // 计算出 bounds 矩形的中⼼心 CGPoint center; center.x = bounds.origin.x + bounds.size.width / 2.0; center.y = bounds.origin.y + bounds.size.height / 2.0; // 圆的半径应该是和 view 的⼤大⼩小⼏几乎⼀一样的 float maxRadius = hypot(bounds.size.width, bounds.size.height) / 4.0; // 线条宽度应该是 10pt 宽 CGContextSetLineWidth(ctx, 10); // 线条的颜⾊色应该是灰⾊色(red/green/blue = 0.6, alpha = 1.0) CGContextSetRGBStrokeColor(ctx, 0.6, 0.6, 0.6, 1.0); // 增加⼀一个矩形到上下⽂文 - 这不会真正绘制矩形 CGContextAddArc(ctx, center.x, center.y, maxRadius, 0.0, M_PI * 2.0, YES); // 执⾏行绘制指令;使⽤用当前状态绘制当前形状 CGContextStrokePath(ctx);}

Page 30: 06 Subclassing UIView and UIScrollView

在 bounds 中⼼心绘制圆形

Page 31: 06 Subclassing UIView and UIScrollView

backgroundColor

•在 HyposisterAppDelegate.m 中,移除设置 view 的背景颜⾊色的代码

•在 HypnosisView.m 中,重写 initWithFrame: 来设置每个 HyposisView 的背景颜⾊色为 clear

HypnosisView *view = [[HypnosisView alloc] initWithFrame:viewFrame]; [view setBackgroundColor:[UIColor redColor]]; [[self window] addSubview:view]; CGRect anotherFrame = CGRectMake(20, 30, 50, 50); HypnosisView *anotherView = [[HypnosisView alloc] initWithFrame:anotherFrame]; [anotherView setBackgroundColor:[UIColor blueColor]];

- (id)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { [self setBackgroundColor:[UIColor clearColor]]; } return self;}

Page 32: 06 Subclassing UIView and UIScrollView

默认填充颜⾊色 和 clearColor

Page 33: 06 Subclassing UIView and UIScrollView

Core Graphics

Page 34: 06 Subclassing UIView and UIScrollView

Core Graphics

• 以 CG 开头的函数和类型都来⾃自于 Core Graphics framework, ⼀一套⽤用于 2D 绘图的 C 语⾔言 API

• Core Graphics framework 的中⼼心是 CGContextRef: 所有其他的 Core Graphics 函数和类型都以某种⽅方式和绘制上下⽂文进⾏行交互,然后由上下⽂文来创建图像

• 前⾯面⽤用到的 Core Graphics 函数:• 使⽤用 CGContextSetLineWidth 设置绘制状态

• 使⽤用 CGContextSetRGBStrokeColor 设置描边颜⾊色

• 使⽤用 CGContextAddArc 增加⼀一个 path 到上下⽂文(Arc 只是 path 的⼀一种)

Page 35: 06 Subclassing UIView and UIScrollView

绘制操作•路径被添加到上下⽂文之后,我们就可以执⾏行⼀一个

绘制操作了。三种绘制操作:•CGContextStrokePath

• 沿着路径绘制线条(描边)

• CGContextFillPaht• 填充由路径构成的形状

• CGContextClip• 限制对由路径定义的区域的进⼀一步绘制操作

Page 36: 06 Subclassing UIView and UIScrollView

绘制⼀一系列的同⼼心圆•⼀一个绘制操作完成后,当前路径就被从上下⽂文中

移除

•这样,要绘制多于⼀一个圆形的话,就需要为每个圆形添加⼀一个路径到上下⽂文

// 圆的半径应该是和 view 的⼤大⼩小⼏几乎⼀一样的// float maxRadius = hypot(bounds.size.width, bounds.size.height) / 4.0; float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2.0; // 线条宽度应该是 10pt 宽 CGContextSetLineWidth(ctx, 10); // 线条的颜⾊色应该是灰⾊色(red/green/blue = 0.6, alpha = 1.0) CGContextSetRGBStrokeColor(ctx, 0.6, 0.6, 0.6, 1.0); for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) { CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2.0, YES); CGContextStrokePath(ctx); }

Page 37: 06 Subclassing UIView and UIScrollView

同⼼心圆绘制•再改造⼀一下,去掉后

来加的那个 HypnosisView

•把剩下的 HypnosisView 的⼤大⼩小改成跟屏幕⼀一般⼤大

Page 38: 06 Subclassing UIView and UIScrollView

修改 didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // 创建⼀一个 HypnosisView,⼤大⼩小和屏幕⼤大⼩小⼀一样⼤大 HypnosisView *view = [[HypnosisView alloc] initWithFrame:[[self window] bounds]]; [[self window] addSubview:view]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 39: 06 Subclassing UIView and UIScrollView

UIKit 绘制•有⼀一些 Foundation 或者 UIKit 的类可以和

CGContextRef ⼀一起⼯工作

Page 40: 06 Subclassing UIView and UIScrollView

UIColor

•例如:UIColor,⼀一个 UIColor 的实例表⽰示⼀一种颜⾊色,可以⽤用来设置上下⽂文当前⽤用来绘制的颜⾊色

•可以把 CGContextSetRGBStrokeColor(ctx, 0.6, 0.6, 0.6, 1.0) 替换为 [[UIColor colorWithRed: 0.6 green:0.6 blue:0.6 alpha:1] setStroke];

•UIColor 也预先准备了很多常⽤用的颜⾊色供使⽤用,可以把上⾯面的代码改成:[[UIColor lightGrayColor] setStroke];

CGContextSetRGBStrokeColor(ctx, 0.6, 0.6, 0.6, 1.0);[[UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1] setStroke];

Page 41: 06 Subclassing UIView and UIScrollView

NSString

•NSString 能够绘制到 CGContextRef

•发送 drawInRect:withFont: 消息给⼀一个 NSString 将会使⽤用给定的字体把这个字符串绘制到当前上下⽂文给定的矩形内

•在 HyposisView.m 中的 drawRect: 中代码的末尾增加⼀一段代码

NSString *text = @"你快被催眠了."; UIFont *font = [UIFont boldSystemFontOfSize:28]; CGRect textRect; textRect.size = [text sizeWithFont:font]; textRect.origin.x = center.x - textRect.size.width / 2.0; textRect.origin.y = center.y - textRect.size.height / 2.0; [[UIColor blackColor] setFill]; [text drawInRect:textRect withFont:font];

Page 42: 06 Subclassing UIView and UIScrollView

绘制⽂文本

Page 43: 06 Subclassing UIView and UIScrollView

使⽤用阴影

// 设置当前上下⽂文填充⾊色为⿊黑⾊色 [[UIColor blackColor] setFill]; // 增加阴影。阴影将会向右移动4个points,向下移动3个points CGSize offset = CGSizeMake(4, 3); // 阴影颜⾊色使⽤用深灰⾊色 CGColorRef color = [[UIColor darkGrayColor] CGColor]; // 使⽤用这些参数设置上下⽂文的阴影,后续绘制都会使⽤用阴影 (blur,模糊,2.0) CGContextSetShadowWithColor(ctx, offset, 2.0, color); // 绘制字符串 [text drawInRect:textRect withFont:font];

Page 44: 06 Subclassing UIView and UIScrollView

带阴影的⽂文字

Page 45: 06 Subclassing UIView and UIScrollView

UIImage

•有⼀一个类似的有⽤用的⽅方法 drawInRect: ⽤用于绘制⼀一个 image 对象到⼀一个上下⽂文

Page 46: 06 Subclassing UIView and UIScrollView

重绘视图

Page 47: 06 Subclassing UIView and UIScrollView

•当⼀一个 UIView 实例收到 setNeedsDisplay 消息时,会重绘它的图像

•当视图⼦子类可绘制内容变更时,会给⾃自⾝身发送 setNeedsDisplay 消息

•例如⼀一个 UILabel 当它被发送 setText: 消息时会为重新显⽰示⽽而标记⾃自⾝身(如果显⽰示的⽂文本变更的话必须要重新绘制图像)

•重绘操作不会⽴立即执⾏行,⽽而是在 run loop 中进⾏行

•重绘和组合

Page 48: 06 Subclassing UIView and UIScrollView

使⽤用 Run Loop 重绘视图

Page 49: 06 Subclassing UIView and UIScrollView

增加 circleColor 属性•HypnosisView.h 中声明 circleColor 属性

•HypnosisView.m 中 synthesize 这个属性

•更新 initWithFrame: ⽅方法创建⼀一个默认的 circleColor

•在 drawRect: 中设置上下⽂文描边颜⾊色使⽤用 circleColor ⽽而不是 light gray

Page 50: 06 Subclassing UIView and UIScrollView

增加 circleColor (代码)@interface HypnosisView : UIView@property (nonatomic, strong) UIColor *circleColor;@end

@implementation HypnosisView@synthesize circleColor;

- (id)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { [self setBackgroundColor:[UIColor clearColor]]; [self setCircleColor:[UIColor lightGrayColor]]; } return self;}

CGContextSetLineWidth(ctx, 10); [[self circleColor] setStroke]; for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) { CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2.0, YES); CGContextStrokePath(ctx); }

Page 51: 06 Subclassing UIView and UIScrollView

动作事件(Motion Events)

Page 52: 06 Subclassing UIView and UIScrollView

•UIView 的超类 UIResponder 的实例,可以在设备被晃动或者键盘上的按键被按下时成为 window 的 "rst responder 并将接收事件

•下⾯面我们把 HypnosisView 的实例作为 Hypnosister window 的 "rst responder ,晃动设备将发送消息给 HypnosisView,并且这个⽅方法将改变它的 circleColor

Page 53: 06 Subclassing UIView and UIScrollView

becomeFirstResponder

•在 HypnosisterAppDelegate.m 中告诉 HypnosisView 实例成为 "rst responder

• becomeFirstResponder ⽅方法返回⼀一个布尔值,标明接收对象是否成功的变成了 window 的 "rst responder

BOOL success = [view becomeFirstResponder]; if (success) { NSLog(@"HypnosisView 成为 first responder"); } else { NSLog(@"⽆无法成为 first responder"); }

Page 54: 06 Subclassing UIView and UIScrollView

canBecomeFirstResponder

• ⼤大多数 UIResponder 对象收到 becomeFirstResponder 时返回 NO

• 因为⼤大多数的视图,默认情况下只关⼼心和它们关联的事件,并且它们(⼏几乎)总有机会处理这些事件

• 举例来说,不管谁是 "rst responder ⼀一个被点击的 UIButton 总会被发送消息

• 所以,⼀一个 responder 对象必须显式表明它希望变成 "rst responder

• 在 HypnosisView.m 中重写UIResponder 的canBecomeFirstResponder 来返回 YES

- (BOOL)canBecomeFirstResponder{ return YES; }

Page 55: 06 Subclassing UIView and UIScrollView

动作事件⽅方法(motion event methods)

•接收事件的⽅方法也是在 UIResponder 中实现的,为了实例能够响应事件它们也必须在 UIResponder 的⼦子类中重写

•为了处理 shakes,在 UIResponder ⼦子类中重写的⽅方法被称为 motion event methods(动作事件⽅方法),其声明类似:

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event

Page 56: 06 Subclassing UIView and UIScrollView

motionBegan:withEvent:

•为了让 HypnosisView 能够知道⽤用户开始摇动设备并且采取⾏行动,必须实现 motionBegan:withEvent: ⽅方法

•在 HypnosisView.m 中重写这个⽅方法以便开始摇动的时候改变 circleColor

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{ NSLog(@"设备开始摇动"); [self setCircleColor:[UIColor redColor]];}

Page 57: 06 Subclassing UIView and UIScrollView

发送 setNeedsDisplay 消息•当 HypnosisView 的 circleColor 改变时,实例变量

circleColor 被设置指向⼀一个新的 UIColor 实例

•但是当这些发⽣生时,我们并没有告诉 HypnosisView 它需要重新绘制它的图像;我们必须在 HypnosisView 改变了它的 circleColor 后给它发送 setNeedsDisplay 消息

•在 HypnosisView.m 中,实现 setCircleColor: 以在变更 circleColor 实例变量后发送这个消息

•此时再运⾏行应⽤用,摇动设备,圆形就变成了红⾊色- (void)setCircleColor:(UIColor *)clr{ circleColor = clr; [self setNeedsDisplay];}

Page 58: 06 Subclassing UIView and UIScrollView

UIEventSubtype

•UIEventSubtype 持有触发这个⽅方法的动作事件的类型。根据类型定义,处理 shake 事件外还可能会有其他事件也触发这个⽅方法

•在 HypnosisView.m 中 增加⼀一⾏行⽤用于判断的代码:if (motion == UIEventSubtypeMotionShake)

typedef NS_ENUM(NSInteger, UIEventSubtype) { UIEventSubtypeNone = 0, UIEventSubtypeMotionShake = 1, UIEventSubtypeRemoteControlPlay = 100, UIEventSubtypeRemoteControlPause = 101, UIEventSubtypeRemoteControlStop = 102, UIEventSubtypeRemoteControlTogglePlayPause = 103, UIEventSubtypeRemoteControlNextTrack = 104, UIEventSubtypeRemoteControlPreviousTrack = 105, UIEventSubtypeRemoteControlBeginSeekingBackward = 106, UIEventSubtypeRemoteControlEndSeekingBackward = 107, UIEventSubtypeRemoteControlBeginSeekingForward = 108, UIEventSubtypeRemoteControlEndSeekingForward = 109,};

Page 59: 06 Subclassing UIView and UIScrollView

使⽤用 UIScrollView

Page 60: 06 Subclassing UIView and UIScrollView

•当我们希望能够让⽤用户滚动显⽰示我们视图的时候,我们⼀一般会使我们的视图成为 UIScrollView 的 subview

Page 61: 06 Subclassing UIView and UIScrollView

UIScrollView 对象⽰示意图

Page 62: 06 Subclassing UIView and UIScrollView

contentSize

•滚动视图⼀一般⽤用于⽐比屏幕⼤大的视图,滚动视图绘制它的 subview 的⼀一个矩形部分,在滚动视图上移动你的⼿手指或者平移时,改变的是 subview 的矩形的位置

•可以把滚动视图看作是⼀一个观察⼝口(view port),滚动视图的⼤大⼩小就是这个观察⼝口的⼤大⼩小

•能够查看的区域⼤大⼩小是 UIScrollView 的 contentSize, ⼀一般就是UIScrollView 的 subview 的⼤大⼩小

Page 63: 06 Subclassing UIView and UIScrollView

UIScrollView 及其内容区域

Page 64: 06 Subclassing UIView and UIScrollView

超⼤大号的 HypnosisView

•在 HypnosisterAppDelegate.m 中创建⼀一个超⼤大号的 HypnosisView

•把⼤大号的 HypnosisView 放到⼀一个 scroll view 中

•把 scroll view 添加到 window

Page 65: 06 Subclassing UIView and UIScrollView

超⼤大号的 HypnosisView(代码)

// 创建⼀一个和 window ⼀一样⼤大的 UIScrollView CGRect screenRect = [[self window] bounds]; UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect]; [[self window] addSubview:scrollView]; // 创建⼀一个是屏幕两倍⼤大的 HypnosisView,并添加到scroll view CGRect bigRect = screenRect; bigRect.size.width *= 2.0; bigRect.size.height *= 2.0; HypnosisView *view = [[HypnosisView alloc] initWithFrame:bigRect]; // 把 HypnosisView 作为 subview 添加到 scrollview,⽽而不是 window [scrollView addSubview:view]; // 告诉 scrollview 它的区域有多⼤大 [scrollView setContentSize:bigRect.size];

Page 66: 06 Subclassing UIView and UIScrollView

超⼤大号的 HypnosisView

Page 67: 06 Subclassing UIView and UIScrollView

平移和分⻚页(panning and paging)

•前⾯面我们⽤用 scroll view 来移动特别⼤大的 view

• scroll view 也可以⽤用来在⼀一些不同的 view 实例之间进⾏行平移

•⽐比如我们有两个屏幕⼤大⼩小的视图,⽤用户可以在它们之间平移

•我们把前⾯面例⼦子中的 HypnosisView 再缩回跟原来屏幕⼀一样⼤大⼩小,并且增加另外⼀一个同样⼤大⼩小的 HypnosisView 作为 UIScrollView 的 subview

•同样,把 contentSize 设成屏幕宽度的两倍,⾼高度⼀一样

Page 68: 06 Subclassing UIView and UIScrollView

平移(代码)

// 创建⼀一个和 window ⼀一样⼤大的 UIScrollView CGRect screenRect = [[self window] bounds]; UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect]; [[self window] addSubview:scrollView]; CGRect bigRect = screenRect; bigRect.size.width *= 2.0; // 把⾼高度改成和屏幕⼀一样// bigRect.size.height *= 2.0;// HypnosisView *view = [[HypnosisView alloc] initWithFrame:bigRect]; HypnosisView *view = [[HypnosisView alloc] initWithFrame:screenRect]; // 把 HypnosisView 作为 subview 添加到 scrollview,⽽而不是 window [scrollView addSubview:view]; // 把另⼀一个 HypnosisView 的矩形移动右边屏幕外 screenRect.origin.x = screenRect.size.width; // 创建另外⼀一个 HypnosisView HypnosisView *anotherView = [[HypnosisView alloc] initWithFrame:screenRect]; [scrollView addSubview:anotherView]; // 同样告诉 scrollview 它的区域有多⼤大 [scrollView setContentSize:bigRect.size];

Page 69: 06 Subclassing UIView and UIScrollView

在两个 HypnosisView 间平移

Page 70: 06 Subclassing UIView and UIScrollView

setPagingEnable:

• 前⾯面移动视图的时候可以停在两个 HypnosisView 之间

• 要强制滚动视图的观察⼝口捕捉这些视图之⼀一,在 HypnosisterAppDelegate.m 中为 scroll view 打开 paging(分⻚页)

• 这样再平移到两个视图中间时,就会⾃自动滚动到其中⼀一个视图

• paging 通过滚动视图的 contentSize / bounds 来分成同样⼤大⼩小的 section 进⾏行显⽰示

CGRect screenRect = [[self window] bounds];UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect];// 打开 paging[scrollView setPagingEnabled:YES];[[self window] addSubview:scrollView];

Page 71: 06 Subclassing UIView and UIScrollView

缩放(Zooming)

•UIScrollView 还以可以放⼤大或者缩⼩小其内容

•要进⾏行缩放,scroll view 需要知道 ⼩小和 ⼤大缩放级别,⽽而且需要知道要进⾏行缩放的视图

•把 HypnosisterAppDelegate.m 中的另⼀一个 HypnosisView 去掉,同时把 UIScrollView 的 contentSize 改回屏幕⼤大⼩小

•然后禁⽤用 paging,设置 zoom 属性,设置 UIScrollView 的 delegate

Page 72: 06 Subclassing UIView and UIScrollView

Zooming(代码)

CGRect screenRect = [[self window] bounds]; UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect]; // 设置缩放属性 [scrollView setMinimumZoomScale:1.0]; [scrollView setMaximumZoomScale:5.0]; // 设置 scrollView 的 delegate [scrollView setDelegate:self]; [[self window] addSubview:scrollView]; CGRect bigRect = screenRect; HypnosisView *view = [[HypnosisView alloc] initWithFrame:screenRect]; // 把 HypnosisView 作为 subview 添加到 scrollview,⽽而不是 window [scrollView addSubview:view]; // 同样告诉 scrollview 它的区域有多⼤大(现在 scroll view 是屏幕⼤大⼩小) [scrollView setContentSize:bigRect.size];

Page 73: 06 Subclassing UIView and UIScrollView

viewForZoomingInScrollView:

•构建并运⾏行,我们可以看到⼀一个 HypnosisView,但是此时我们既不能平移也不能缩放

•要实现缩放,必须在 UIScrollView 的 delegate 中实现 viewForZoomingInScrollView: ⽅方法

•这个⽅方法返回在其上进⾏行缩放的 view 的实例,这⾥里的这个 view 应该是 HypnosisView 的实例,UIScrollView 的 delegate 将是 HypnosisterAppDelegate

Page 74: 06 Subclassing UIView and UIScrollView

实现 viewForZoomingInScrollView

•在 HypnosisterAppDelegate.h 中,声明 HypnosisterAppDelegate 遵守 UIScrollViewDelegate

•在 HypnosisterAppDelegate 中把指向 HypnosisView 的本地变量改成实例变量,以便在 viewForZoomingInScrollView: 中可以访问

•实现 viewForZoomingInScroolView 来返回这个 view

Page 75: 06 Subclassing UIView and UIScrollView

实现 viewForZoomingInScrollView(代码)#import <UIKit/UIKit.h>// 导⼊入 HypnosisView 头⽂文件#import "HypnosisView.h"

@interface HypnosisterAppDelegate : UIResponder <UIApplicationDelegate, UIScrollViewDelegate>{ HypnosisView *view;}@property (strong, nonatomic) UIWindow *window;@end

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{ return view;}

// HypnosisView *view = [[HypnosisView alloc] initWithFrame:screenRect]; view = [[HypnosisView alloc] initWithFrame:screenRect];

[scrollView addSubview:view];

Page 76: 06 Subclassing UIView and UIScrollView

Zooming HypnosisView

•在模拟器上可以通过按住 Option 键来模拟两个⼿手指,然后点击并移动⿏鼠标

Page 77: 06 Subclassing UIView and UIScrollView

隐藏状态栏•在窗⼝口可⻅见前隐藏状态栏

•在 HypnosisterAppDelegate.m 中的 application:didFinishLaunchingWithOptions: 接近顶部的地⽅方添加⼀一⾏行代码

•再构建并运⾏行时,会发现状态栏在应⽤用程序启动以后会逐渐消失(fading out)

// 隐藏状态栏 status bar [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

Page 78: 06 Subclassing UIView and UIScrollView

设置属性在显⽰示前隐藏状态栏•也可以通过往应⽤用程序的信息属性列表(info

property list)中添加⼀一个新的键值对在应⽤用程序出现在屏幕前隐藏状态栏

•选择 Hypnosister target,然后选择 Info ⾯面板,在后⼀一⾏行点 “+” 图标,会显⽰示⼀一个新⾏行

•在新⾏行的 Key 列中,选择 “Status bar is initially hidden”, 在 Value 列中把值从NO改成 YES

Page 79: 06 Subclassing UIView and UIScrollView

隐藏状态栏的 Info property list

Page 80: 06 Subclassing UIView and UIScrollView

隐藏了状态栏的窗⼝口


Top Related