🔏
iOS
  • 前言
  • View编程参考
    • View/Window架构
    • Window
    • View
    • Animations
  • ViewController编程参考
    • 定义
    • 呈现视图控制器
  • UIKit
    • 关于应用开发
    • 保护用户隐私
    • 应用和环境
      • 应用声明周期
      • 应用启动
      • UIApplication
      • UIApplicationDelegate
    • Windows and Screens
      • Window
      • Scene
      • Screen
    • 视图和控制
      • UIView
Powered by GitBook
On this page
  • 视图架构基础
  • 查看层次结构和子视图管理
  • 绘制流程
  • 内容模式
  • 可伸缩的视图
  • 内置动画支持
  • 视图几何和坐标系
  • 框架,边界和中心属性的关系
  • 坐标系转换
  • 点与像素
  • 视图的运行时交互模型
  • 有效使用视图的提示
  • 视图并不总是具有对应的视图控制器
  • 最小化自定义绘制
  • 利用内容模式
  • 尽可能将视图声明为不透明
  • 滚动时调整视图的绘图行为
  • 不要通过嵌入子视图来自定义控件

Was this helpful?

  1. View编程参考

View/Window架构

PreviousView编程参考NextWindow

Last updated 4 years ago

Was this helpful?

视图和窗口显示应用程序的用户界面,并处理与该界面的交互。UIKit和其他系统框架提供了许多视图,您几乎可以不加修改地使用它们。您还可以为需要不同于标准视图显示内容的地方定义自定义视图。

视图架构基础

您可能希望在视觉上做的大多数事情都是使用视图对象完成的,即UIView类的实例。视图对象在屏幕上定义一个矩形区域,并处理该区域中的绘图和触摸事件。视图还可以充当其他视图的父级,并协调这些视图的放置和大小调整。UIView类完成了管理视图之间的这些关系的大部分工作,但是您也可以根据需要自定义默认行为。

视图与Core Animation图层结合使用,以处理视图内容的渲染和动画处理。UIKit中的每个视图都由一个图层对象(通常是CALayer类的实例)支持,该对象管理该视图的后备存储并处理与视图相关的动画。您执行的大多数操作应通过UIView界面。但是,在需要更好地控制视图的渲染或动画行为的情况下,可以改为通过其图层执行操作。

要了解视图和图层之间的关系,有助于查看示例。图1-1显示了ViewTransitions示例应用程序中的视图架构以及与基础Core Animation层的关系。应用程序中的视图包括一个窗口(也是一个视图),一个通用的UIView对象,它充当容器视图,图像视图,用于显示控件的工具栏和条形按钮项(它本身不是视图,而是在内部管理视图)。实际的ViewTransitions示例应用程序包括一个用于实现过渡的附加图像视图。为简单起见,由于该视图通常是隐藏的,因此未包含在图1-1中。)。每个视图都有一个相应的图层对象,可以从该视图的layer属性访问该对象。(由于长条按钮项不是视图,因此无法直接访问其图层。)这些图层对象后面是Core Animation渲染对象,最终是用于管理屏幕上实际位的硬件缓冲区。

核心动画层对象的使用对性能具有重要影响。尽可能少地调用视图对象的实际绘图代码,并且在调用该代码时,结果由Core Animation缓存并在以后尽可能多地重用。重用已经渲染的内容消除了更新视图通常所需的昂贵绘制周期。此内容的重用在动画中尤其重要,在动画中,可以操纵现有内容。这种重用比创建新内容消耗更少的性能。

查看层次结构和子视图管理

除了提供自己的内容外,视图还可以充当其他视图的容器。当一个视图包含另一个视图时,将在两个视图之间创建父子关系。关系中的子视图称为子视图,父视图称为超级视图。建立这种类型的关系对应用程序的外观和应用程序的行为都有影响。

