UIView@IT·互联网程序员

UIView详解+UILabel

2016-08-03  本文已影响331人  Kevin_wzx

1.UIView

1.概念

UIView表示屏幕上的一块矩形区域,它在App中占有绝对重要的地位,因为iOS中几乎所有可视化控件都是UIView的子类。负责渲染区域的内容,用它来显示操作界面并且响应该区域内发生的触摸事件。

  • 1.2简单的回顾下视图的基本用法
  1. 视图的层级结构
  2. tag的使用、给视图命名(nameTag)
  3. UIView的几何特性(仿射变换)
  4. 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的形状

7).通过CALayer添加动画效果

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
上一篇 下一篇

猜你喜欢

热点阅读