你真的了解UIView吗?
一:首先查看一下关于UIView的定义
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace> + (Class)layerClass; // 默认为 [CALayer class].用于创建视图的底层时使用。 - (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; //初始化视图
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; @property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // 默认是是。如果设置为NO,用户事件(触摸,键)将被忽略,并从事件队列中删除。
@property(nonatomic) NSInteger tag; // 默认为 0
@property(nonatomic,readonly,strong) CALayer *layer; // 返回视图层。将始终返回一个非零值。视图是层的委托. + (UIUserInterfaceLayoutDirection)userInterfaceLayoutDirectionForSemanticContentAttribute:(UISemanticContentAttribute)attribute NS_AVAILABLE_IOS(9_0); //用户界面的布局方向 IOS9以后的功能
@property (nonatomic) UISemanticContentAttribute semanticContentAttribute NS_AVAILABLE_IOS(9_0); //UIView 也增加了 UISemanticContentAttribute 这样一个属性来判断视图是否会遵循显示的方向規則(默认是 UISemanticContentAttributeUnspecified )
@end
从上面我们可以知道,UIView是继承于UIResponder,并且有相应的委托协议NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace;关于UIResponder的知识点我们放在下一个文章进行学习,首先了解一下几个协议的作用:
a:NSCoding 实现了NSCoding协议以后,就可以进行归档转换了,这个协议在平常的归档类中是必须要遵循了;
b:UIAppearance+UIAppearanceContainer iOS5及其以后提供了一个比较强大的工具UIAppearance,我们通过UIAppearance设置一些UI的全局效果,这样就可以很方便的实现UI的自定义效果又能最简单的实现统一界面风格;改变UIAppearance协议对象的属性,就能改变所有其遵从该协议类的所有实例,iOS应用的appearance只会对当视图加入到窗口时有效,对于已经添加在窗口的视图不起作用,需要通过移除其所在的视图层次位置,再添加回来.
c:UIDynamicItem 遵守了UIDynamicItem协议,能做物理仿真效果
d:UITraitEnvironment 该协议用于监听和获取SizeClass的情况
e:UICoordinateSpace 获取当前screen旋转之后的坐标体系,有时候需要在 Core Graphics 和 UIKit 的坐标系之间进行转换,就要遵循这个协议
二:关于UIView几个重要的分类
a: UIView(UIViewGeometry) 这个里面的内容也是我们经常要用到,包含一些相应的坐标内容及相应的触分事件;
@interface UIView(UIViewGeometry) @property(nonatomic) CGRect frame; //视图在父视图中的尺寸和位置(以父控件的左上角为坐标原点) @property(nonatomic) CGRect bounds; // 控件所在矩形框的位置和尺寸(以自己左上角为坐标原点,所以bounds的x\y一般为0)而宽高则为frame size @property(nonatomic) CGPoint center; // 中心点的坐标,控件中点的位置(以父控件的左上角为坐标原点)
@property(nonatomic) CGAffineTransform transform; // 仿射变换(通过这个属性可以进行视图的平移、旋转和缩放) @property(nonatomic) CGFloat contentScaleFactor NS_AVAILABLE_IOS(4_0); //内容视图伸张的模式,修改contentScaleFactor可以让UIView的渲染精度提高,这样即使在CGAffineTransform放大之后仍然能保持锐利 @property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled; // 默认是NO 是否允许多点触摸 @property(nonatomic,getter=isExclusiveTouch) BOOL exclusiveTouch; // 默认是NO 可以达到同一界面上多个控件接受事件时的排他性,从而避免一些问题。也就是说避免在一个界面上同时点击多个button //这两个方法主要是进行用户事件的拦截
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // 去调用pointInside:withEvent:. 点在接收机的坐标系统中
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // 如何在范围内则返回YES // 视图中的坐标转换 // 将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值
- (CGPoint)convertPoint:(CGPoint)point toView:(nullable UIView *)view;
// 将像素point从view中转换到当前视图中,返回在当前视图中的像素值
- (CGPoint)convertPoint:(CGPoint)point fromView:(nullable UIView *)view;
// 将rect由rect所在视图转换到目标视图view中,返回在目标视图view中的rect
- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
// 将rect从view中转换到当前视图中,返回在当前视图中的rect
- (CGRect)convertRect:(CGRect)rect fromView:(nullable UIView *)view; //自动尺寸调整属性
@property(nonatomic) BOOL autoresizesSubviews; // 默认为YES. if set, subviews are adjusted according to their autoresizingMask if self.bounds changes
@property(nonatomic) UIViewAutoresizing autoresizingMask; // 默认值为: UIViewAutoresizingNone - (CGSize)sizeThatFits:(CGSize)size; // 返回“最佳”的大小,以适应给定的大小。不实际调整视图。默认是返回现有视图大小
- (void)sizeToFit; // 随着当前视图边界和更改边界大小,调用这个方法实际上是调用 sizeThatFits 方法。 @end
知识点1:frame: 以父视图为原点;bounds : 以自身为原点;center : 以父视图为原点。
知识点2:autoresizesSubviews属性的大意是:默认autoresizesSubviews = YES。如果UIView设置了autoresizesSubviews,那么他的子控件的bounds如果发生了变化,他的子控件将会根据子控件自己的autoresizingMask属性的值来进行调整。
知识点3:autoresizingMask是一个枚举值,作用是自动调整子控件与父控件中间的margin(间距)或者子控件的宽高。默认其枚举值是UIViewAutoresizingNone。如下是其全部的枚举值;
UIViewAutoresizingNone就是不自动调整。
UIViewAutoresizingFlexibleLeftMargin 自动调整与superView左边的距离,保证与superView右边的距离不变。
UIViewAutoresizingFlexibleRightMargin 自动调整与superView的右边距离,保证与superView左边的距离不变。
UIViewAutoresizingFlexibleTopMargin 自动调整与superView顶部的距离,保证与superView底部的距离不变。
UIViewAutoresizingFlexibleBottomMargin 自动调整与superView底部的距离,也就是说,与superView顶部的距离不变。
UIViewAutoresizingFlexibleWidth 自动调整自己的宽度,保证与superView左边和右边的距离不变。
UIViewAutoresizingFlexibleHeight 自动调整自己的高度,保证与superView顶部和底部的距离不变。
例如:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin 自动调整与superView左边的距离,保证与左边的距离和右边的距离和原来距左边和右边的距离的比例不变。比如原来距离为20,40,调整后的距离应为50,100,即50/20=100/40;UIView的autoresizesSubviews属性为YES时(默认为YES),autoresizing才会生效。如果视图的autoresizesSubviews属性被设置为 NO,则该视图的直接子视图的所有自动尺寸调整行为将被忽略。类似地,如果一个子视图的自动尺寸调整掩码被设置为 UIViewAutoresizingNone,则该子视图的尺寸将不会被调整,因而其直接子视图的尺寸也不会被调整。
知识点4:iOS中,hit-Testing的作用就是找出这个触摸点下面的View是什么,HitTest会检测这个点击的点是不是发生在这个View上,如果是的话,就会去遍历这个View的subviews,直到找到最小的能够处理事件的view,如果整了一圈没找到能够处理的view,则返回自身。UIView中提供两个方法用来确定hit-testing View分别为hitTest,pointInside;
知识点5:当一个View收到hitTest消息时,会调用自己的pointInside:withEvent:方法,如果pointInside返回YES,则表明触摸事件发生在我自己内部,则会遍历自己的所有Subview去寻找最小单位(没有任何子view)的UIView,如果当前View.userInteractionEnabled = NO,enabled=NO(UIControl),或者alpha<=0.01, hidden等情况的时候,hitTest就不会调用自己的pointInside了,直接返回nil,然后系统就回去遍历兄弟节点。【关于事件分发可以看这文章:iOS事件分发机制】
实例:我们可以利用hit-Test做一些事情,比如我们点击了ViewA,我们想让ViewB响应 @implementation STPView - (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(, , CGRectGetWidth(frame), CGRectGetHeight(frame) / );
button.tag = ;
button.backgroundColor = [UIColor grayColor];
[button setTitle:@"Button1" forState:UIControlStateNormal];
[self addSubview:button];
[button addTarget:self action:@selector(_buttonActionFired:) forControlEvents:UIControlEventTouchDown]; UIButton *button2 = [UIButton buttonWithType:UIButtonTypeCustom];
button2.frame = CGRectMake(, CGRectGetHeight(frame) / , CGRectGetWidth(frame), CGRectGetHeight(frame) / );
button2.tag = ;
button2.backgroundColor = [UIColor darkGrayColor];
[button2 setTitle:@"Button2" forState:UIControlStateNormal];
[self addSubview:button2];
[button2 addTarget:self action:@selector(_buttonActionFired:) forControlEvents:UIControlEventTouchDown];
}
return self;
} - (void)_buttonActionFired:(UIButton *)button {
NSLog(@"=====Button Titled %@ ActionFired ", [button titleForState:UIControlStateNormal]);
} - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == [self viewWithTag:]) {
return [self viewWithTag:];
}
return hitView;
} @end
知识点6:UIView 类定义了下面这些方法,用于在不同的视图本地坐标系统之间进行坐标转换:
convertPoint:fromView:
convertRect:fromView:
convertPoint:toView:
convertRect:toView:
UIWindow 的版本则使用窗口坐标系统。
convertPoint:fromWindow:
convertRect:fromWindow:
convertPoint:toWindow:
convertRect:toWindow:
b:UIView(UIViewHierarchy) 这个分类里面主要包含一些视图的层级关系及视图生命周期操作;
@interface UIView(UIViewHierarchy) @property(nullable, nonatomic,readonly) UIView *superview; //父视图
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews; //子视图数组
@property(nullable, nonatomic,readonly) UIWindow *window; //当前window //删除视图
- (void)removeFromSuperview;
//插入视图
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
//调整视图顺序
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;
//增加视图
- (void)addSubview:(UIView *)view; //插入视图在子视图siblingSubview下面
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
//插入视图在子视图siblingSubview上面
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;
//能够将参数中的view 调整到父视图的最上面
- (void)bringSubviewToFront:(UIView *)view;
//能够将参数中的view 调整到父视图的最下面
- (void)sendSubviewToBack:(UIView *)view; //当视图添加子视图时调用
- (void)didAddSubview:(UIView *)subview;
//当子视图从本视图移除时调用
- (void)willRemoveSubview:(UIView *)subview;
//当视图即将加入父视图时 / 当视图即将从父视图移除时调用
- (void)willMoveToSuperview:(nullable UIView *)newSuperview;
//当试图加入父视图时 / 当视图从父视图移除时调用
- (void)didMoveToSuperview;
//当视图即将加入父视图时 / 当视图即将从父视图移除时调用
- (void)willMoveToWindow:(nullable UIWindow *)newWindow;
//// 当视图加入父视图时 / 当视图从父视图移除时调用
- (void)didMoveToWindow; - (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
- (nullable UIView *)viewWithTag:(NSInteger)tag; //可以通过tag查找对应的视图包括它自个 // Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early
- (void)setNeedsLayout; //标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用 - (void)layoutIfNeeded; //如果,有需要刷新的标记,立即调用layoutSubviews进行布局 - (void)layoutSubviews; // 这个方法,默认没有做任何事情,需要子类进行重写 @property (nonatomic) UIEdgeInsets layoutMargins NS_AVAILABLE_IOS(8_0);
@property (nonatomic) BOOL preservesSuperviewLayoutMargins NS_AVAILABLE_IOS(8_0); // default is NO - 设置为使通过或从该视图的父对其子视图的边缘的级联行为
- (void)layoutMarginsDidChange NS_AVAILABLE_IOS(8_0); @property(readonly,strong) UILayoutGuide *layoutMarginsGuide NS_AVAILABLE_IOS(9_0); @property (nonatomic, readonly, strong) UILayoutGuide *readableContentGuide NS_AVAILABLE_IOS(9_0);
@end
知识点1: isDescendantOfView:方法来判定一个视图是否在其父视图的视图层中。一个视图层次的根视图没有父视图,因此其superview 属性被设置为nil。对于当前被显示在屏幕上的视图,窗口对象通常是整个视图层次的根视图。
知识点2:为某个视图添加子视图时,UIKit 会向相应的父子视图发送几个消息,通知它们当前发生的状态变化。您可以在自己的定制视图中对诸如willMoveToSuperview: 、willMoveToWindow: 、 willRemoveSubview: 、 didAddSubview: 、didMoveToSuperview 和 didMoveToWindow这样的方法进行重载,以便在事件发生的前后进行必要的处理,并根据发生的变化更新视图的状态信息;关于增加视图跟移除视图的实例:
实例1:
MyView *view = [[MyView alloc] initWithFrame:CGRectMake(, , , )];
view.tag = ;
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view]; UIView *v = [[UIView alloc] initWithFrame:CGRectMake(, , , )];
v.backgroundColor = [UIColor yellowColor];
[view addSubview:v]; -- ::46.737 Test[:] willMoveToSuperview:
-- ::46.737 Test[:] didMoveToSuperview
-- ::46.738 Test[:] didAddSubview:
-- ::46.738 Test[:] willMoveToWindow:
-- ::46.738 Test[:] didMoveToWindow
实例2:
MyView *myView = [(MyView *)self.view viewWithTag:];
[myView removeFromSuperview]; -- ::48.394 Test[:] willMoveToSuperview:
-- ::48.395 Test[:] willMoveToWindow:
-- ::48.395 Test[:] didMoveToWindow
-- ::48.395 Test[:] didMoveToSuperview
-- ::48.396 Test[:] willRemoveSubview:
知识点3:layoutSubviews在以下情况下会被调用
1.1、init初始化不会触发layoutSubviews,但是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
1.2、addSubview会触发layoutSubviews
1.3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
1.4、滚动一个UIScrollView会触发layoutSubviews
1.5、旋转Screen会触发父UIView上的layoutSubviews事件
1.6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
c:UIView(UIViewRendering) 此分类里面主要包括关于视图的一些透明度、裁剪、隐藏、背景色等的设置;
@interface UIView(UIViewRendering) //重写 进行绘画
- (void)drawRect:(CGRect)rect; //setNeedsDisplay是更新整个view。
- (void)setNeedsDisplay;
//setNeedsDisplayInRect是更新view的部分区域。
- (void)setNeedsDisplayInRect:(CGRect)rect; @property(nonatomic) BOOL clipsToBounds; // 如果子视图的范围超出了父视图的边界,那么超出的部分就会被裁剪掉 默认值为 NO.
@property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // 默认值 nil.
@property(nonatomic) CGFloat alpha; // 透明度 默认值为 1.0
@property(nonatomic,getter=isOpaque) BOOL opaque; // default is YES. 不透明的视图必须填充其整个范围,或结果是未定义的。在drawRect主动CGContext:不会被清除,可能有非零像素
@property(nonatomic) BOOL clearsContextBeforeDrawing; // 决定绘制前是否清屏,默认为YES。用于提高描画性能,特别是在可滚动的视图中。当这个属性被设置为YES时,UIKIt会在调用drawRect:方法之前,把即将被该方法更新的区域填充为透明的黑色
@property(nonatomic,getter=isHidden) BOOL hidden; // 是否隐藏 默认为NO
@property(nonatomic) UIViewContentMode contentMode; // 视图内容的填充方式 默认 UIViewContentModeScaleToFill
@property(nonatomic) CGRect contentStretch NS_DEPRECATED_IOS(3_0,6_0); // 已弃用 animatable. default is unit rectangle {{0,0} {1,1}}. Now deprecated: please use -[UIImage resizableImageWithCapInsets:] to achieve the same effect. @property(nullable, nonatomic,strong) UIView *maskView NS_AVAILABLE_IOS(8_0);
//色调颜色,开始用于iOS7
@property(null_resettable, nonatomic, strong) UIColor *tintColor NS_AVAILABLE_IOS(7_0);
//色调模型,可以和tintColor结合着使用
@property(nonatomic) UIViewTintAdjustmentMode tintAdjustmentMode NS_AVAILABLE_IOS(7_0);
//更新视图的渲染
- (void)tintColorDidChange NS_AVAILABLE_IOS(7_0); @end
知识点1:UIViewContentMode的说明
@property(nonatomic) UIViewContentMode contentMode;
typedef NS_ENUM(NSInteger, UIViewContentMode) {
UIViewContentModeScaleToFill, //填充到整个视图区域,不等比例拉伸。
UIViewContentModeScaleAspectFit, //长宽等比填充视图区域,当某一个边到达视图边界的时候就不再拉伸,保证内容的长宽比是不变的同时尽可能的填充视图区域。
UIViewContentModeScaleAspectFill, //长宽等比填充视图区域,当某一个边到达视图边界的时候还继续拉伸,直到另一个方向达到视图边界。内容的长宽比不变的同时填满整个视图区域,不显示超过的部分。
UIViewContentModeRedraw, //重绘视图边界
UIViewContentModeCenter, //视图居中
UIViewContentModeTop, //视图顶部对齐
UIViewContentModeBottom, //视图底部对齐
UIViewContentModeLeft, //视图左侧对齐
UIViewContentModeRight, //视图右侧对齐
UIViewContentModeTopLeft, //视图左上角对齐
UIViewContentModeTopRight, //视图右上角对齐
UIViewContentModeBottomLeft, //视图左下角对齐
UIViewContentModeBottomRight, //视图右下角对齐
};
最近有个妹子弄的一个关于扩大眼界跟内含的订阅号,每天都会更新一些深度内容,在这里如果你感兴趣也可以关注一下(嘿对美女跟知识感兴趣),当然可以关注后输入:github 会有我的微信号,如果有问题你也可以在那找到我;当然不感兴趣无视此信息;
你真的了解UIView吗?的更多相关文章
- 关于UIView(转)
曾经有人这么说过,在iphone里你看到的,摸到的,都是UIView,所以UIView在iphone开发里具有非常重要的作用.那么UIView我们到底知道多少呢.请看看下面的问题, 如果这些你都知道, ...
- 你真的会用UITableView嘛
UITableView是工程开发中最经常使用到的UI控件,但是你真的了解它嘛,这里记录几点有用的但你可能并不知道的. 当我们的数据未能显示满一屏幕的时候,UITableView会显示多余的横线,这个时 ...
- [转] 关于UIView
[转载] 原文地址 :http://blog.csdn.net/itianyi/article/details/8982518 UIView是开发中使用得最多的控件了,深入的理解很有必要. UIVie ...
- 从Xib文件加载UIView的5种方式
在不同的Xib文件中最容易维护的是定义的视图,因此对于从Xib文件中加载UIView来说一个方便的流程是非常重要. 在过去的几年里我发现唯一易于管理创建和维护视图(或者任何界面元素,通常会更多)方式就 ...
- 关于UIView及其子类重绘drawRect
转载自:https://nezha.gitbooks.io/ios-developmentarticles/content/UIView%E7%9A%84drawRect%E9%87%8D%E7%BB ...
- UIView你知道多少
转载自:http://www.cnblogs.com/likwo/archive/2011/06/18/2084192.html 曾经有人这么说过,在iphone里你看到的,摸到的,都是UIVie ...
- 理解UIView的绘制-孙亚洲
前言 最近研究OpenGL ES相关和 GPU 相关 发现这篇文章很具有参考的入门价值. 理解 UIView 的绘制, UIView 是如何显示到 Screen 上的? 首先要从Runloop开始说, ...
- App你真的需要么
随着智能手机.移动路联网的普及,APP火的一塌糊涂,APP应用可谓五花八门,街上经常看到各种推广:扫码安装送东西,送优惠券.仿佛一夜之间一个企业没有自己的APP就跟不上时代了. 有时我在想:APP,你 ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
随机推荐
- SQL Server时间粒度系列----第3节旬、月时间粒度详解
本文目录列表: 1.SQL Server旬时间粒度2.SQL Server月有关时间粒度 3.SQL Server函数重构 4.总结语 5.参考清单列表 SQL Server旬时间粒度 ...
- 利用getBoundingClientRect方法实现简洁的sticky组件
补充于2016-03-20: 本文实现有不足,不完美的地方,请在了解本文相关内容后,移步阅读<sticky组件的改进实现>了解更佳的实现. sticky组件,通常应用于导航条或者工具栏,当 ...
- 使用ExpandoObject来实现多个Model传送至视图
昨天Insus.NET有实现<使用ViewModel来实现多个Model传送至视图>http://www.cnblogs.com/insus/p/5594134.html 那今天Insus ...
- Java/Android引用类型及其使用分析
Java/Android中有四种引用类型,分别是: Strong reference - 强引用Soft Reference - 软引用Weak Reference - ...
- Calling startActivity() from outside of an Activity
在Activity中使用startActivity()方法不会有任何限制,因为Activity重载了Context的startActivity()方法.但是如果是在其他地方(如Widget或Servi ...
- ArrayList和LinkedList的区别
简单的说,ArrayList是顺序存储,而LinkedList是链式存储.
- 【Java每日一题】20161024
20161021问题解析请点击今日问题下方的"[Java每日一题]20161024"查看 package Oct2016; public class Ques1024 { publ ...
- 【Java每日一题】20161019
20161018问题解析请点击今日问题下方的"[Java每日一题]20161019"查看 package Oct2016; import java.util.List; publi ...
- FMX 讯息框 FrameDialog
说明:目前 Delphi XE5 无法在 Android 平台下正常使用 ShowMessage 或 MessageDlg 功能(当失去焦点后会当机,如关闭屏幕后再开),这里展示如何使用 TFrame ...
- Echars详解
简介 ECharts,缩写来自Enterprise Charts,商业级数据图表,一个纯Javascript的图表库,可以流畅的运行在PC和移动设备上,兼容当前绝大部分浏览器(IE6/7/8/9 /1 ...