在视觉上,子视图的内容会掩盖其父视图的全部或部分内容。如果子视图是完全不透明的,则子视图所占据的区域将完全遮挡父视图的相应区域。如果子视图是部分透明的,则将两个视图中的内容混合在一起,然后再显示在屏幕上。每个超级视图都将其子视图存储在一个有序数组中,并且该数组中的顺序也会影响每个子视图的可见性。如果两个同级子视图彼此重叠,则最后添加(或移至子视图数组末尾)的子视图会出现在另一个子视图的顶部。

超级视图与子视图的关系也会影响几种视图行为。更改父视图的大小会产生连锁反应,这也会导致任何子视图的大小和位置也发生变化。更改父视图的大小时,可以通过适当配置视图来控制每个子视图的调整大小行为。影响子视图的其他更改包括隐藏超级视图,更改超级视图的alpha(透明度)或对超级视图的坐标系进行数学变换。

视图层次结构中视图的排列方式还决定了您的应用程序如何响应事件。当特定视图内发生触摸时,系统会将带有触摸信息的事件对象直接发送到该视图以进行处理。但是,如果视图不处理特定的触摸事件,则可以将事件对象传递到其父视图。如果超级视图不处理事件,则它将事件对象传递给其超级视图,依此类推。特定的视图也可以将事件对象传递给中间的响应者对象,例如视图控制器。如果没有对象处理该事件,则该事件最终到达应用程序对象,该对象通常将其丢弃。

绘制流程

UIView类使用按需绘制模型来呈现内容。当视图首次出现在屏幕上时,系统会要求它绘制其内容。系统捕获该内容的快照,并将该快照用作视图的视觉表示。如果您从不更改视图的内容,则可能永远不会再次调用视图的绘图代码。快照图像可用于涉及视图的大多数操作。如果确实更改了内容,则会通知系统视图已更改。然后,视图重复绘制视图和捕获新结果快照的过程。

当视图的内容更改时,您不会直接重绘这些更改。而是使用setNeedsDisplay或setNeedsDisplayInRect:方法使视图无效。这些方法告诉系统视图的内容已更改,需要在下一个机会重新绘制。在启动任何绘图操作之前,系统将一直等到当前运行循环结束。此延迟使您有机会一次使多个视图无效,从层次结构中添加或删除视图,隐藏视图,调整视图大小以及重新放置视图。您所做的所有更改都将同时反映出来。

更改视图的几何形状不会自动导致系统重新绘制视图的内容。视图的contentMode属性确定如何解释对视图几何的更改。大多数内容模式会在视图范围内拉伸或重新定位现有快照,而不创建新快照。

当需要渲染视图内容时,实际的绘制过程会根据视图及其配置而有所不同。系统视图通常实现私有绘制方法来呈现其内容。这些相同的系统视图通常会公开可用于配置视图实际外观的界面。对于自定义UIView子类,通常会覆盖视图的drawRect:方法,并使用该方法绘制视图的内容。还有其他提供视图内容的方法,例如直接设置基础层的内容,但是重写drawRect:方法是最常见的技术。

内容模式

每个视图都有一个内容模式,该模式可以控制视图如何根据视图几何形状的变化来回收其内容以及是否完全回收其内容。首次显示视图时,它会照常渲染其内容,并将结果捕获在基础位图中。之后,对视图几何形状的更改并不总是导致重新创建位图。而是,contentMode属性中的值确定是应缩放位图以适合新的边界还是应将其简单固定到视图的一个角或边缘。 只要执行以下操作,便会应用视图的内容模式:

  • 更改视图框架或边界矩形的宽度或高度。

  • 将包含缩放因子的变换分配给视图的变换属性。

    默认情况下,大多数视图的contentMode属性设置为UIViewContentModeScaleToFill,这将导致视图内容被缩放以适应新的框架大小。

    图1-2显示了某些可用内容模式下发生的结果。从图中可以看出,并非所有内容模式都会导致视图边界被完全填充,而那些内容模式可能会扭曲视图的内容。

