View Programming Guide for iOS 学习笔记_0

View Programming Guide for iOS 学习笔记

最近开始看苹果的官方文档,第一篇看的是这个View Programming Guide for iOS,因为我不爱看官方文档,全都是英语,有点头疼,但是这个东西确实不看不可的,所以我逼着自己把这篇Guide翻译一遍,顺便提高一下自己阅读文档的能力。还没有翻译完,有更新我会贴上来的。因为我的翻译能力有限,所以希望大家多多批评指正让我更好的进步,谢谢大家。

视图结构基础

  1. 所有UIKit的View都有一个层,这个层一般是CALayer类的对象。这个层管理这个View的backing Store并且处理与这个View相关的动画。可以通过View的 layer 属性来访问这个层。

  2. 一个View的绘制代码应该尽量少调用,当绘制代码调用时,绘制的结果会被Core Animation储存并尽可能多的重用。重用已渲染好的内容可以避免使用在更新View时需要的消耗很大的绘制循环。重用比新创建一个新的内容话费要少。

视图的层次结构与子视图的管理

  1. 子视图如果是不透明的,它将掩盖父视图的一些内容,子视图如果是透明的,子视图与父视图的内容将会混杂在一起显示在屏幕上。
  2. 每个子视图都把它自己的子视图储存在一个有序的数组中,这些子视图在数组中的顺序会影响它们在视觉上的排列。最后被加入或者被移动到数组最后的子视图将会显示在所有子视图的上方。
  3. 父视图的变化也将影响到子视图的变化,比如大小,隐藏,改变透明度,或者应用数学形变。
  4. 视图层次结构也决定了你的应用如何响应事件,当一个触摸事件发生在一个View上时,系统会向该View发送带有触摸信息的事件。如果该View不处理这个触摸事件的话,这个触摸事件将会被传递到它的父视图来进行处理,如果父视图还不处理,就传递到父视图的父视图(也就是爷爷视图,哈哈)来处理。视图也可以把触摸事件出图到一个介入的responder object,比如View Controller,如果没有对象处理这个事件,这个事件将会到达application object,application object将会丢弃这个事件。

视图的绘制循环

  1. UIView使用一个即时的绘制模型去绘制内容,当一个视图在屏幕中第一次出现时,系统要求它绘制它的内容。系统捕获一个该View内容的快照当做这个View的视觉表现。如果不去改变这个View的内容,这个View的绘制代码将永远不会执行。当需要这个View时这个快照图像将被重用。如果你改变了View的内容,这个过程将被重复一遍,系统将会使用新生成的快照作为新的结果。
  2. 当你的视图的内容改变时,也不需要直接重绘这些改变,你需要使用 setNeedSDispaly: 或者 setNeedSDisplyInRect: 方法。这些方法告诉系统,视图的内容发生改变了,在下次有机会时需要重绘这个视图。机会是什么呢,系统将会等到当前循环结束才进行重绘,这种延迟给你机会处理多个视图,在你的视图层次结构里添加或者移除视图,隐藏视图,改变视图大小,改变视图的大小,这些操作将会一次性处理。
    注意:改变View的几何形状将不会使系统重绘视图的内容,视图的contentMode属性决定了当视图的几何形状改变时该如何做。大多数内容模式会在视图的边界里里拉伸或者改变现有的快照。
  3. 当渲染视图内容是,实际的绘制过程是各不相同的,这要取决于View自身与View的配置。系统的视图实现了私有的绘制方法去渲染视图的内容。这些相同的系统视图会有接口你可以使用这些接口来配置视图的实际显示。对于用户自定义的UIView自雷,你需要覆盖你的View类 drawRect: 方法,利用这个方法来绘制你的视图的内容。还有其他方法去渲染视图的内容,比如直接配置其下的layer去设置内容,当时覆盖drawRect: 方法是比较常用的技术。

Content Mode

View的Content Mode将会在以下的情况被应用:

  • 改变视图的frame或bounds的width、height
  • 给View的transform属性指派一个包括比例因子的形变

Content Mode对View的内容的重用是有好处的,但是,你仍然可以吧contentMode设置为UIViewContentModeRedraw 当你想要使你自定义的视图在发生缩放与大小改变时重绘自己。

