【转载】 原文地址 :http://blog.csdn.net/itianyi/article/details/8982518html
UIView是开发中使用得最多的控件了,深刻的理解颇有必要。ios
UIView是组成画面的基本元素, NS_CLASS_AVAILABLE_IOS(2_0)@interface UIView :UIResponder<NSCoding,UIAppearance, UIAppearanceContainer> {数组
@package缓存
CALayer *_layer;网络
id _tapInfo;架构
id _gestureInfo;app
NSMutableArray *_gestureRecognizers;框架
NSArray *_subviewCache;iphone
float _charge;异步
NSInteger _tag;
UIViewController *_viewDelegate;
NSString *_backgroundColorSystemColorName;
struct {
unsigned int userInteractionDisabled:1;
unsigned int implementsDrawRect:1;
unsigned int implementsDidScroll:1;
unsigned int implementsMouseTracking:1;
unsigned int hasBackgroundColor:1;
unsigned int isOpaque:1;
unsigned int becomeFirstResponderWhenCapable:1;
unsigned int interceptMouseEvent:1;
unsigned int deallocating:1;
unsigned int debugFlash:1;
unsigned int debugSkippedSetNeedsDisplay:1;
unsigned int debugScheduledDisplayIsRequired:1;
unsigned int isInAWindow:1;
unsigned int isAncestorOfFirstResponder:1;
unsigned int dontAutoresizeSubviews:1;
unsigned int autoresizeMask:6;
unsigned int patternBackground:1;
unsigned int fixedBackgroundPattern:1;
unsigned int dontAnimate:1;
unsigned int superLayerIsView:1;
unsigned int layerKitPatternDrawing:1;
unsigned int multipleTouchEnabled:1;
unsigned int exclusiveTouch:1;
unsigned int hasViewController:1;
unsigned int needsDidAppearOrDisappear:1;
unsigned int gesturesEnabled:1;
unsigned int deliversTouchesForGesturesToSuperview:1;
unsigned int chargeEnabled:1;
unsigned int skipsSubviewEnumeration:1;
unsigned int needsDisplayOnBoundsChange:1;
unsigned int hasTiledLayer:1;
unsigned int hasLargeContent:1;
unsigned int unused:1;
unsigned int traversalMark:1;
unsigned int appearanceIsInvalid:1;
unsigned int monitorsSubtree:1;
unsigned int layoutEngineIsOverridden:1;
unsigned int constraintsAreClean:1;
unsigned int subviewLayoutConstraintsAreClean:1;
unsigned int potentiallyHasDanglyConstraints:1;
unsigned int doesNotTranslateAutoresizingMaskIntoConstraints:1;
unsigned int autolayoutIsClean:1;
unsigned int subviewsAutolayoutIsClean:1;
unsigned int layoutFlushingDisabled:1;
unsigned int layingOutFromConstraints:1;
unsigned int wantsAutolayout:1;
unsigned int subviewWantsAutolayout:1;
unsigned int isApplyingValuesFromEngine:1;
unsigned int isInAutolayout:1;
unsigned int isUpdatingAutoresizingConstraints:1;
unsigned int isUpdatingConstraints:1;
unsigned int stayHiddenAwaitingReuse:1;
unsigned int stayHiddenAfterReuse:1;
unsigned int skippedLayoutWhileHiddenForReuse:1;
} _viewFlags;
}
+ (Class)layerClass; // default is [CALayer class]. Used when creating the underlying layer for the view.
- (id)initWithFrame:(CGRect)frame; // default initializer
@property(nonatomic,getter=isUserInteractionEnabled)BOOL userInteractionEnabled; // default is YES. if set to NO, user events (touch, keys) are ignored and removed from the event queue.
@property(nonatomic) NSInteger tag; // default is 0
@property(nonatomic,readonly,retain) CALayer *layer; // returns view's layer. Will always return a non-nil value. view is layer's delegate
@end
1,父类是UIResponder,说明全部的UIView都具备响应器的特性。
2,有一个对象 CALayer *_layer;
先看看定义:
@interface CALayer : NSObject <NSCoding, CAMediaTiming>
{
@private
struct _CALayerIvars {
int32_t refcount;
uint32_t magic;
void *layer;
#if TARGET_OS_MAC && !TARGET_RT_64_BIT
void *unused1[8];
#endif
} _attr;
}
UIView之因此可以显示出来,其实所有是靠CALayer的显示。
当UIView须要显示的时候,会调用drawRect:方法进行绘图,而且会将全部内容绘制在本身的层上(CALayer上),系统将层拷贝到屏幕上,完成UIView的显示。
经过操做这个CALayer对象,能够很方便的调整UIView的一些界面属性,好比:阴影,圆角大小,旋转等等。
viewT.layer.cornerRadius = 10; //设置半径 |
viewT.layer.borderWidth = 10; //设置边框的宽度,固然能够不要 |
viewT.layer.borderColor = [[UIColor redColor] CGColor]; //设置边框的颜色 |
viewT.layer.masksToBounds = YES ; //设为NO去试试 |
maskToBounds属性决定子图层是否相对于父图层裁剪
CALayer能够添加层,跟UIView同样,UIView能够添加子视图,那么怎么选择是食用UIView仍是UILayer来实现呢?
对比CALayer,UIView多了一个事件的处理功能,由于UIView是UIResponder的子类,若是有交互必须使用UIView,CALayer效率上很高。
3,UIView的setNeedsDisplay和setNeedsLayout方法
一、在Mac OS中NSWindow的父类是NSResponder,而在i OS 中UIWindow 的父类是UIVIew。程序通常只有一个窗口可是会又不少视图。
二、UIView的做用:描画和动画,视图负责对其所属的矩形区域描画、布局和子视图管理、事件处理、能够接收触摸事件、事件信息的载体、等等。
三、UIViewController 负责建立其管理的视图及在低内存的时候将他们从内存中移除。还为标准的系统行为进行响应。
四、layOutSubViews 能够在本身定制的视图中重载这个方法,用来调整子视图的尺寸和位置。
五、UIView的setNeedsDisplay和setNeedsLayout方法。首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样能够拿到UIGraphicsGetCurrentContext,就能够画画了。而setNeedsLayout会默认调用layoutSubViews,就能够处理子视图中的一些数据。
综上所述:setNeedsDisplay方便绘图,而layoutSubViews方便出来数据
setNeedDisplay告知视图它发生了改变,须要从新绘制自身,就至关于刷新界面.
4,initWithFram:(CGRect)frame方法用来初始化一个UIView,传入CGRect当参数
5,能够看出UIView常用的属性没有定义在类里面,那确定是定义在类别里面了:
@interface UIView(UIViewGeometry)
@property(nonatomic)CGRect frame;
@property(nonatomic)CGRect bounds; // default bounds is zero origin, frame size. animatable
@property(nonatomic)CGPoint center;
。。。
@end
能够看到UIView经常使用的属性在这个类别里面。
注意:虽然ios6.0的时候增长了一个新功能,若是在类定义的时候声明属性以后,会自动生成一个如下划线开头的成员变量,可是对于类别是无效的。
类别只能添加方法,不能添加成员变量的。因此这里添加的属性只不过是UIView的getter/setter方法而已,并无_frame或者_bounds这种成员变量。
只有view.frame 相似的点方法。返回的是一个CGRect的结构体
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
这个结构体的值多是存放在CALayer对象的的void *unused1[8];指针数组中的某个指针指向的数据里。(仅猜想)
6,UIView一些经常使用方法:
@interface UIView(UIViewHierarchy)
@property(nonatomic,readonly)UIView *superview;
@property(nonatomic,readonly,copy)NSArray *subviews;
@property(nonatomic,readonly)UIWindow *window;
- (void)addSubview:(UIView *)view;
- (void)removeFromSuperview;
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;
- (void)bringSubviewToFront:(UIView *)view;
- (void)sendSubviewToBack:(UIView *)view;
- (void)didAddSubview:(UIView *)subview;
- (void)willRemoveSubview:(UIView *)subview;
- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
- (UIView *)viewWithTag:(NSInteger)tag; // recursive search. includes self
// Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
- (void)layoutSubviews;
@end
@interface UIView(UIViewRendering)
- (void)drawRect:(CGRect)rect;
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)rect;
@property(nonatomic,getter=isHidden)BOOL hidden;
@property(nonatomic) BOOL clipsToBounds; // When YES, content and subviews are clipped to the bounds of the view. Default is NO.
@property(nonatomic,copy) UIColor *backgroundColor; // default is nil
@property(nonatomic) CGFloat alpha;
@property(nonatomic) CGRect contentStretch
@end
@interface UIView(UIViewAnimation) //动画
+ (void)beginAnimations:(NSString *)animationID context:(void *)context; // additional context info passed to will start/did stop selectors. begin/commit can be nested
+ (void)commitAnimations;
+ (void)setAnimationDuration:(NSTimeInterval)duration; // default = 0.2
+ (void)setAnimationDelay:(NSTimeInterval)delay;
@end
@interface UIView(UIViewAnimationWithBlocks)
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completionNS_AVAILABLE_IOS(4_0);
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completionNS_AVAILABLE_IOS(4_0);// delay = 0.0, options = 0
@end
@interface UIView (UIViewGestureRecognizers)//为UIView添加手势
@property(nonatomic,copy) NSArray *gestureRecognizersNS_AVAILABLE_IOS(3_2);
- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizerNS_AVAILABLE_IOS(3_2);
- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizerNS_AVAILABLE_IOS(3_2);
@end
只是摘录了一部分经常使用的。
如下内容 为官方文档翻译(来源于网络),若是你英文还能够,请看英文地址:英文地址
视图和窗口展现了应用的用户界面,同时负责界面的交互。UIKit和其余系统框架提供了不少视图,你能够就地使用而几乎不须要修改。当你须要展现的内容与标准视图容许的有很大的差异时,你也能够定义本身的视图。
无论你是使用系统的视图仍是建立本身的视图,你须要理解UIView和UIWindow类所提供的基本结构。这些类提供了复杂的方法来管理视图的布局和展现。理解这些方法的工做很是重要,使你在应用发生改变时能够确认视图有合适的行为。
视图架构 fundamentals
大部分你想要可视化操做都是由视图对象-即UIView 类的实例-来进行的。一个视图对象定义了一个屏幕上的一个矩形区域,同时处理该区域的绘制和触屏事件。一个视图也能够做为其余视图的父视图,同时决定着这 些子视图的位置和大小。UIView类作了大量的工做去管理这些内部视图的关系,可是须要的时候你也能够定制默认的行为。
视图与Core Animation层联合起来处理着视图内容的解释和动画过渡。每一个UIKit框架里的视图都被一个层对象支持(一般是一个CALayer类的实例),它 管理管理着后台的视图存储和处理视图相关的动画。然而,当你须要对视图的解释和动画行为有更多的控制权时,你可使用层。
为了理解视图和层之间的关系,咱们能够借助于一些例子。 图1-1显示了ViewTransitions样例程序的视图层次及其对底层Core Animation层的关系。应用中的视图包括了一个window(同时也是一个视图),一个通用的表现得像一个容器视图的UIView对象,一个图像视 图,一个控制显示用的工具条,和一个工具条按钮(它自己不是一个视图可是在内部管理着一个视图)。(注意这个应用包含了一个额外的图像视图,它是用来实现 动画的)。为了简化,同时由于这个视图一般是被隐藏的,因此没把它包含在下面的图中。每一个视图都有一个相应的层对象,它能够经过视图礶r属性被访问。(因 为工具条按钮不是一个视图,你不能直接访问它的层对象。)在它们的层对象以后是Core Animation的解释对象,最后是用来管理屏幕上的位的硬件缓存。
Figure 1-1 View architecture
使用Core Animation的层对象有很重要的性能意义。一个视图对象的绘制代码须要尽可能的少被调用,当它被调用时,其绘制结果会被Core Animation缓存起来并在日后能够被尽量的重用。重用已经解释过的内容消除了一般须要更新视图的开销昂贵的绘制周期。内容的重用在动画中特别重 要,咱们可使用已有的内容,这样比建立新的内容开销更小。
视图层次和子视图管理
除了提供本身的内容以外,一个视图也能够表现得像一个容器。当一个视图包含其余视图时,就在两个视图之间建立了一个父子关系。在这个关系中孩子视图被看成子视图,父视图被看成超视图。建立这样一个关系对应用的可视化和行为都有重要的意义。
在视觉上,子视图隐藏了父视图的内容。若是子视图是彻底 不透明的,那么子视图所占据的区域就彻底的隐藏了父视图的相应区域。若是子视图是部分透明的,那么两个视图在显示在屏幕上以前就混合在一块儿了。每一个父视图 都用一个有序的数组存储着它的子视图,存储的顺序会影响到每一个子视图的显示效果。若是两个兄弟子视图重叠在一块儿,后来被加入的那个(或者说是排在子视图数 组后面的那个)出如今另外一个上面。
父子视图关系也影响着一些视图行为。改变父视图的尺寸会连带着改变子视图的尺寸和位置。在这种状况下,你能够经过合适的配置视图来重定义子视图的尺寸。其余会影响到子视图的改变包括隐藏父视图,改变父视图的alpha值,或者转换父视图。
视图层次的安排也会决定着应用如何去响应事件。在一个具 体的视图内部发生的触摸事件一般会被直接发送到该视图去处理。然而,若是该视图没有处理,它会将该事件传递给它的父视图,在响应者链中以此类推。具体视图 可能也会传递事件给一个干预响应者对象,像视图控制器。若是没有对象处理这个事件,它最终会到达应用对象,此时一般就被丢弃了。
获取更多关于如何建立视图层次,查看 creating and managing a view hierarchy
视图绘制周期
UIView类使用一个点播绘制模型来展现内容。当一个 视图第一次出如今屏幕前,系统会要求它绘制本身的内容。在该流程中,系统会建立一个快照,这个快照是出如今屏幕中的视图内容的可见部分。若是你历来没有改 变视图的内容,这个视图的绘制代码可能永远不会再被调用。这个快照图像在大部分涉及到视图的操做中被重用。
若是你确实改变了视图内容,也不会直接的从新绘制视图内 容。相反,使用setNeedsDisplay或者setNeedsDisplayInRect:方法废止该视图,同时让系统在稍候重画内容。系统等待当 前运行循环结束,而后开始绘制操做。这个延迟给了你一个机会来废止多个视图,从你的层次中增长或者删除视图,隐藏,重设大小和重定位视图。全部你作的改变 会稍候在同一时间反应。
注意:改变一个视图的几何结构不会自动引发系统重画内 容。视图的contentMode属性决定了改变几何结构应该若是解释。大部份内容模式在视图的边界内拉伸或者重定位了已有快照,它不会从新建立一个新的 快照。获取更多关于内容模式若是影响视图的绘制周期,查看 content modes
当绘制视图内容的时候到了时,真正的绘制流程会根据视图 及其配置改变。系统视图一般会实现私有的绘制方法来解释它们的视图,(那些相同的系统视图常常开发接口,好让你能够用来配置视图的真正表现。)对于定制的 UIView子类,你一般能够覆盖drawRect:方法并使用该方法来绘制你的视图内容。也有其余方法来提供视图内容,像直接在底部的层设置内容,可是 覆盖drawRect:时最通用的技术
内容模式
视图的内容模式控制着视图如何回收内容来响应视图几何结 构的变化,也控制着是否须要回收内容。当一个视图第一次显示时,它一般会解释内容,其结果会被底层的层级树捕获为一张位图。在那以后,改变视图的几何结构 不会致使从新建立位图。相反,视图中contentMode属性的值决定着这张位图是否该被拉伸,以适应新的边界或者只是简单的被放到角落或者视图的边 界。
视图的内容模式在你进行以下操做时被应用:
改变视图frame或者bounds矩形的宽度或者高度时。
赋值给视图的transform属性,新的转换包括一个放缩因子。
大部分视图的contentMode值是 UIViewContentModeScaleToFiill,它使视图的内容被放缩到适合新框架的值。Figure 1-2展现了使用其余可用的内容模式的结果。正如你在图中所看到的那样,不是全部的内容模式均可以填充视图的范围,能够的模式可能会扭曲内容。
内容模式很好的支持了视图的内容回收,可是当你想视图在 放缩和重设尺寸的操做中重绘你也能够用UIViewContentModeRedraw内容模式。设置这个值绘强制系统调用视图的drawRect:方法 来响应几何结构的变化。一般来说,你应该尽量的避免使用这个模式,同时你不该该在标准的系统视图中使用这个模式。
获取更多骨干与可用的内容模式,查看UIView Class Reference
Figure 1-2
拉伸视图
你能够指定视图的某部分为可拉伸的,以便当视图的尺寸改 变时只有可拉伸的部分被影响到。可拉伸的部分一般给按钮或者其余的部分为重复模式的视图。由你指定的可拉伸区域容许沿着两条或者其中一条轴拉伸。固然,当 一个视图沿着两条轴拉伸的时候,视图的边界必须也定义了一个重复的模式来避免任何的扭曲。Figure1-3展现了这种扭曲在视图里是怎么表现本身的。每 个视图里的原始像素的颜色都自我复制,以即可以填充更大视图的相应区域。
Figure 1-3 拉伸一个按钮的背景
你能够用contentStretch属性来定义一个视 图的可拉伸区域。这个属性的值一个边的值被标准化为0.0到1.0之间的矩形。当拉伸这个视图时,系统将视图的当前边界值和放缩因子乘以标准值,以便决定 哪些像素须要被拉伸。使用标准值能够减轻每次改变视图的边界值都更新contentStretch属性的须要。
视图的内容模式也在决定如何视图的可拉伸区域的使用中扮 演着重要的角色。只有当内容模式可能绘引发视图内容放缩的时候可拉伸区域才会被使用。这意味这你的可拉伸视图只被 UIViewContentModeScaleToFill, UIViewContentModeScaleAspectFit和UIViewContentModeScaleAspectFill内容模式。若是你 指定了一个将内容弹到边界或者角落的内容模式(这样就没有真正的放缩内容),这个视图会忽视可拉伸区域。
注意:当须要建立一个可拉伸UIImage对象做为视图的背景时,使用contentStretch属性是推荐的。可拉伸视图彻底被Core Animation层处理,这样性能一般更好。
嵌入式动画支持
使用层对象来支持视图的其中一个利益是你能够轻松的用动 画处理视图相关的改变。动画是与用户进行信息交流的一个有用的方法,并且应该老是在进行应用设计的过程当中考虑使用动画。UIView类的不少属性是动画化 的-也就是,能够半自动的从一个值动画的变化到另外一个值。为了实现这样一个动画,你须要作的只是:
1 告诉UIKit你想要实现一个动画
2 改变这个属性的值
在一个UIView对象中有如下的动画化属性:
frame - 你可使用这个来动画的改变视图的尺寸和位置
bounds - 使用这个能够动画的改变视图的尺寸
center - 使用这个能够动画的改变视图的位置
transform - 使用这个能够翻转或者放缩视图
alpha - 使用这个能够改变视图的透明度
backgroundColor - 使用这个能够改变视图的背景颜色
contentStretch - 使用这个能够改变视图内容如何拉伸
动画的一个很重要的地方是用于从一组视图到另外一组视图的 过渡。一般来讲,会用一个视图控制器来管理关系到用户界面的主要变动的动画。例如,涉及到从高层到底层信息的导航的界面,一般会使用一个导航控制器来管理 视图的过渡,这些视图显示了数据的每个连续层面。然而,你也可使用动画来建立两组视图的过渡,而不是视图控制器。当你想用一个系统提供的视图控制器无 法支持的导航方案时你可能会这样作。
除了用UIKit类能够建立动画外,你也能够用Core Animation层来建立动画。在更低层你有更多的在时间或者动画属性上的控制权。
获取更多关于如何建立一个基于视图的动画,查看 Animations
获取更多关于使用Core Animation建立动画的信息,查看Core Animation Programming Guide和Core Animation Cookbook.
视图几何结构和坐标系统
UIKit的默认坐标系统把原点设置在左上角,两条轴往 下和右扩展。作标志被表示为浮点数,这样容许内容的精确布局和定位而无论底层的屏幕。Figure1-4展现了相对于屏幕的坐标系统。除了屏幕坐标系统窗 口和视图也定义了它们本身的本地坐标系统,这样容许你指定相对于视图或者窗口原点的坐标而不是屏幕。
Figure 1-4 UIKit中的坐标系统
由于每一个视图和窗口都定义了它本身的本地坐标系统,你需 要留意在任什么时候间内是哪一个坐标系统在起做用。每次绘制或者改变一个视图都是基于一个坐标系统的。在某些绘制中会基于视图自己的坐标系统。在某些几何结构变 更中是基于父视图的坐标系统的。UIWindow和UIView类都包含了帮助你从一个坐标系统转换到另外一个的方法。
重要:一些iOS技术定义了默认的坐标系统,它们的原点和方向与UIKit的不一样。;例如,Core Graphics和OpenGL ES的坐标系统是原点在可视区域的左下角,而y轴往上递增。当绘制或者建立内容时,你的代码应该考虑到一些不一样而且适应坐标值。
frame, bounds和center属性之间的关系
视图对象使用frame, bounds和center属性来跟踪它的尺寸和位置:
frame属性包含了frame矩形,指定了在父视图坐标系统中该视图的尺寸和位置。
center属性包含了在父视图坐标系统中的已知中心点。
bounds属性包含了边界矩形,指定了在视图本地坐标系统中视图的尺寸。
主要使用center和frame属性来控制当前视图的 几何结构。例如,当在运行时构建你的视图层次或者改变视图的尺寸或者位置时你可使用这些属性。若是你只是要改变视图的位置,那么推荐使用center属 性。center属性的值永远是可用的,即便添加了放缩或者转换因子到视图的转换矩阵当中。可是对于frame属性却不是,当视图的转换矩形不等于原始矩 阵时它被看成时无效的。
在绘制的过程当中主要使用bounds属性。这个边界矩阵 在视图的本地坐标系统被解释。这个矩形的默认原点是(0, 0),它的尺寸也适应frame矩形的尺寸。任何绘制在这个矩形当中的东西都是该视图的可视内容的一部分。若是你改变了bounds矩形的原点,任何你绘 制在新矩形的东西都会变成该视图可视内容的一部分。
Figure1-5展现了一个图像视图的frame和bounds矩形之间的关系。图中,图像视图的右上角被定位在父视图坐标系统的(40, 40),它的矩形尺寸为240x380。对于bounds矩形,原点是(0, 0),矩形尺寸也是240x380。
Figure 1-5 视图frame和bounds之间的关系
即便你能够独立的改变frame,bounds和center属性,其中一个改变仍是会影响到另外两个属性:
当你设置了frame属性,bounds属性的尺寸值也改变来适应frame矩形的新尺寸。center属性也会改变为新frame矩形的中心值。
当你设置了center属性,frame的原点也会相应的改变。
当你设置了bounds属性,frame属性会改变以适应bounds矩形的新尺寸。
视图的框架默认不会被它的父视图框架裁剪。这样的化,任 何放置在父视图外的子视图都会被完整的解释。你能够改变这种行为,改变父视图的clipsToBounds属性就能够。无论子视图是否在视觉上被裁剪,触 屏事件老是发生在目标视图父视图的bounds矩形。换句话说,若是触摸位于父视图外的那部分视图,那么该事件不会被发送到该视图。
坐标系统转换矩阵
坐标系统转换矩阵给改变视图(或者是它的视图)提供了一 个轻松和简易的方法。一个仿射转换是一个数学矩阵,它指定了在坐标系统中的点是怎么被映射到另外一个坐标系统中的点。你能够对整个视图应用仿射转换,以基于 其父视图来改变视图的尺寸,位置或者朝向。你也能够在你的绘制代码中应用仿射转换,以对已解释内容的独立部分实现相同的操控。如何应用仿射转换是基于这样 的上下文的:
为了修改整个视图,能够修改视图transform属性的仿射转换值。
为了在视图中的drawRect:方法中修改内容的指定部分,能够修改与当前图形上下文相关的仿射转换。
当你想实现动画时,一般能够修改视图的 transform属性值。例如,你可使用这个属性来制做一个视图围绕中心点翻转的动画。你不该该在其父视图的坐标空间中用这个属性来永久的改变你的视 图,像修改它的位置和尺寸。对于这种类型的改变,你能够修改视图的frame矩形。
注意:当修改视图的transform属性值时,全部的转换都是基于视图的中心点来实现的。
在视图的drawRect:方法中,你可使用仿射转换 来定位或者翻转你想要绘制的项目。相对于在视图某些部位中修正对象的位置,咱们更倾向于相对于一个固定点去建立对象,一般是(0, 0),同时在绘制以前使用转换来定位对象。这样的话,若是在视图中对象的位置改变了,你要作的只是修改转换矩阵,这样比为对象从新建立新的位置性能更好开 销更低。你能够经过使用CGContextGetCTM方法来获取关于图形上下文的仿射转换,同时能够用Core Graphics的相关方法在绘制中来设置或者修改这个转换矩阵。
当前转换矩阵(CTM)是一个在任什么时候候都被使用的仿射矩阵。当操控整个视图的几何结构时,CTM就是视图transform属性的值。在drawRect:方法中,CTM是关于图形上下文的仿射矩阵。
每一个子视图的坐标系统都是构建在其祖先的坐标系统之上 的。因此当你修改一个视图的transform属性,这个改变会影响到视图及其全部的子视图。然而,这些改变只会影响到屏幕上视图的最终解释。由于每一个视 图都负责绘制本身的内容和对本身的子视图进行布局,因此在绘制和布局的过程当中它能够忽略父视图的转换。
Figure1- 6描述了在解释的时候,两个不一样的转换因子是如何在视觉上组合起来的。在视图的drawRect:方法中,对一个形状应用一个45度的转换因子会使该形状 翻转指定的角度。另外加上一个45度的转换因子会致使整个形状翻转90度。这个形状对于绘制它的视图来说仍然只是翻转了45度,可是视图本身的转换让它看 起来像使翻转了90度。
Figure 1-6 翻转一个视图和它的内容
重要:若是一个视图的transform属性不是其定义 时转换矩阵,那么视图的frame属性是未定义的并且必须被忽略。当对视图应用转换时,你必须使用视图的bounds和center属性来获取视图的位置 和尺寸。子视图的frame矩形仍然是有效的,由于它们与视图的bounds相关。
获取更多关于在运行时修改视图的transform属 性,查看 “Translating, Scaling, and Rotating Views.”获取更多如何在绘制过程当中使用转换来定位内容,查看 Drawing and Printing Guide for iOS.
点与像素
在iOS中,全部的坐标值和距离都被指定为使用浮点数,其单元值称为点。点的数量随着设备的不一样而不一样,并且彼此不相关。要明白关于点的最主要一点是它们提供了一个绘制用的固定框架。
Table 1-1 列出了不一样iOS设备的分辨率(点度量)。前为宽后为长。只要你依照这些屏幕的尺寸来设计用户界面,你的视图就回被相应的设备正确显示。
Table 1-1
Device Screen dimensions (in points)
iPhone and iPod touch 320 x 480
iPad 768 x 1024
每一种使用基于点度量系统的设备都定义了一个用户坐标空 间。这是几乎在你全部的代码都会用到的标准坐标空间。例如,当你要操控视图的几何结构或者调用Core Graphics方法来绘制内容时会用到点和用户坐标空间。即便有时用户坐标空间里的坐标时直接映射到设备屏幕的像素,你仍是永远不该该假设这是永远不变 的。相反,你应该记住:一个点并不必定对应着屏幕上的一个像素
在设备层面,全部由你指定的视图上的坐标在某些点上必须 被转化成像素。然而,从用户坐标空间上的点到设备坐标空间上的像素一般由系统来处理。UIKit和Core Graphics都主要使用基于向量的绘制模型,全部的坐标值都被指定为使用点。这样,若是你用Core Graphics画了一条曲线,你会用一些值来指定这条曲线,而无论底层屏幕使用怎样的解决方法。
当你须要处理图像或者其余基于像素的技术,像 OpenGL ES时,iOS会帮你管理这些像素。对于存储为应用程序的束中的资源的静态图像文件,iOS定义了一些约定,能够指定不一样像素密度的图像,也能够在加载图 像时最大限度的适应当前屏幕的解决方案。视图也提供了关于当前放缩因子的信息,以便你能够适当的调整任何基于像素的绘制代码来适应有更高级解决方案的屏 幕。在不一样屏幕的解决方案中处理基于像素内容的技术能够在"Supporting High-Resolution Screens"和"Drawing and Printing Guide for iOS"找到描述。
视图的运行时交互模型
当用户和界面进行交互时,或者由代码程序性的改变一些东 西时,一系列复杂的事件就会发生在UIKit的内部来处理这些交互。在这个系列中的某些点,UIKit唤出你的视图类,同时给它们一个机会去响应程序的行 为。理解这些唤出点对于理解视图在哪里融入系统很重要。Figure 1-7 展现了这些事件的基本序列,从用户触屏开始到图形系统更新屏幕内容来响应结束。一样的事件序列也会发生在任何程序性启动的动做。
Figure 1-7 UIKit 与视图对象进行交互
如下的步骤分解了图1-7中的事件序列,既解释了在每一步发生了什么,也解释了应用如何响应
1 用户触屏
2 硬件报告触摸事件给UIKit框架
3 UIKit框架将触摸事件打包成UIEvent对象,同时分发给适合的视图。(对于UIKit框架如何提交事件给视图的详细解释,查看 Event Handing Guide for iOS)
4 视图中的事件处理代码可能进行如下的动做来响应:
改变视图或者其子视图的属性(frame, bounds, alpha, 等等)
调用setNeedsLayout方法以标记该视图(或者它的子视图)为须要进行布局更新
调用setNeedsDisplay或者setNeedsDisplayInRect:方法以标记该视图(或者它的子视图)须要进行重画
通知一个控制器关于一些数据的更新
固然,哪些事情要作,哪些方法要被调用是由视图来决定的。
5 若是一个视图的几何结构改变了,UIKit会根据如下几条规则来更新它的子视图:
a 若是自动重设尺寸的规则在发生做用,UIKit会根据这些规则来调整视图。获取更多关于自动重设尺寸规则如何工做,查看"Handling Layout Changes Automatically Using Autoresizing Rules."
b 若是视图实现了layoutSubviews方法,UIKit会调用它。你能够在你的定制视图中覆盖这个方法同时用它来调整任何子视图的位置和大小。例 如,一个提供了巨大滚动区域的视图会须要使用几个子视图做为“瓦块”而不是建立一个不太可能放进内存的巨大视图。在这个方法的实现中,视图会隐藏任何屏幕 外的子视图,或者重定位它们而后用来绘制新的可视内容。做为这个流程的一部分,视图的布局代码也能够废止任何须要被重画的视图。
6 若是任何视图的任何部分被标记为须要重画,UIKit会要求视图重画自身。
对于显式的定义了drawRect:方法的定制视图,UIKit会调用这个方法。这方法的实现应该尽快重画视图的指定区域,而且不该该再作其余事。不要在这个点上作额外的布局,也不要改变应用的数据模型。提供这个方法仅仅是为了更新视图的可视内容。
标准的系统视图一般不会实现drawRect:方法,可是也会在这个时候管理它们的绘制。
7 任何已经更新的视图会与应用余下的可视内容组合在一块儿,同时被发送到图形硬件去显示。
8 图形硬件将已解释内容转化到屏幕上。
注意:上面的更新模型主要应用于使用标准系统视图和绘制 技术的应用。使用OpenGL ES来绘制的应用一般会配置一个单一的全屏视图和直接绘制相关的OpenGL图像上下文。你的视图仍是应该处理触屏事件,可是它是全屏的,毋需给子视图布 局或者实现drawRect:方法。获取更多关于使用OpenGL ES的信息,查看 OpenGL ES Programming Guide for iOS.
给定以前的一系列步骤,将本身的定制视图整合进去的方法包括:
事件处理方法:
touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
touchesCancelled:withEvent:
layoutSubviews方法
drawRect:方法
这些是视图的最经常使用的覆盖方法,可是你可能不须要覆盖全 部。若是你使用手势识别来处理事件,你不须要覆盖事件处理方法。类似的,若是你的视图没有包含子视图或者它的尺寸不会改变,那就没有理由去覆盖 layoutSubviews方法。最后,只有当视图内容会在运行时改变,同时你要用UIKit或者Core Graphics等本地技术来绘制时才须要用到drawRect。
要记住这些是主要的整合点,可是不只仅只有这些。UIView类中有些方法是专门设计来给子类覆盖的。你应该到UIView Class Reference中查看这些方法的描述,以便在定制时清楚哪些方法适合给你覆盖。
有效使用视图的提示
当你须要绘制一些标准系统视图不能提供的内容时,定制视图是颇有用的。可是你要负责保证视图的性能要足够的高。UIKit会尽量的优化视图相关的行为,也会帮助你提升性能。然而,考虑一些提示能够帮助到UIKit。
重要:在调整绘制代码以前,你应该一直收集与你视图当前性能有关的数据。估量当前性能让你能够肯定是否真的有问题,同时若是真的有问题,它也提供一个基线,让你在将来的优化中能够比较。
视图不会老是有一个相应的视图控制器
在应用中,视图和视图控制器之间的一对一关系是不多见的。视图控制器的工做是管理一个视图层次,而视图层次常常是包含了多个视图,它们都有自包含特性。对于iPhone应用,每一个视图层次一般都填满了整个屏幕,尽管对于iPad应用来讲不是。
当你设计用户界面的时候,考虑到视图控制器的所扮演的角色是很重要的。视图控制器提供了不少重要的行为,像协调视图的展现,协调视图的剔除,释放内存以响应低内存警告,还有翻转视图以响应界面的方向变动。逃避这些行为会致使应用发生错误。
获取更多关于视图控制器的信息,查看 View Controller Programming Guide for iOS
最小化定制的绘画
尽管定制的绘画有时是须要的,可是你也应该尽可能避免它。真正须要定制绘画的时候是已有的视图类没法提供足够的表现和能力时。任什么时候候你的内容都应该能够被组装到其余视图,最好结果时组合那些视图对象到定制的视图层次
利用内容模式
内 容模式能够最小化重画视图要花费的时间。默认的,视图使用UIViewContentModeScaleToFill 内容模式,这个模式会放缩视图的已有内容来填充视图的frame矩形。须要时你能够改变这个模式来调整你的内容,可是应该避免使用 UIViewContentModeRedraw内容模式。无论哪一个内容模式发生做用,你均可以调用setNeedsDisplay或者 setNeedsDisplayInRect:方法来强制视图重画它的内容。
可能的话将视图声明为不透明
UIKit使用opaque属性来决定它是否能够优化组 合操做。将一个定制视图的这个属性设置为YES会告诉UIKit不须要解释任何在该视图后的内容。这样能够为你的绘制代码提升性能而且是推荐的。固然,如 果你将这个属性设置为YES,你的视图必定要用不透明的内容彻底填充它的bounds矩形。
滚动时调整视图的绘制行为
滚动会致使数个视图在短期内更新。若是视图的绘制代码 没有被适当的调整,滚动的性能会很是的缓慢。相对于老是保证视图内容的平庸,咱们更倾向于考虑滚动操做开始时改变视图行为。例如,你能够暂时减小已解释的 内容,或者在滚动的时候改变内容模式。当滚动中止时,你能够将视图返回到前一状态,同时须要时更新内容。
不要嵌入子视图来定制控制
尽管在技术上增长子视图到标准系统控制对象-继承自 UIControl的类-是可行的,你仍是永远不该该用这种方法来定制它们。控制对象支持定制,它们有显式而且良好归档的接口。例如,UIButton类 包含了设置标题和背景图片的方法。使用已定义好的定制点意味着你的代码老是会正确的工做。不用这些方法,而嵌入一个定制的图像视图或者标签到按钮中去会导 致应用出现未预期的结果。