内容模式非常适合回收视图的内容,但是当您特别希望自定义视图在缩放和调整大小操作时重新绘制自己时,也可以将内容模式设置为UIViewContentModeRedraw值。将视图的内容模式设置为该值会强制系统响应于几何形状的更改调用视图的drawRect:方法。通常,应该避免在任何可能的情况下使用此值,并且一定不要在标准系统视图中使用它。

可伸缩的视图

您可以将视图的一部分指定为可拉伸的,以便当视图的大小更改时,仅可拉伸部分中的内容受到影响。通常,将可拉伸区域用于按钮或其他视图,其中视图的一部分定义了可重复的模式。您指定的可拉伸区域可以允许沿视图的一个或两个轴拉伸。当然,当沿两个轴拉伸视图时,视图的边缘还必须定义可重复的图案,以避免任何变形。图1-3显示了这种变形如何在视图中显现出来。每个视图原始像素的颜色被复制以填充较大视图中的相应区域。

您可以使用contentStretch属性指定视图的可拉伸区域。此属性接受一个矩形,其值规格化为0.0到1.0。拉伸视图时,系统将这些归一化值乘以视图的当前边界和比例因子,以确定需要拉伸的像素。标准化值的使用减轻了您每次视图范围更改时都更新contentStretch属性的需求。

视图的内容模式在确定如何使用视图的可拉伸区域方面也发挥着作用。仅当内容模式会导致视图内容缩放时,才使用可拉伸区域。这意味着只有UIViewContentModeScaleToFill,UIViewContentModeScaleScaleAspectFit和UIViewContentModeScaleScaleAspectFill内容模式才支持可拉伸视图。如果您指定一种将内容固定到边缘或角落的内容模式(因此实际上不会缩放内容),则视图将忽略可拉伸区域。

在为视图指定背景时,建议在创建可拉伸UIImage对象时使用contentStretch属性。可拉伸视图完全在“核心动画”层中处理,通常可以提供更好的性能。

内置动画支持

在每个视图后面都有一个图层对象的好处之一是,您可以轻松地对许多与视图相关的更改进行动画处理。动画是一种向用户传达信息的有用方法,在设计应用程序时应始终考虑动画。UIView类的许多属性都是可设置动画的,即存在从一个值到另一个值的动画的半自动支持。要为这些可设置动画的属性之一执行动画,您要做的就是:

  • 告诉UIKit您要执行动画。

  • 更改属性的值。

    您可以在UIView对象上设置动画的属性包括:

  • frame - 使用它为视图设置位置和大小变化的动画。

  • bounds - 使用此动画可对视图大小进行动画处理。

  • center - 使用它可以动画化视图的位置。

  • transform - 使用它旋转或缩放视图。

  • alpha - 使用它可以更改视图的透明度。

  • backgroundColor - 使用此按钮可以更改视图的背景色。

  • contentStretch - 使用它来更改视图内容的拉伸方式。

从一组视图过渡到另一组视图时,动画非常重要。通常,您使用视图控制器来管理与用户界面各部分之间的重大更改相关的动画。例如,对于涉及从上层信息导航到下层信息的界面,通常使用导航控制器来管理显示每个连续数据级别的视图之间的过渡。但是,您也可以使用动画而不是视图控制器在两组视图之间创建过渡。您可能会在标准视图控制器动画无法产生所需结果的地方这样做。

除了使用UIKit类创建的动画外,还可以使用“核心动画”层创建动画。放到图层级别,可以更好地控制动画的时间和属性。

视图几何和坐标系

UIKit中的默认坐标系的原点在左上角,并且轴从原点向下延伸到右侧。坐标值使用浮点数表示,无论基础屏幕分辨率如何,都可以精确地布局和定位内容。图1-4显示了相对于屏幕的此坐标系。除了屏幕坐标系外,窗口和视图还定义了自己的局部坐标系,使您可以指定相对于视图或窗口原点而不是相对于屏幕的坐标。