可伸展的视图

你可以把视图的一部分指定为可伸展的,这样当视图的大小改变时,只有处于这个可伸展部分的内容会受影响。可伸展的视图典型的使用在UIButton和那些部分视图是有重复式样的View上。你可以在一个轴或者两个轴上对视图进行伸展。当在两个轴上进行伸展时,View的边缘必须要是有可重复的图案,不然会导致形变。

你可以通过View的ontentStretch属性来指定可伸展的区域。这个属性接受归一化的矩形。当伸展这个View时,系统用View的bounds和缩放因子去乘以这个归一化的值去决定哪些像素(们)需要被伸展。使用归一化的值是你不用去在View大小改变时去更新的的contentStretch的值。

视图的content mode也会决定驶入的可伸展区域是怎么使用的。可伸展区域只能使用在content mode 会造成View的内容产生缩放的时候。所以说可伸展的视图只能在content mode 为 UIViewContentModeScaleToFill, UIViewContentModeScaleAspectFit, UIViewContentModeScaleAspectFill 时使用。如果你把视图的内容固定到某个边、角,系统将会忽略可伸展的区域。

**注意**contentStretch属性推荐在创建可伸展的UIImage时使用,这个UIImage被指定作为一个View的背景。可伸展的视图是完全在CoreAnimation层操作的,这会提供更好的性能。

内置的动画支持

许多UIView(子)类的属性都是设置有动画的。为了表现一个这个动画属性的动画,你需要做的只是:

  • 告诉UIKit你想要呈现一个动画
  • 改变这个属性的值

UIView中你可以使用动画的这些属性有:

  • frame
  • bounds
  • center
  • transform
  • alpha
  • background
  • contentStretch

动画非常重要的一个用到的地方就是从一些列Views过渡到另一些Views。典型的,你使用一个视图控制器来管理这些有关你的界面的重要改变的动画。然而你同样可以使用动画来创建一个过渡来代替ViewController。你也可以在系统ViewController的动画不能满足你的时候时候使用这些。
你也可以使用Core Animation层来创建动画,这个会给你对动画更多的控制。

视图几何与坐标系统

默认的坐标系统的原点在左上角,向左向右各延伸出一个轴,分别为x轴和y轴。坐标值用浮点数来表示。这将允许精确的布局,并且忽略掉屏幕分辨率的约束。
在屏幕的坐标之上,window和view定义了自己的坐标系统允许你根据View和window的原点指定坐标。
因为所有的View和window都有自己的坐标系统,所以要注意哪一个坐标系统现在正在起作用。当绘制内容时,你是在View自己的坐标系统下指定坐标。当你改变View的几何大小是,你是在父视图的坐标系统下指定坐标。UIWindow类和UIView类都包含了帮助你从一个坐标系统改变到另一个坐标系统的方法。

注意
有些iOS技术定义的坐标系统是跟UIKit系统定义的坐标系统不同的。比如Core Graphics和OpenGL ES的原点就在左下角,x轴向左,y轴向上。所以要时刻注意坐标系统。

Frame、Bounds、Center属性的联系

一个View对象通过他的frame、bounds、center属性来追踪自己的位置。

  • frame属性包含一个frame矩形,它确定了View的大小和这个View在父视图坐标系下的位置
  • bounds属性包含一个bounds矩形,它在本View的坐标系下确定了View的大小和它的内容的原点
  • center属性包含了在父视图坐标系下本View已知的中心点

主要使用center和frame属性来对当前View的几何形状进行操作。
center属性是一直有效的,即使View被缩放、旋转。frame属性却不是一直有效的,frame属性什么时候是无效的呢,当这个View的形变不是恒等形变。

使用bounds属性时主要是用在绘制上,bounds矩形是在本View的坐标系下的。默认的矩形原点是(0,0),大小与frame矩形的大小相匹配。你在这个矩形内绘制的任何东西都是本View可见内容的一部分。如果你改变了bounds矩形原点的位置,任何在你新的矩形内绘制的东西都是这个View可见的内容。

