UIView详解+UILabel
1.UIView
1.概念
UIView表示屏幕上的一块矩形区域,它在App中占有绝对重要的地位,因为iOS中几乎所有可视化控件都是UIView的子类。负责渲染区域的内容,用它来显示操作界面并且响应该区域内发生的触摸事件。
- 1.1 UIView的功能
1). 管理矩形区域里的内容
2). 处理矩形区域中的事件
3). 子视图的管理
4). 还能实现动画 UIView的子类也具有这些功能
- 1.2简单的回顾下视图的基本用法
- 视图的层级结构
- tag的使用、给视图命名(nameTag)
- UIView的几何特性(仿射变换)
- UIView动画
2.UIView类的层次关系
3.UIView 的几何特性
1). 在UIView(UIViewGeometry)类别中有很多如frame、bounds、center等属性;其参照系统都不一样。
2). frame和center是的参照是上级视图的坐标系统,而bounds参照的是当前视图的坐标系统,所以bounds的坐标原点一般是{0,0}(注意:在UIScrollView中设置bounds原点不为{0,0},来显示不一样的效果)
3). frame、bounds、center都是直接对视图的CALayer的frame、bounds、center进行调用处理得到的。同时苹果并不提供对这些属性的CGPoint size 和origin的直接写入,但是我们可以自己简单的写个方法来处理这种不便,如:
- (void)setOrigin:(CGPoint)myPoint {
CGRect myframe = self.frame;
myframe.origin = myPoint;
self.frame = myframe;
}
4).几个对在我们调整视图次尺寸时有帮助的方法:
//决定当视图的bounds发生变化时,其子视图是否回自动调整
// default is YES. if set, subviews are adjusted
according to their autoresizingMask if self.bounds changes
@property(nonatomic) BOOL autoresizesSubviews;
//决定上级视图bounds变化时,本视图该如何响应,当时用autolayout
时,该属性被忽略,因为这时系统会自动设置其大小
// simple resize. default is UIViewAutoresizingNone
@property(nonatomic) UIViewAutoresizing autoresizingMask;
//确定系统是否应该把超出本视图的bounds的部分显示出来,YES为不显示
// When YES, content and subviews are clipped to the
bounds of the view. Default is NO.
@property(nonatomic) BOOL clipsToBounds;
//改属性与其他几个设计视图缩放的属性都有关联,主要指明视图的
CALayer在bounds发生变化时改如何响应
// default is UIViewContentModeScaleToFill
@property(nonatomic) UIViewContentMode contentMode;
//自动调整到最合适的位置,如在根据文本内容的长度来自动调整
UILable的长度 可以使用改方法
// calls sizeThatFits: with current view bounds and changes bounds size.
- (void)sizeToFit;
补充:UIView的三个结构体
先学习三个结构体:CGPoint、CGSize、CGrect
确定UIView的位置和大小。
这三个结构体均在一个头文件里:CGGeometry.h
屏幕快照 2016-09-01 上午10.02.45.png
4.属性一览无余
屏幕快照 2016-08-04 上午10.51.52.png
8).形变属性
@property(nonatomic)CGAffineTransform transform;
//default is CGAffineTransformIdentity
9).超出控件边框范围的内容都剪掉
@property(nonatomic)BOOL clipsToBounds;
即view.clipsToBounds = YES;
clipsToBounds(UIView)
是指视图上的子视图,如果超出父视图的部分就截取掉,
masksToBounds(CALayer)
却是指视图的图层上的子图层,如果超出父图层的部分就截取掉
从这块来看 clipsToBounds执行的时候,调用了自己涂层的maskToBounds方法
5.常用方法
//从父视图上移除
- (void)removeFromSuperview;
//将视图插入到subviews数组的对应下标处,插入操作会使subviews下标增加
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
//交换对应下标视图的位置
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;
//将视图添加到本视图上
- (void)addSubview:(UIView *)view;
//在siblingSubview后添加view 视图
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
//在siblingSubview前添加view 视图
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;
//将子视图向前移(把指定的子视图移动到顶层 )
- (void)bringSubviewToFront:(UIView *)view;
//将子视图向后移
- (void)sendSubviewToBack:(UIView *)view;
//查看某视图是否是当前视图的上级视图
- (BOOL)isDescendantOfView:(UIView *)view;
备注:
提示:每一个子视图只能有一个父视图,当我们将一个子视图添加到另一个父视图上面时,它会脱离
原来的父视图。另外,我们指定子视图的frame、bounds等属性时,它的值是相当于父视图的相对值,
而不是屏幕的绝对值,如果父视图改变了位置,那么这些子视图也会跟着改变。父视图如果设置了隐
藏或者透明效果,肯定也会影响到子视图。子视图超出父视图的部分,是不能够接受事件的。
如果需要从父视图中批量删除子视图,可以使用下面的代码:
NSArray *subViews = self.subViews;
if([subViews count] != 0) {
[subViews makeObjectPerformSelector:@selector(removeFromSuperview)];
}
6.仿射变换
1.(CALayer中可以实现3D变换,这里的仿射变换是UIView层面的职能实现2D变换)
CGAffineTransform transform属性其实就是如下的一个结构体(CALayer 对应结构体是CATransform3D)
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
对应的就是一个仿射变换矩阵
| a, b, 0 |
| c, d, 0 |
|tx, ty, 1|
2.设原参数为(X,Y,1),那么经仿射变换后的到的坐标为(aX+cY+tx,bX+dY+ty,1),这是高等数学里基本的矩阵相乘,由此可见a,b,c,d,tx,ty取不同值就会得到不同效果的变换后的坐标,变换的方式就是平移、放缩、旋转等 (参照CGAffineTransform详解http://m.oschina.net/blog/407007 )
其中很多函数都是通过对上述a,b,c,d,tx.ty进行操作后封装好后提直接提供给我们使用的,如下列两段代码效果是一样的:
CGAffineTransform myaffine = CGAffineTransformMake(2, 0, 0, 2, 0, 0);
//x y轴分别拉升两倍,就是整个视图放大到原谅的两倍
myView.transform = myaffine;
CGAffineTransform myaffine2 = CGAffineTransformMakeScale(2,2);
//效果和上述一样 myView.transform = myaffine2;
6.1 实例1Demo
屏幕快照 2016-08-31 下午6.30.00.png
屏幕快照 2016-08-31 下午6.31.01.png
屏幕快照 2016-08-31 下午6.31.21.png
效果图如下:
屏幕快照 2016-08-31 下午6.33.04.png
屏幕快照 2016-08-31 下午6.33.37.png
屏幕快照 2016-08-31 下午6.33.49.png
6.2 实例2
看一个旋转45度角度的代码:
UIView * myView = [[UIView alloc]initWithFrame:CGRectMake(100, 300, 100, 100)];
myView.backgroundColor = [UIColor grayColor];//断点1
CGAffineTransform myaffine3 = CGAffineTransformMakeRotation(3.1415026/4);
myView.transform = myaffine3;//断点2
在上述两段都打上断点,调试输入po myView,结果分别如下:
也就是说在旋转前frame = (100 300; 100 100),旋转后为frame = (79.2893 279.289; 141.421 141.421)。如图:
如果黑色为原图frame,蓝色为旋转后的视图,而红色就为旋转后的frame,可见旋转后的origin和size都改变了,但是该视图的bounds并没有改变,所以在判断变换后的两个视图是否相交就需要另作考虑(未变换的两个柜形视图可以使用CGRectIntersectsRect判断是否相交)。在仿射变换后可以使用CGAffineTransformIdentity来获取原来的frame,CGAffineTransformIdentity就是原图的transform
7.UIView的动画
苹果分别在如下几个类别中提供了很多动画处理的静态方法:
UIView(UIViewAnimation)
UIView(UIViewAnimationWithBlocks)
UIView (UIViewKeyframeAnimations)
在改变视图的frame、transform等熟悉后系统就会自动产生动画如:
[UIView animateWithDuration:3.0 animations:^{
myView.frame = CGRectMake(self.view.bounds.size.width-50, 100, 50, 50);
myView.alpha = 0; myView.transform = CGAffineTransformMakeRotation(3.1415926);
} completion:^(BOOL finished) {
NSLog(@"animation completion!");
}];
这段代码是显示一个正方形从左向右运动,同时越来越透明,并且全过程旋转360度
一些简单的动画用这些方法很容易实现,实现更复杂的动画需要我们自己去设计;比如Core animation,详情见动画篇
8.UIView与UIWindow与UIScreen以及UIRespnder的联系
1.UIView类,这个类继承自UIResponder,看这个名字我们就知道它是负责显示的画布,如果说把window比作画框的话。我们就是不断地在画框上移除、更换或者叠加画布,或者在画布上叠加其他画布,大小当然 由绘画者来决定了。有了画布,我们就可以在上面任意施为了
2.UIWindow继承自UIView,关于这一点可能有点逻辑障碍,画框怎么继承自画布呢?不要过于去专牛角尖,画框的形状不就是跟画布一样吗?拿一块画布然后用一些方法把它加强,是不是可以当一个画框用呢?这也是为什么一个view可以直接加到另一个view上去的原因了。它管理和协调应用程序的显示。UIWindow类是UIView的子类,可以看作是特殊的UIView。一般应用程序只有一个UIWindow对象,但可以手动创建多个添加到程序中,即使有多个UIWindow对象,也只有一个UIWindow可以接受到用户的触屏事件。
看一下系统的初始化过程(在application didFinishLauchingWithOptions里面):
//创建一个全屏的UIWindow
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
//在window中放入根控制器
self.window.rootViewControl = rootViewControl;
//给window设置一个背景色
self.window.backgroundColor = [UIColor grayColor];
//让window显示出来
[self.window makeKeyAndVisible];
//获取当前的keyWindow
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
2.1UIWindow主要起三个作用:
屏幕快照 2016-09-01 上午10.53.30.png
3.UIScreen类代表了屏幕(可以看成就是手机屏幕,它提供了一个画布,可以在上面画各种视图控件,可以通过[[UIScreen mainScreen] bounds]来获取屏幕尺寸),通过这个类我们可以获取一些想要的
屏幕快照 2016-09-01 上午11.10.08.png
//返回的是带有状态栏的Rect
CGrect screenBounds = [[UIScreen mainScreen] bounds];(0, 0, 320, 480)
//不包含状态栏的Rect [iphone状态栏高度20像素]
CGRect viewBounds = [[UIScreen mainScreen] applicationFrame]; (0, 20, 320, 480)
iphone屏幕分辨率:
iphone4前的设备:320*480
iphone4和4s:640*960
iphone5和5s:640*1136
iphone6:750*1334
iphone6p:1242*2208
ipad ipad2:1024*768
ipad3和4 ipad4:2048*1536
ipad mini:1024*768
开发中使用的时候都是➗2;比如iPhone6开发中是用:375 X 667
4.UIRespnder
屏幕快照 2016-09-01 上午10.57.00.png
9.UIView与CLayer之间的关系
1).UIView和CALayer是相互依赖的关系,UIView依赖与CALayer提供的内容(又高于CALayer),通过调用drawRect方法来渲染自身的内容,调节CALayer属性可以调整UIView的外观;
2).CALayer依赖UIView提供的容器来显示绘制的内容。CALayer基于图像管理内容并允许你在这些内容上创建动画。UIView本身,更像是一个CALayer的管理器,访问它的跟绘图和坐标有关的属性,如frame,bounds等,实际上内部都是访问它所在CALayer的相关属性
3).如果没有CALayer,UIView自身也不会存在,UIView是一个特殊的CALayer实现,添加了响应事件的能力。一言以蔽之,UIView来自CALayer,高于CALayer,是CALayer高层实现与封装;UIView的很多特性都源于CALayer对它的支持。
4).在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层
5).当UIView需要显示到屏幕上时 4.1 会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层layer属性上 4.2 绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示
6).通过CALayer改变UIView的形状
- borderColor属性:边框颜色。
- borderWidth属性:边框宽度。
- cornerRadius属性:边框转角半径(实现圆角效果)。
7).通过CALayer添加动画效果
- transforms属性:指定对CALayer中的内容做怎样的变换,支持3D效果和动画。
8).UIView和CALayer的选择
屏幕快照 2016-09-01 上午10.30.52.png
9).CALayer
屏幕快照 2016-09-01 上午10.32.13.png
10).CALayer的基本属性
// 宽度和高度
@propertyCGRectbounds;
// 位置(默认指中点,具体由anchorPoint决定)
@propertyCGPointposition;
// 锚点(x,y的范围都是0-1),决定了position的含义
@propertyCGPointanchorPoint;
// 背景颜色(CGColorRef类型)
@propertyCGColorRefbackgroundColor;
// 形变属性
@propertyCATransform3Dtransform;
// 边框颜色(CGColorRef类型)
@propertyCGColorRefborderColor;
// 边框宽度
@propertyCGFloatborderWidth;
// 圆角半径
@propertyCGColorRefborderColor;
// 内容(比如设置为图片CGImageRef)
@property(retain)idcontents;
11).补充:
1.UIKit使用UIResponder作为响应对象,来响应系统传递过来的事件并进行处理。UIApplication、UIViewController、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
2.在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。
屏幕快照 2016-09-01 上午10.39.31.png
12).总结
屏幕快照 2016-09-01 上午10.39.53.png
补充:图层的能力
https://zsisme.gitbooks.io/ios-/content/chapter1/layer-capabilities.html
2.UILabel的使用详解
1.基础使用
屏幕快照 2017-04-21 下午2.23.28.png
20150701191733906.png
屏幕快照 2017-04-21 下午2.23.53.png
接下来我们为刚才创建的应用程序添加下面的代码来放置一个标签:
屏幕快照 2017-04-21 下午2.24.58.png
应用在模拟器(iPhone 6)上运行效果如下所示:
20150701192355228.png
2.自定义行间距和字间距
比如默认的如下效果图:
屏幕快照 2017-10-26 下午3.20.28.png
然后用一个封装起来的Category来调整这部分文字的行间距为5
屏幕快照 2017-10-26 下午3.22.12.png
屏幕快照 2017-10-26 下午3.22.53.png
这是一个UILabel 的Category,他的内部实现是这样的:
UILabel+ChangeLineSpaceAndWordSpace.h
#import <UIKit/UIKit.h>
@interface UILabel (ChangeLineSpaceAndWordSpace)
/**
* 改变行间距
*/
+ (void)changeLineSpaceForLabel:(UILabel *)label WithSpace:(float)space;
/**
* 改变字间距
*/
+ (void)changeWordSpaceForLabel:(UILabel *)label WithSpace:(float)space;
/**
* 改变行间距和字间距
*/
+ (void)changeSpaceForLabel:(UILabel *)label withLineSpace:(float)lineSpace WordSpace:(float)wordSpace;
@end
UILabel+ChangeLineSpaceAndWordSpace.m
#import "UILabel+ChangeLineSpaceAndWordSpace.h"
@implementation UILabel (ChangeLineSpaceAndWordSpace)
+ (void)changeLineSpaceForLabel:(UILabel *)label WithSpace:(float)space {
NSString *labelText = label.text;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:space];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])];
label.attributedText = attributedString;
[label sizeToFit];
}
+ (void)changeWordSpaceForLabel:(UILabel *)label WithSpace:(float)space {
NSString *labelText = label.text;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText attributes:@{NSKernAttributeName:@(space)}];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])];
label.attributedText = attributedString;
[label sizeToFit];
}
+ (void)changeSpaceForLabel:(UILabel *)label withLineSpace:(float)lineSpace WordSpace:(float)wordSpace {
NSString *labelText = label.text;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText attributes:@{NSKernAttributeName:@(wordSpace)}];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:lineSpace];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])];
label.attributedText = attributedString;
[label sizeToFit];
}
@end
根据内部实现也可以自己灵活运用,不一定用Category的方法
3.实现如下图效果标签
屏幕快照 2018-01-21 下午4.56.09.png
屏幕快照 2018-02-02 下午5.22.13.png