因为每个视图和窗口都定义了自己的局部坐标系,所以您需要知道在任何给定时间有效的坐标系。每次绘制视图或更改其几何形状时,都是相对于某个坐标系进行的。对于绘图,您可以指定相对于视图自身坐标系的坐标。如果几何形状发生变化,则可以指定相对于超级视图坐标系的坐标。 UIWindow和UIView类都包含帮助您从一个坐标系转换为另一个坐标系的方法。

某些iOS技术定义了默认坐标系,其原点和方向与UIKit所使用的不同。例如,Core Graphics和OpenGL ES使用的坐标系的原点位于视图或窗口的左下角,并且y轴相对于屏幕向上。在绘制或创建内容时,您的代码必须考虑到这些差异,并根据需要调整坐标值(或坐标系统的默认方向)。

框架,边界和中心属性的关系

视图对象使用其frame,bounds和center属性跟踪其大小和位置:

  • frame 包含 frame 矩形,用于指定视图在其父视图的坐标系中的大小和位置。

  • bounds 包含 bounds 矩形,它在视图自己的本地坐标系中指定视图的大小(及其内容原点)。

  • center 包含超级视图坐标系中视图的已知中心点。

您主要使用center和frame属性来操纵当前视图的几何形状。例如,在构建视图层次结构或在运行时更改视图的位置或大小时,可以使用这些属性。如果仅更改视图的位置(而不是其大小),则首选center属性。即使将缩放或旋转因子添加到视图的变换中,center属性中的值也始终有效。frame属性中的值也不一样,如果视图的变换与恒等变换不相等,则视为无效。

您主要在绘制过程中使用bounds属性。边界矩形以视图自身的局部坐标系表示。此矩形的默认原点为(0,0),其大小与框架矩形的大小匹配。您在此矩形内绘制的所有内容都是视图可见内容的一部分。如果更改边界矩形的原点,则在新矩形内绘制的所有内容都会成为视图可见内容的一部分。

图1-5显示了图像视图的框架和边界矩形之间的关系。在该图中,图像视图的左上角位于其超级视图坐标系中的点(40、40),矩形的大小为240 x 380点。对于边界矩形,原点为(0,0),并且矩形的大小类似地为240 x 380点。

尽管可以独立于其他属性更改frame,bounds和center属性,但是对一个属性的更改会通过以下方式影响其他属性:

  • 设置frame属性时,bounds属性中的大小值将更改以匹配框架矩形的新大小。center属性中的值类似地更改以匹配框架矩形的新中心点。

  • 设置center属性时,frame中的原点值会相应更改。

  • 当您设置bounds属性的大小时,frame属性中的大小值将更改以匹配bounds矩形的新大小。

    默认情况下,视图的框架不会裁剪到其父视图的框架。因此,位于其父视图框架之外的所有子视图均会完整呈现。不过,您可以通过将超级视图的clipsToBounds属性设置为YES来更改此行为。无论子视图是否在视觉上被剪切,触摸事件始终会遵循目标视图的父视图的边界矩形。换句话说,发生在视图范围之外的视图的边界矩形之外的触摸事件不会传递到该视图。

坐标系转换

坐标系转换提供了一种快速,轻松地更改视图(或其内容)的方法。仿射变换是一种数学矩阵,它指定一个坐标系中的点如何映射到另一个坐标系中的点。您可以将仿射变换应用于整个视图,以更改视图相对于其超级视图的大小,位置或方向。您还可以在绘图代码中使用仿射变换来对单个渲染内容执行相同类型的操作。因此,如何应用仿射变换取决于上下文:

  • 要修改整个视图,请在视图的transform属性中修改仿射变换。

  • 要修改视图的drawRect:方法中的特定内容,请修改与活动图形上下文关联的仿射变换。