尽管你可以单独的改变frame、bounds、center属性中的任何一个,但是一个属性的改变会影响到其他属性,下面是改变规则

  • 当你设置了新的frame属性,bounds属性的size会改变以匹配新的frame矩形的size,center属性也会相应的改变去匹配新的frame矩形。
  • 当你设置了新的center属性,frame属性的原点将会相应的改变,但size不会发生变化
  • 当你设置了新的bounds属性的size,frame属性的size也会相应的发生变化。

默认情况下,一个View的frame是不会被父视图的frame剪切的。因此,任何在父视图frame外的子视图将会自己完全负责渲染。你可以通过改变附属图的clipsToBounds属性为YES来改变这个行为。不管子视图是否被剪切,触摸事件总会遵守目标视图的父视图的bounds矩形。总之,如果一个视图有一部分在父视图之外,则发生在之外这部分上的触摸事件是不会被传递到这个视图的。

坐标系统变换

坐标系统转化提供了一种可以快速便捷改变你的View的方式。affine transform 是一个数学矩阵,它确定了一个点是怎样从一个坐标系转换到另一个坐标系。你可以应用affine transforms(仿射变换)到你的View上去改变View的大小,位置,或者它在父视图中的方向(也就是旋转咯?)你也可以在你的绘制代码中应用affine transform去对单独的某块渲染内容做同样的操作。怎样使用呢?

*如果你想修改真个View的话,只要修改View的transform属性就好啦
*如果你在你的drawRect方法中修改某一特定部分的内容的话,你需要修稿与当前活动的graphics context相关的affine transform

当你想要实现一个动画时,通常会修改View的transform属性。你不会使用这个属性对View进行固定的改变,比如改变View在父视图坐标系下的的位置、大小,对于这种修该,你应该修改frame矩形。

注意当修改View的transform属性时,所有的变换都是根据View的中心做变换的。

在View的drawRect方法中,使用affine transforms去放置或定位一个你想要绘制的事物。相对于固定一个对象的位置到View的某个方位上,创建每个对象的时候都参考某个固定的点要简单不少,这个点通常是(0,0),然后在绘制之前使用transform去放置这个对象。这样的话,如果一个对象的位置发生变化的话,你需要做的只是去修改transform,这要比在新的位置重新创建一个对象要快,话费也少。你可以通过CGContextGTM方法来获得一个graphic context的affine transform,可以在绘制时使用Core Graphic的相关函数来修改transform。

current transformation matarix(当前形变矩阵)CTM是一个可以在任何时候都使用的affine transform。当操作View的几何形状是,CTM是储存在View的transform属性中的affine transform。在你的drawRect:方法中,CTM是与当前活动的graphics context相关联的affine transform。
每一个子视图的坐标系统都是构建在它的祖宗(哈哈哈)视图坐标系统之上的。当你修改一个视图的transform属性时,这种修改会影响到该视图与它的子视图的。然而,这些改变只会影响到一个View最终在屏幕上的渲染。因为因为每一个View 绘制自己的内容同时根据自己的边界来对子视图进行布局,View在绘制或者布局时会忽略父视图的变换。
绘制时候的形变只会影响绘制的内容的形变,对view的形变会影响到整个view。

注意如果一个View的transform 属性不是恒等变换,View的frame属性的值是未定的,它必须被忽略(因为发生形变了呀已经,View的frame已经发生变化了)。所以当你对一个view进行变换时,你必须使用view的bounds属性和center属性来得到view的大小与位置。frame矩形和该View的任何子视图依然是有效的,因为他们是与该View的bounds相关的。

点与像素

在iOS中,所有坐标值和距离都是用浮点数单元来表示的,这个浮点数单元叫做点(point)。在不同设备上,一个点代表的大小是不同的,也是不相关的。理解点最主要的事情就是点提供了一个固定的的frame以供绘制。
下表是iOS设备屏幕的尺寸(用点来表示的)

设备屏幕尺寸
4英寸retina屏幕320x568
3.5英寸屏幕320x480
ipad768x1024

这种基于点的测量系统被用在各种类型的设备上被称为user coordinate space(用户坐标空间)的地方。这是你在你所有的代码中使用的标准坐标空间。
要注意一个点不一定对应一个像素,这两者是不相同的概念。在设备上,你在View中确定的点会被转化成一些像素。

但是,将user coordinate space中的点映射为像素的过程是由系统来完成的。UIKit和Core Graphics主要使用基于向量的绘制模型,这个模型的坐标系统是由点来指定的。所以当你使用Core Graphics绘制东西的时候,要使用点(忘了像素这个东西吧,在你写代码的时候使用不到的)。

当你需要使用image或者其他基于像素的技术,比如说OpenGL ES,iOS提供了管理这些像素的帮助。对于在你的程序包中储存的作为资源的静态image文件,iOS定义了来指定的不同分辨率的image和加载最适合当前屏幕分辨率的image的约定。View也提供了关于当前的缩放因子的信息,这样你可以手工使用任何基于像素的绘制代码为高分辨率的的屏幕服务。有关这种技术可以参考 Supporting High-Resolution Screens In Views in Drawing 和 Printing Guide for iOS。

View的运行时交互模型

无论何时用户与你的用户界面发生交互,无论何时你的代码改变了一些东西,UIKit中的一个复杂的事件序列将会处理这个交互。在这个序列的一个某个特定的时刻,UIKit调用视图类,让视图类来代表你的程序响应事件。

以一个触摸事件为例:

  1. 用户触摸了屏幕
  2. 硬件向UIKit框架报告这个事件
  3. UIKit框架将这个触摸打包成一个UIEvent对象并将这个UIEvent对象发送到一个合适的View。(详细了解UIKit是怎么传递事件到你的View的,参考Event Handling Guide for iOS)
  4. View的事件处理代码相应这个事件,比如,你的代码可能这么做:

    • 改变view或者View的子视图的属性(frame、bounds、center、alpha等等)
    • 调用setNeedsLayout方法把这个View或者他的子视图标记为需要做布局更新
    • 调用setNeedsDisplay方法或者setNeedsDisplayInRect方法把这个View或者他的子视图标记为需要重绘
    • 通知一个controller改变一些数据

    这取决于你想要怎么处理这个事件

  5. 如果一个view的几何形状需要改变,UIKit根据如下的规则更新它的子视图
    • 如果你已经为你的View配置了自动重新改变大小的规则,UIKit根据这些规则来调整每一个视图(详见 Handling Layout Changes Automatically Using Autoresizing Rules.)
    • 如果view实现了layoutSubviews方法,UIKit会调用它。
      在你自定义的View中你可以覆盖这个方法并且使用它来调整任何子视图的大小和位置。比如,一个提供很大的滚动区域的View将会需要许多的子视图作为瓷片,而不是创建一个大的View。在这个方法的实现中,这个View会隐藏任何不在屏幕区域中的子视图、改变这些子视图位置、或者用这个View区或者新的内容。作为这个进程的一部分,这个View的布局代码也可以使任何需要重绘的view无效。
  6. 如果任何视图的任何部分被标记为需要重绘,UIKit通知这个View重绘自己。
    对于用户自定义的已经明确定义了drawRect方法的view,UIKit调用这个方法。这个方法的实现代码应该尽快的重绘这个特定的区域,并且不应该做别的事。不要做任何附加的布局调整,也不要改变你的程序的数据模型。这个方法这是为了更新视图的可见内容。
    标准的系统view通常不虚线drawRect方法,取而代之的是管理他们的绘制。
  7. 任何view的更新将会与程序其他可见内容组合到一起送到绘图硬件去显示出来。
  8. 绘图硬件将这些渲染过的内容显示在屏幕上。
    注意预先更新模型主要应用在那些使用标准系统视图和绘制技术的应用上,使用OpenGL ES绘制的程序通常会配置一个单一的全屏幕的View,并且直接在相关的OpenGL ES的graphics context中绘制。在这种情况下,view可能仍然处理触摸事件,但是,因为View是全屏幕的,它将不需要对它的子视图进行调整。(详见OpenGL ES Programming Guide for iOS)

在前面的步骤中,您自己的自定义视图的主要几点是:

  • 事件处理方法

    • touchesBegan:withEvent:
    • touchesMoved:withEvent:
    • touchesEnded:withEvent:
    • touchesCancelled:withEvent:
    • layoutSubview: 方法
    • drawRect: 方法