当您要实现动画时,通常可以修改视图的transform属性。例如,您可以使用此属性来创建围绕其中心点旋转的视图动画。您不会使用此属性对视图进行永久更改,例如修改视图的位置或在视图的坐标空间内调整视图的大小。对于这种类型的更改,您应该改为修改视图的框架矩形。

修改视图的transform属性时,所有变换都相对于视图的中心点执行。

在视图的drawRect:方法中,您可以使用仿射变换来定位和定向要绘制的项目。与其将对象的位置固定在视图中的某个位置上,不如相对于固定点(通常为(0,0))创建每个对象,然后使用变换在紧接绘图之前将对象定位更简单。这样,如果对象的位置在视图中发生变化,那么您只需要做的就是修改转换,这比在新位置重新创建对象要快得多且花费更少。您可以使用CGContextGetCTM函数检索与图形上下文关联的仿射变换,并且可以在绘图过程中使用相关的Core Graphics函数来设置或修改此变换。

当前变换矩阵(CTM)是在任何给定时间使用的仿射变换。操纵整个视图的几何图形时,CTM是存储在视图的transform属性中的仿射变换。在drawRect:方法内部,CTM是与活动图形上下文关联的仿射变换。

每个子视图的坐标系都基于其祖先的坐标系。因此,当您修改视图的transform属性时,该更改会影响该视图及其所有子视图。但是,这些更改仅影响屏幕上视图的最终呈现。由于每个视图都绘制其内容并相对于其自己的边界布置其子视图,因此在绘制和布局期间,它可以忽略其父视图的变换。

图1-6演示了渲染时如何在视觉上结合两个不同的旋转因子。在视图的drawRect:方法内部,对形状应用45度旋转因子会使该形状看起来旋转45度。对视图应用单独的45度旋转因子,然后使形状看起来像旋转了90度。形状仍然相对于绘制它的视图旋转了45度,但是视图旋转使它看起来旋转了更多。

如果视图的变换属性不是恒等变换,则该视图的框架属性的值是不确定的,必须忽略。将变换应用于视图时,必须使用视图的边界和中心属性来获取视图的大小和位置。任何子视图的框架矩形仍然有效,因为它们是相对于视图边界的。

点与像素

在iOS中,所有坐标值和距离都是使用浮点值(以称为点的单位)指定的。点的可测量大小因设备而异,并且在很大程度上无关紧要。关于点的主要理解是,它们为图纸提供了固定的参考系。

表1-1列出了纵向不同类型的基于iOS的设备的屏幕尺寸(以磅为单位)。首先列出宽度尺寸,然后是屏幕的高度尺寸。只要您针对这些屏幕尺寸设计界面,您的视图就会在相应类型的设备上正确显示。

设备

屏幕尺寸

具有4英寸Retina显示屏的iPhone和iPod touch设备

320 x 568

其他iPhone和iPod touch设备

320 x 480

iPad

768 x 1024

用于每种类型的设备的基于点的测量系统定义了所谓的用户坐标空间。这是几乎所有代码都使用的标准坐标空间。例如,在操纵视图的几何体或调用Core Graphics函数绘制视图的内容时,可以使用点和用户坐标空间。尽管有时用户坐标空间中的坐标会直接映射到设备屏幕上的像素,但您永远不要以为是这种情况。相反,您应该始终记住以下几点:一个点不一定对应于屏幕上的一个像素。

在设备级别,必须在某个点将在视图中指定的所有坐标转换为像素。但是,用户坐标空间中的点到设备坐标空间中的像素的映射通常由系统处理。UIKit和Core Graphics都使用主要基于矢量的绘图模型,其中所有坐标值都是使用点指定的。因此,如果使用Core Graphics绘制曲线,则无论基础屏幕的分辨率如何,都可以使用相同的值指定曲线。