这些一个View是最常用的需要覆盖的方法,但是你不需要全部覆盖这些方法。如果你使用手势识别方法,你不需要覆盖任何事件处理方法。简单来说,如果你的View不包含子视图或者View的大小没有发生改变,就不需要覆盖layoutSubview方法。drawRect方法只是在运行时View内容发生改变的时候才需要,这是,你使用本体的技术,比如UIKit或者Core Graphics来进行绘制。
同时还要记住这只是常用的需要覆盖的方法,而不是全部的。许多UIView的方法被设计成可覆盖的,你需要查阅UIView Class Reference中方法的描述来看哪一个methods是合适你去覆盖来实现你自定义的实现的。

高效实用视图的小贴士

当系统提供的view们无法满足你的时候,你需要自定义实现一个View,但是你要保证你这个view的性能足够好。UIKit做了很多的事可以充分利用与View有关的行为,也帮助你自定义的view能达到一个好的表现性能。下面是使用这些帮助的小贴士。

**注意:**在你优化你的绘制代码之前,你应该搜集你现有的View表现性能的数据,这样你才能确定哪里有问题,同时给你一个比较的基准。

view不一定总是有一个相应的view controller

在你的应用中,单独的view与view controller 的一对一得关系是十分罕见的。view controller的工作就是管理一个视图层次结构,view controller经常含有许多的view来实现一些自给自足的特性。对于iPhone 应用,每一个视图层次结构通常充满了整个屏幕,虽然,对于ipad程序,一个视图层次结构只会填充屏幕的一部分。
当你设计你应用的用户界面是,考虑view controller扮演什么角色的非常重要的。view controller提供了许多重要的行为,比如协调view在屏幕上的外观,协调从屏幕上移除这些view,当收到低内存警告的时候释放内存,还有在界面方向发生改变时旋转view。绕过这些行为会是你的应用表现的不正确。
view controller的用法,详见iew Controller Programming Guide for iOS。

减少自定义绘制

尽管有时自定义的绘制是必须的,你也应该尽可能的避免一些事情。你唯一需要做自定义绘制的时候就是当现有的系统view类不能提供你需要的外观或者能力。你的内容可以被不同的现有的view混合来组成的话,你最好的用法就是把这些view对象组合成一个自定义的视图层次结构。

利用ContentMode

content mode减少重绘view的次数。默认情况下,view使用UIVIEWContentModeScaleTofFill的content mode,这个content mode会缩放已有的内容来适应view的frame矩形(填满)。当你需要的时候你可能会改变这个mode,但是你应该尽可能的避免使用UIViewContentModeRedraw这种contentMode。无论哪种contentMode生效,你都可以调用setNeedsDisplay或者setNeedDisplayInRect去强迫View去重绘自己。

尽可能的把View声明称不透明的。

UIKit使用view的opaque属性来决定view是否可以利用合成操作。把一个自定义view的opaque属性设置为YES来告诉UIKit,在这个视图之后的任何内容都不需要渲染。更少的渲染你可以增加你的绘制代码的性能,是被Apple鼓励的。当然,当你把opaque属性设置为YES时,你的view必须使用完全不透明的内容来完全充满view的边界。(要是不能的话就不要这么设置了)

当滚动时调整你自定义视图的绘制行为

scroll会在短时间内导致许多view的更新。如果你的自定义view的绘制代码没有适当的调整,view的滚动表现将会迟滞。与其保证你的自定义视图的内容总是崭新的,不如在滚动操作发生时考虑改变你的view的行为。比如,当一个滚动scroll在进程里时,你可以暂时降低你渲染内容的质量,或者改变contentMode。当scroll停止时,你可以返回你view之前的状态并且在必要的情况下更新内容。

不能通过内嵌子视图来自定义控制

尽管在技术上增加一个子视图到标准系统controls(继承自UIControl的对象)是可行的,但是,你应该永远不要像这样自定义controls。control是通过自己详细的、文档齐全的接口来提供这样的自定义。

Windows

每个iOS应用都需要至少一个window(UIWindow类的实例),有些可能需要更多的window。一个window有许多职责:

  • 它包含你的应用的所有可见的内容
  • 它在传递touch event到view或其他应用对象时扮演了一个主要的角色
  • 它与你应用的view controller一同合作来是方向变化变得便利
    在iOS中,window没有title bar,封闭的矩形,或者其他可见的饰物。一个window就是一个空白的可以容纳一个或多个view的容器而已。当一个新的window被显示的时候应用程序也不会改变内容。当你想要改变显示的内容是,你应该改变在window之上的view。
    大多数的iOS应用在生命过程中仅创造和使用一个window。这个window占据了设备的整个主屏幕。window是在应用程序生命周期开始前从程序的主nib文件加载的(或者用代码实现的)。然而,如果一个程序支持使用外部显示器作为视频实处,应用程序可以创建一个新的window去显示外部显示器的内容。其他的所有的window通常都是被系统创建出来的,创建出来用于相应特定的时间,比如说来了一个电话。

涉及到window的任务

对于多数应用程序,应用程序唯一与它的window交互的时候就是在应用程序在启动时创建window的时候。然而你可以使用你应用程序的window对象来执行一些与应用程序有关的任务。
* 使用window对象去转换point或者矩形到window自己的坐标系(反之也可以)。比如,提供给你了一个在window坐标小的值,在使用之前你可能想要转换这个值到一个特定的view坐标系之下。(详见Converting Coordinates in the View Hierarchy.)
* 使用window notification去跟踪与window相关的变化。当一个window被显示、被隐藏或者注册为主状态、注销朱状态时产生一个notification。你可以使用这些notification去执行一些在你的应用程序其他部分的动作。(详见Monitoring Window Changes)

创建和配置一个window

你可以用代码创建你的应用程序的主window或者用Interface Builder来穿件。在这两种情况下,你在启动阶段创建这个window并且要retain它,并且在你的应用程序中的代理对象储存一个引用。如果你的应用程序创建额外的window(们),应用程序会在需要它们的时候再创建(延迟创建)。
你应该总是在启动的时候创建你的应用程序的主window,不论你的应用程序是在前台被启动还是在后台被启动。对它自身来说,创建和配置一个window不是什么消耗大的操作。然而如果你的应用程序是创建之后直接进入到后台,你应该避免让这个window课件知道你的应用程序进入前台。

在Interface builder中创建window

用Interface builder创建你应用程序的主window是很简单的因为Xcode项目的模板为了做了之歌工作。所有的新的Xcode应用程序项目包含一个主nib文件(通常叫MainWind.xi或者一些辩题),这个主nib文件包含了应用程序的主window。额外说一句,这些模板也在应用程序的代理对象里为这个window定义了一个outlet。(就是在AppDelegate中的@property (strong, nonatomic) UIWindow *window;),在你的代码中你可以使用这个outlet来访问这个window对象。

    当在Interface builder创建你的window时,会推荐你在属性检视板中的启动选项中打开 Full Screen。如果这个选项没有打开,同时你的window要比这个目标设备的屏幕小的话,触摸事件将不会被一些view接收到。这是因为window(像其他的view一样)不会接收在它的边界bounds矩形之外的触摸事件。因为默认view是不会被window的边界剪切的,这个view将会仍然显示,但是事件是不会到达的。在启动选项中打开Full Screen会保证window是适合当前的屏幕的。

如果你使用Interface Builder改造一个项目,使用Interface Builder创建一个window是一桩小事,你只需要拖拽一个window对象到你的nib文件。当然你还应该做下面的这些事:

  • 为了在运行时访问这个window,你应该将这个window关联到一个outlet,通常是在你的应用程序代理或者在nib文件的File’s Owner中定义的
  • 如果你改造计划中包括使你新建的nib文件作为你应用程序的主nib文件,你在你的应用程序的Info.plist必须设置这个NSMainNibFile值为你新建的nib文件。改变这个键的值保证了在你应用程序的代理中的application:didFinishLaunchingWithOptions: 方法被调用时这个nib文件已经被加载并且可以使用。

创建和配置nib文件,详见 Interface Builder User Guide.
如何在运行时加载nib文件到你的应用程序中,详见Resource Programming Guide中的Nib Files.

用代码创建一个window

如果你更喜欢用代码创建你的应用程序的主window,你应该将如下的代码加入到你应用程序代理中的application:didFinishLaunchingWithOptions: 中:

self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