当您需要使用图像或其他基于像素的技术(例如OpenGL ES)时,iOS提供了管理这些像素的帮助。对于在应用程序捆绑包中作为资源存储的静态图像文件,iOS定义了约定,用于以不同的像素密度指定图像并加载与当前屏幕分辨率最匹配的图像。视图还提供有关当前比例因子的信息,以便您可以手动调整任何基于像素的绘图代码以适应高分辨率屏幕。

视图的运行时交互模型

每当用户与您的用户界面进行交互,或者您自己的代码以编程方式更改某些内容时,UIKit内都会发生一系列复杂的事件来处理该交互。在该序列中的特定时间点,UIKit调出您的视图类,并为其提供机会代表您的应用程序进行响应。了解这些标注点对于了解视图在系统中的位置非常重要。图1-7显示了事件的基本顺序,该顺序从用户触摸屏幕开始,到图形系统作为响应更新屏幕内容,然后结束。对于任何以编程方式启动的操作,也将发生相同的事件序列。

以下步骤进一步分解了图1-7中的事件序列,并解释了每个阶段发生的情况以及您可能希望应用程序如何响应。 1. 用户触摸屏幕。 2. 硬件将触摸事件报告给UIKit框架。 3. UIKit框架将触摸包装到UIEvent对象中,并将其分派到适当的视图。 4. 您的视图的事件处理代码响应该事件。例如,您的代码可能:

  • 更改视图或其子视图的属性(frame,bounds,alpha等)。

  • 调用setNeedsLayout方法将视图(或其子视图)标记为需要布局更新。

  • 调用setNeedsDisplay或setNeedsDisplayInRect:方法以将视图(或其子视图)标记为需要重绘。

  • 通知控制器某些数据更改。

    当然,由您决定视图应执行哪些操作以及应调用哪些方法。

    1. 如果视图的几何由于任何原因发生了变化,则UIKit会根据以下规则更新其子视图:

  • 如果为视图配置了自动调整大小规则,则UIKit会根据这些规则调整每个视图。

  • 如果该视图实现layoutSubviews方法,则UIKit会调用它。

    您可以在自定义视图中覆盖此方法,并使用它来调整任何子视图的位置和大小。例如,提供较大可滚动区域的视图将需要使用多个子视图作为“平铺”,而不是创建一个大视图,该大视图无论如何都不太适合内存。在此方法的实现中,视图将隐藏现在不在屏幕上的所有子视图,或者重新放置它们,并使用它们绘制新显示的内容。在此过程中,视图的布局代码还可以使需要重绘的所有视图无效。

    1. 如果任何视图的任何部分被标记为需要重绘,则UIKit要求视图重绘自身:

      对于明确定义了drawRect:方法的自定义视图,UIKit会调用该方法。此方法的实现应尽快重绘视图的指定区域,而别无其他。此时,请勿进行其他布局更改,也不要对应用程序的数据模型进行其他更改。此方法的目的是更新视图的视觉内容。

      标准系统视图通常不实现drawRect:方法,而是在此时管理其绘制。

    2. 任何更新的视图都将与应用程序的其余可见内容混合在一起,并发送到图形硬件进行显示。

    3. 图形硬件将渲染的内容传输到屏幕。

先前的更新模型主要适用于使用标准系统视图和绘图技术的应用程序。使用OpenGL ES进行绘制的应用程序通常会配置一个全屏视图并直接绘制到关联的OpenGL ES图形上下文。在这种情况下,该视图仍可以处理触摸事件,但是由于它是全屏的,因此无需布置子视图。

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

  • 事件处理方法:

    • touchesBegan:withEvent:

    • touchesMoved:withEvent:

    • touchesEnded:withEvent:

    • touchesCancelled:withEvent:

  • layoutSubviews方法

  • drawRect方法

    这些是视图最常用的重写方法,但是您可能不需要全部重写。如果使用手势识别器来处理事件,则无需覆盖任何事件处理方法。同样,如果您的视图不包含子视图,或者其大小未更改,则没有理由覆盖layoutSubviews方法。最后,仅当视图的内容可以在运行时更改并且您正在使用本机技术(例如UIKit或Core Graphics)来绘制图形时,才需要drawRect:方法。