在这个示例代码中,self.window被假定为一个在你的应用程序代理中已申明的属性,这个属性已经被配置retain这个创建的window对象。如果你为一个外部显示器创建一个window,你应该把他指派给一个不同的变量,同时你需要说明这个不是主UIScreen对象的边界。

当创建一个window,你应该设置window的大小为屏幕的整个边界的大小。你不应该为添加状态栏或者其他的东西来减小这个size。状态栏总会浮动在window的上部,你为放置这个唯一要做的事就是缩小你放到window上的view。如果你说哪个view controller,这个view controller会自动的调整你的view(们)的大小。

为你的window添加内容

每一个window通常都有一个单独的根视图对象(被相应的view controller管理),这个试图对象包含了其他所有的表现你的内容的view(们)。使用一个单独的根视图简化了改变界面的过程;为了显示新的内容,你需要做的只是替换这个根视图。为了加载一个view 到你的window,使用addSubview:方法。比如说,为了加载一个被一个view controller管理的视图,你应该使用如下的示例代码:

[window addSubview:viewController.view];

取代以上的代码,你可以在你的nib文件中配置window的rootViewController属性。这个属性提供了一个便捷的方式去配置window的根视图,它是用nib文件实现而不是代码实现。如果这个属性在这个window从nib文件中加载的时候被设置,UIKit自动的加载这个与view controller相关联的view作为这个window的根视图。这个属性只能被用来设置window的根视图,不应该被用来window与view controller的交流沟通。

你可以使用任何一个view作为window的根视图。取决于你的界面设计,这个根视图可以是一个通用的UIView对象(扮演了一个容纳了一个或多个子视图的容器),这个根视图可以使一个标准系统View,这个根视图可以使一个你自定义的视图。一些标准系统视图通常被用作根视图,这些标准系统视图包括scroll views, table views, 和image views。

当配置一个window的根视图时,你需要负责在window内设置这个视图的位置和大小。对于不含状态栏的应用程序,或者显示半透明状态栏的应用程序,设置根视图的大小匹配window的大小。对于那些现实不透明的状态栏的应用程序,你需要放置这个根视图在状态栏之下,它的大小要比window的大小要小一些。根视图的高度=window的高度-状态栏的高度,以避免根视图的上部被状态栏遮盖住。

注意如果window的根视图是由一个容器view controller提供的(比如tab bar controller,navagation controller,或者split view controller),你不需要自己设置初设的高度。不论状态栏是否可见,容器viewcontroller会自动设置view为一个合适的大小。

改变窗口等级

所有的UIWindow对象都有一个可配置的windowLevel属性,这个属性决定了这个window是怎么与其他window有联系的放置的。在大多数情况下,你不需要改变应用程序window的这个level。在创建时新的window自动的被分配为normal window level。这个normal window level指示这个window呈现与应用程序相关的内容。Higer window level是为那些需要浮动在应用程序内容之上的信息预留的,比如说系统状态栏或alert message。尽管你可以给window分配higer window level,当你使用特殊的界面时系统通常会为你做这些工作。比如说,当你想要显示或者隐藏状态栏或者alret View,系统会自动的创建这些需要的window去显示这些内容。

监视window变化

如果你想要在你的应用程序中追踪window的出现于消失,你可以使用这些与window相关的notification:

  • UIWindowDisBecomeVisibleNotification
  • UIWindowDidBecomeHiddenNotification
  • UIWindowDidBecomeKeyNotification
  • UIWindowDidResignKeyNotification
    当你通过代码对window进行改变时,作为响应,这些notification将会被传递。因此,当你的应用程序显示或者隐藏一个window时,UIWindowDisBecomeVisibleNotification和UIWindowDidBecomeHiddenNotification将会相应的被传递。当你的应用程序在后台运行的时候,这些notification将不会被传递。即使在你的应用程序处于后台状态你的window不在屏幕上显示的时候,window仍然被看做是可见的。
    UIWindowDidBecomeKeyNotification 和UIWindowDidResignKeyNotification通知帮助你的应用程序追踪哪一个window是主window(也就是说哪一个window是现在接受键盘事件和其他不与触摸相关的事件)。然而触摸事件会被传递到触摸事件发生的那个window,没有相关的坐标值的事件会被传递到你应用程序的主window。同一时刻,只能有一个window是主window。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值