同样重要的是要记住,这些是主要的集成点,但不是唯一的。UIView类的几种方法被设计为子类的重写点。

有效使用视图的提示

对于需要绘制标准系统视图未提供的内容的情况,自定义视图很有用,但是您有责任确保视图的性能足够好。UIKit尽其所能来优化与视图相关的行为,并帮助您在自定义视图中获得良好的性能。但是,您可以通过考虑以下提示来在这方面帮助UIKit。

优化绘图代码之前,应始终收集有关视图当前性能的数据。测量当前性能可以让您确认是否确实存在问题,如果存在,还可以为您提供基准测量结果,您可以将其与未来的优化进行比较。

视图并不总是具有对应的视图控制器

应用程序中的各个视图和视图控制器之间几乎没有一对一的关系。视图控制器的工作是管理视图层次结构,该视图层次结构通常由多个视图组成,这些视图用于实现某些自包含功能。对于iPhone应用程序,每个视图层次结构通常填充整个屏幕,尽管对于iPad应用程序,视图层次结构可能仅填充屏幕的一部分。

在设计应用程序的用户界面时,请务必考虑视图控制器将扮演的角色。视图控制器提供了许多重要的行为,例如,协调视图在屏幕上的呈现,协调从屏幕上移除这些视图,响应于内存不足警告释放内存,以及响应于界面方向变化而旋转视图。规避这些行为可能导致您的应用程序行为不正确或以意外方式运行。

最小化自定义绘制

尽管有时需要自定义工程图,但您也应尽可能避免这样做。只有当现有系统视图类无法提供所需的外观或功能时,您才真正应该进行任何自定义绘图。每当您的内容可以与现有视图的组合一起组合时,最好的选择是将这些视图对象组合到自定义视图层次结构中。

利用内容模式

内容模式最大程度地减少了重绘视图所花费的时间。默认情况下,视图使用UIViewContentModeScaleToFill内容模式,该模式缩放视图的现有内容以适合视图的框架矩形。您可以根据需要更改此模式,以进行不同的调整,但是如果可以的话,应避免使用UIViewContentModeRedraw内容模式。无论采用哪种内容模式,都可以始终通过调用setNeedsDisplay或setNeedsDisplayInRect:来强制视图重绘其内容。

尽可能将视图声明为不透明

UIKit使用每个视图的opaque属性来确定该视图是否可以优化合成操作。对于自定义视图,将此属性的值设置为YES可以告诉UIKit,它不需要在视图后呈现任何内容。较少的渲染可以提高绘图代码的性能,因此通常会鼓励这样做。当然,如果将opaque属性设置为YES,则视图必须使用完全不透明的内容完全填充其边界矩形。

滚动时调整视图的绘图行为

滚动可以在短时间内引起大量视图更新。如果视图的绘图代码未正确调整,则视图的滚动性能可能会变慢。与其尝试确保您的视图内容始终保持原始状态,不如考虑在滚动操作开始时更改视图的行为。例如,您可以暂时降低渲染内容的质量,或者在滚动过程中更改内容模式。滚动停止时,您可以将视图返回到其先前的状态并根据需要更新内容。

不要通过嵌入子视图来自定义控件

尽管从技术上讲可以将子视图添加到标准系统控件(从UIControl继承的对象)中,但是您永远不应以这种方式对其进行自定义。支持自定义的控件通过控件类本身中的显式且文档齐全的接口来实现。例如,UIButton类包含用于设置按钮标题和背景图像的方法。使用定义的定制点意味着您的代码将始终正常工作。通过在按钮内嵌入自定义图像视图或标签来规避这些方法,可能会导致您的应用程序现在或将来某个按钮的实现发生变化时,表现不正确。

图1-2
图1-3
图1-4
图1-5
图1-6
图1-7