转载

玩转swift -- UIKit 之 UIView(1)

原文

概述

UIView类通过定义一个在屏幕和界面上的矩形区域来管理这块区域的内容。在运行时,视图对象处理其区域内的任何内容渲染,还处理与该内容的任何相互作用。

进入正题

一、初始化视图对象

//?纯代码初始化执行
public?init(frame:?CGRect)
1
//?使用?Interface?Builder?构造界面执行这个
public?init?(coder?aDecoder:?NSCoder)

现在开始测试:

先构建一个自定义View: TestInitView, 并添加两个初始化函数,如下:

//?纯代码走这个
override?init(frame:?CGRect)?{

????super.init(frame:?frame);

????print("执行了?init(frame:?CGRect)")
}

//?使用?Interface?Builder?构造界面走这个
required?init?(coder?aDecoder:?NSCoder)?{

????super.init(coder:?aDecoder);
????print("执行了?init?(coder?aDecoder:?NSCoder)")
}

1、纯代码构造界面

测试代码如下:

//?test?view?纯代码初始化
func?testViewInit()?{

????let?testView?=?TestInitView.init(frame:?CGRect.init(x:?100,?y:?100,?width:?100,?height:?100));
????testView.backgroundColor?=?#colorLiteral(red:?0.1215686277,?green:?0.01176470611,?blue:?0.4235294163,?alpha:?1);

????self.view.addSubview(testView);
}

测试结果:

执行了 init(frame: CGRect)

注意:此方法是由自己调用,来初始化对象的。

2、使用 Interface Builder 构造界面

① 使用storyBoard来构建界面,并拖入一个View,并将此View的Class设置为 TestInitView,如下:

玩转swift -- UIKit 之 UIView(1)

② 运行程序,结果如下:

执行了?init?(coder?aDecoder:?NSCoder)

注意:由系统来调用,自己不能调用。

二、配置视图的视觉外观

//?视图的背景颜色
@NSCopying?open?var?backgroundColor:?UIColor?
//?视图的alpha值
open?var?alpha:?CGFloat
//?确定视图是否不透明的布尔值(它却决定不了当前UIView是不是不透明),用处在于给绘图系统提供一个性能优化开关。
open?var?isOpaque:?Bool
//?确定视图是否被隐藏的布尔值
open?var?isHidden:?Bool
//?这个属性定义了一个非默认的着色颜色值,其值的设置会影响到以视图为根视图的整个视图层次结构。
@available(iOS?7.0,?*)
open?var?tintColor:?UIColor!
//?本视图及父视图的tintColor或tintAdjustmentMode属性改变时自动调用
@available(iOS?7.0,?*)
open?func?tintColorDidChange()
//?一个枚举值,定义了tint?color的调整模式。
@available(iOS?7.0,?*)
open?var?tintAdjustmentMode:?UIViewTintAdjustmentMode
//?决定子视图是否被限定在当前视图的bounds中
open?var?clipsToBounds:?Bool
//?决定在视图重画之前是否先清理视图以前的内容
open?var?clearsContextBeforeDrawing:?Bool
//?一个通过alpha通道来掩盖一个view的内容的可选view。
@available(iOS?8.0,?*)
open?var?mask:?UIView?
//?为此类的实例创建图层的类
open?class?var?layerClass:?Swift.AnyClass?{?get?}
//?UIView的视图层
open?var?layer:?CALayer?{?get?}

1、alpha && isOpaque && isHidden

测试代码如下:

func?testalpha_isOpaque_isHidden()?{

????//?test?alpha
????let?leftAlphaView?=?self.getNewTestView(index_i:?0,?index_j:?0);
????leftAlphaView.alpha?=?0.0;

????let?centerAlphaView?=?self.getNewTestView(index_i:?1,?index_j:?0);
????centerAlphaView.alpha?=?0.5;

????let?rightAlphaView?=?self.getNewTestView(index_i:?2,?index_j:?0);
????rightAlphaView.alpha?=?1.0;

????//?test?isHidden

????let?leftIsHiddenView?=?self.getNewTestView(index_i:?0,?index_j:?1);
????leftIsHiddenView.isHidden?=?true;

????let?rightIsHiddenView?=?self.getNewTestView(index_i:?1,?index_j:?1);
????rightIsHiddenView.isHidden?=?false;

????//?test?isOpaque

????let?leftDownOpaqueView?=?self.getNewTestView(index_i:?0,?index_j:?2);
????let?leftUpOpaqueView?=?self.getNewTestView(index_i:?0,?index_j:?3);
????leftUpOpaqueView.alpha?=?1.0;
????leftUpOpaqueView.isOpaque?=?true;
????leftUpOpaqueView.backgroundColor?=?#colorLiteral(red:?0.2196078449,?green:?0.007843137719,?blue:?0.8549019694,?alpha:?1);
????leftUpOpaqueView.center?=?CGPoint.init(x:?leftDownOpaqueView.center.x,?y:?leftDownOpaqueView.center.y?+?25);

????let?rightDownOpaqueView?=?self.getNewTestView(index_i:?1,?index_j:?2);
????let?rightUpOpaqueView?=?self.getNewTestView(index_i:?1,?index_j:?3);
????rightUpOpaqueView.alpha?=?0.5;
????rightUpOpaqueView.isOpaque?=?false;
????rightUpOpaqueView.backgroundColor?=?#colorLiteral(red:?0.2196078449,?green:?0.007843137719,?blue:?0.8549019694,?alpha:?1)
????rightUpOpaqueView.center?=?CGPoint.init(x:?rightDownOpaqueView.center.x,?y:?rightDownOpaqueView.center.y?+?25);
????}

func?getNewTestView(index_i:?NSInteger,?index_j:?NSInteger)?->?UIView?{

????let?testView?=?UIView.init(frame:?CGRect.init(x:?30?+?index_i?*?80,?y:?80?+?index_j?*?80,?width:?50,?height:?50));
????testView.backgroundColor?=?#colorLiteral(red:?0.7450980544,?green:?0.1568627506,?blue:?0.07450980693,?alpha:?1);

????self.view.addSubview(testView);

????return?testView;
}

测试结果如下:

玩转swift -- UIKit 之 UIView(1)

注意:当把UIView的alpha属性设成0,或者把isHidden设成true的时候,当前UIView和它所包含的子UIView都会变成不可见,同时也不会再响应event事件。isOpaque,而不是决定View的是否是不透明。即View不透明时,isOpaque需要设置为true,来优化性能,有透明度时,isOpaque需要设置为false,防止不可预测事情发生(我也不知道啥事情,测试显示没啥区别!)。

2、tintColor && tintAdjustmentMode && tintColorDidChange

① 首先测试一下,tintColor的设置:

自定义 TIntColorTestView:

//?test?tintColor
func?testTintColor()?{

????self.label.frame?=?CGRect.init(x:?30,?y:?80,?width:?250,?height:?50);
????self.label.text?=?"这是王隆帅的label";
????self.label.textColor?=?self.tintColor;
????self.addSubview(self.label);

????let?button?=?UIButton.init(type:?.system);
????button.frame?=?CGRect.init(x:?30,?y:?140,?width:?250,?height:?50);
????button.addTarget(self,?action:?#selector(btnClick),?for:?.touchUpInside)
????button.setTitle("这是王隆帅的btn",?for:?.normal);

????self.addSubview(button);

????var?image?=?UIImage.init(named:?"imageToColor");
????image?=?image?.withRenderingMode(.alwaysTemplate);
????let?imageView?=?UIImageView.init(frame:?CGRect.init(x:?30,?y:?200,?width:?250,?height:?50));
????imageView.image?=?image;
????self.addSubview(imageView);

}

func?btnClick()?{

????self.tintColor?=?UIColor.init(red:?CGFloat(Double(arc4random()?%?255)?/?255.0),?green:?CGFloat(Double(arc4random()?%?255)?/?255.0),?blue:?CGFloat(Double(arc4random()?%?255)?/?255.0),?alpha:?1.0);
}

override?func?tintColorDidChange()?{

????self.label.textColor?=?self.tintColor;
}

初始化这个View:

func?testTintColor()?{

????let?tintColorView?=?TIntColorTestView.init(frame:?self.view.bounds);
????self.view.addSubview(tintColorView);
}

运行程序,并不断点击btn,结果如下:

玩转swift -- UIKit 之 UIView(1)

由上可知,tintColor会影响到以视图为根视图的整个视图层次结构。主要是改变系统的某些控件,比如 UIButton, UISlider, ?UIProgressView, UIStepper, UIImageView等等。假如想要更改label的文字颜色,或者某些View的背景颜色等,可以监听 tintColorDidChange,来做相应更改!

② 再来测试一下 tintAdjustmentMode

@available(iOS?7.0,?*)
public?enum?UIViewTintAdjustmentMode?:?Int?{

????//?视图的着色调整模式与父视图一致
????case?automatic
????//?视图的tintColor属性返回UIExtendedSRGBColorSpace?颜色空间的颜色
????case?normal
????//?视图的tintColor属性返回?UIExtendedGrayColorSpace?颜色空间的颜色
????case?dimmed
}

测试代码,在上面的基础上 加上以下代码:

print("normal?-----?/(self.tintAdjustmentMode.rawValue)");
print("normal?-----?/(self.tintColor)");

self.tintAdjustmentMode?=?.dimmed;

print("dimmd?-----?/(self.tintAdjustmentMode.rawValue)");
????????print("dimmd?-----?/(self.tintColor)");

button.tintAdjustmentMode?=?.normal;
imageView.tintAdjustmentMode?=?.automatic;

测试结果如下:

normal?-----?1
normal?-----?Optional(UIExtendedSRGBColorSpace?0?0.478431?1?1)
dimmd?-----?2
dimmd?-----?Optional(UIExtendedGrayColorSpace?0.484669?0.8)

玩转swift -- UIKit 之 UIView(1)

由图及打印可知,normal 及 dimmd,确实对应着 UIExtendedSRGBColorSpace 及 UIExtendedGrayColorSpace 两个颜色空间!并且,imageView设置的 .automatic 是继承了父View的 .dimmed 属性。

想要了解更多关于颜色空间,可以看这篇文章

3、clipsToBounds

测试代码如下:

//?测试?clipsToBounds
func?testClipsToBounds()?{

????//?clipsToBounds?false
????let?clipsView1_down?=?self.getNewTestView(index_i:?0,?index_j:?0);
????clipsView1_down.clipsToBounds?=?false;

????let?clipsView1_up?=?self.getNewTestView(index_i:?0,?index_j:?1);
????clipsView1_up.backgroundColor?=?#colorLiteral(red:?0.1294117719,?green:?0.2156862766,?blue:?0.06666667014,?alpha:?1);
????clipsView1_up.center?=?CGPoint.init(x:?25,?y:?50);

????clipsView1_down.addSubview(clipsView1_up);

????//?clipsToBounds?true
????let?clipsView2_down?=?self.getNewTestView(index_i:?1,?index_j:?0);
????clipsView2_down.clipsToBounds?=?true;

????let?clipsView2_up?=?self.getNewTestView(index_i:?1,?index_j:?1);
????clipsView2_up.backgroundColor?=?#colorLiteral(red:?0.1294117719,?green:?0.2156862766,?blue:?0.06666667014,?alpha:?1);
????clipsView2_up.center?=?CGPoint.init(x:?25,?y:?50);

????clipsView2_down.addSubview(clipsView2_up);
}

测试结果:

玩转swift -- UIKit 之 UIView(1)

4、clearsContextBeforeDrawing

见名知意,此属性决定绘制前是否清屏,默认为true。当这个属性被设置为时true,UIKIt会在调用 drawRect: 方法之前,把即将被该方法更新的区域填充为透明的黑色。将这个属性设置为false可以取消相应的填充操作,view中原有内容会保留。

如果将此属性的值设置为false,则我们应该确保在 draw(:) 方法中正确绘制视图的内容。在此前提下,可以提高性能。

5、mask

测试代码如下:

//?测试?mask
func?testMask()?{

????let?view1?=?UIView.init(frame:?CGRect.init(x:?20,?y:?80,?width:?80,?height:?80));
????view1.backgroundColor?=?UIColor.blue;

????let?maskView1?=?UIView.init(frame:?view1.bounds);
????maskView1.backgroundColor?=?UIColor.red;
????maskView1.alpha?=?0.1;

????view1.mask?=?maskView1;
????self.view.addSubview(view1);


????let?view2?=?UIView.init(frame:?CGRect.init(x:?120,?y:?80,?width:?80,?height:?80));
????view2.backgroundColor?=?UIColor.blue;

????let?maskView2?=?UIView.init(frame:?view2.bounds);
????maskView2.backgroundColor?=?UIColor.green;
????maskView2.alpha?=?0.5;

????view2.mask?=?maskView2;
????self.view.addSubview(view2);


????let?view3?=?UIView.init(frame:?CGRect.init(x:?220,?y:?80,?width:?80,?height:?80));
????view3.backgroundColor?=?UIColor.blue;

????let?maskView3?=?UIView.init(frame:?view3.bounds);
????maskView3.backgroundColor?=?UIColor.brown;
????maskView3.alpha?=?1.0;

????view3.mask?=?maskView3;
????self.view.addSubview(view3);
}

测试结果如下:

玩转swift -- UIKit 之 UIView(1)

由上图可知,mask自带的颜色不会显示,最终效果图怎么显示只跟mask每个point的alpha相关。(本例是全部都是一样的alpha,假如想要部分为透明,可以添加含有alpha通道的图片)

可以这样理解,是将mask的每个point的alpha赋值给View的重叠部分相对应的point,这样view的重叠每个point都有个alpha值了,view重叠部分就可能显示多种透明色。

5、layer && layerClass

测试代码:

//?测试?layer
func?testLayer()?{

????let?layer?=?CALayer.init();
????layer.bounds?=?CGRect.init(x:?0,?y:?0,?width:?100,?height:?100);
????layer.position?=?CGPoint.init(x:?100,?y:?200);
????layer.contents?=?UIImage.init(named:?"imageToColor")?.cgImage;
????layer.cornerRadius?=?10.0;
????layer.masksToBounds?=?true;

????self.view.layer.addSublayer(layer);
}

测试结果:

玩转swift -- UIKit 之 UIView(1)

总结:UIView和CALayer是相互依赖的关系。UIView依赖于CALayer提供的内容,CALayer依赖UIView提供的容器来显示绘制的内容。归根到底CALayer是这一切的基础,如果没有CALayer,UIView自身也不会存在,UIView是一个特殊的CALayer实现,添加了响应事件的能力。

想要更详细了解两者详细可以查看这两篇文章(第一篇第二篇),这里就不再赘述了!

三、配置事件相关行为

//?设置视图的可交互性
open?var?isUserInteractionEnabled:?Bool
//?设置是否支持多点触控
open?var?isMultipleTouchEnabled:?Bool
//?设置控件接受事件时的排他性
open?var?isExclusiveTouch:?Bool

1、isUserInteractionEnabled

当一个视图对象的 isUserInteractionEnabled 被置为 false ,则这个视图对象就被从响应者链里移除,它所负责响应的事件全部无效。它的 subviews 事件也会被丢弃。当重新设为 true 时,则事件可以正常的传递给该视图对象。额外的,UIImageView 、UILabel 默认的 isUserInteractionEnabled 是 false ,UIView 的 isUserInteractionEnabled 默认是 true 。

2、isMultipleTouchEnabled

测试代码如下:

//?测试?isMultipleTouchEnabled
var?touchNums?=?0;
func?testIsMultipleTouchEnabled()?{

????self.view.isMultipleTouchEnabled?=?true;
}

override?func?touchesBegan(_?touches:?Set,?with?event:?UIEvent?)?{

????touchNums?=?touchNums?+?touches.count;
????print(touchNums);
}

override?func?touchesEnded(_?touches:?Set,?with?event:?UIEvent?)?{

????touchNums?=?touchNums?-?touches.count;
????print(touchNums);
}

override?func?touchesCancelled(_?touches:?Set,?with?event:?UIEvent?)?{

????touchNums?=?touchNums?-?touches.count;
????print(touchNums);
}

分别用十个指头点击屏幕,测试结果如下:

2
4
5
2
0

由结果可知,屏幕最多支持五点触控。

将 isMultipleTouchEnabled 设置为 false,可得以下结果:

1
0
1
0

即,最多支持一个触摸事件。

3、isExclusiveTouch

测试代码如下:

//?测试?isExclusiveTouch
func?testIsExclusiveTouch()?{

????let?btn1?=?UIButton.init(frame:?CGRect.init(x:?100,?y:?100,?width:?100,?height:?100))
????btn1.backgroundColor?=?UIColor.red;
//????????btn1.isExclusiveTouch?=?true;
????btn1.addTarget(self,?action:?#selector(touchClickBtn),?for:?.touchUpInside);
????self.view.addSubview(btn1);

????let?btn2?=?UIButton.init(frame:?CGRect.init(x:?220,?y:?100,?width:?100,?height:?100))
????btn2.backgroundColor?=?UIColor.blue;
//????????btn2.isExclusiveTouch?=?true;
????btn2.addTarget(self,?action:?#selector(touchClickBtn),?for:?.touchUpInside);
????self.view.addSubview(btn2);
}

func?touchClickBtn()?{

????print("btn?被点击了!!");
}

运行程序,手指先点击红色按钮,摁住不放,另一个手指点击蓝色按钮,然后同时放开,得出下面结果:

btn?被点击了!!
btn?被点击了!!

由此可知,两个按钮同时响应了点击事件。

将代码中注释的代码打开,重新运行程序,并执行上面的操作,得出下面的结果:

btn?被点击了!!

即,设置了View接收事件的排他性为true,则同一时间在两个同级的View之间,只能有一个事件触发!

四、设置位置及大小

//?设置视图在其父视图中的位置及大小
open?var?frame:?CGRect
//?设置视图在父坐标系下的大小
open?var?bounds:?CGRect
//?设置视图在父视图中的位置
open?var?center:?CGPoint
//?通过此属性可以修改对象的平移、缩放比例和旋转角度
open?var?transform:?CGAffineTransform

1、frame && bounds && center

测试代码如下:

//?测试?frame?&&?bounds?&&?center
func?testFrame()?{

????let?view?=?UIView.init(frame:?CGRect.init(x:?100,?y:?200,?width:?50,?height:?50));
????self.view.addSubview(view);

????print(view.frame);
????print(view.bounds);
????print(view.center);
}

测试结果如下:

(100.0,?200.0,?50.0,?50.0)
(0.0,?0.0,?50.0,?50.0)
(125.0,?225.0)

由结果可知,bounds属性只是提供了视图对象的大小,并不包含位置!

2、transform

transform 是一个非常重要的属性,它在矩阵变换的层面上改变视图的显示效果,完成旋转、形变、平移等等操作。

偷个懒,这个属性牵涉面较多,可以参看这篇文章。

五、管理视图层次结构

//?父视图
open?var?superview:?UIView??{?get?}
//?子视图数组
open?var?subviews:?[UIView]?{?get?}
//?该视图所在的窗口视图
open?var?window:?UIWindow??{?get?}

//?从父视图移除
open?func?removeFromSuperview()
//?在指定索引处插入某个子视图
open?func?insertSubview(_?view:?UIView,?at?index:?Int)
//?交换两个指定索引的子视图
open?func?exchangeSubview(at?index1:?Int,?withSubviewAt?index2:?Int)

//?添加视图到子视图列表的末尾????
open?func?addSubview(_?view:?UIView)
//?在指定的子视图之上插入某个子视图
open?func?insertSubview(_?view:?UIView,?belowSubview?siblingSubview:?UIView)
//?在指定的子视图之下插入某个子视图
open?func?insertSubview(_?view:?UIView,?aboveSubview?siblingSubview:?UIView)

//?移动某个子视图到所有子视图的最上方???
open?func?bringSubview(toFront?view:?UIView)
//?移动某个子视图到所有子视图的最下方?
open?func?sendSubview(toBack?view:?UIView)

//?
open?func?isDescendant(of?view:?UIView)?->?Bool

测试代码如下:

//?测试?视图的层次结构
func?testHierarchicalStructure()?{

????let?view1?=?self.createView(tag:?0);
????self.view.addSubview(view1);

????let?view2?=?self.createView(tag:?1);
????self.view.addSubview(view2);

????let?view3?=?self.createView(tag:?2);
????self.view.addSubview(view3);

????print("superView:/n"?+?String(describing:?view1.superview));
????self.printSubviews(name:?"subviews");

????let?view101?=?self.createView(tag:?100);
????UIApplication.shared.keyWindow?.addSubview(view101);
????print("/nwindow2:/n"?+?String(describing:?view101.window))

????view1.removeFromSuperview();
????self.printSubviews(name:?"removeFromSuperview");

????self.view.insertSubview(view1,?at:?1);
????self.printSubviews(name:?"insertSubviewAtIndex");

????self.view.exchangeSubview(at:?0,?withSubviewAt:?1);
????self.printSubviews(name:?"exchangeSubview");

????let?view4?=?self.createView(tag:?3);
????self.view.addSubview(view4);
????self.printSubviews(name:?"addSubview");


????let?view5?=?self.createView(tag:?4);
????self.view.insertSubview(view5,?aboveSubview:?view3);
????self.printSubviews(name:?"insertSubview:aboveSubview");


????let?view6?=?self.createView(tag:?5);
????self.view.insertSubview(view6,?belowSubview:?view3);
????self.printSubviews(name:?"insertSubview:belowSubview");

????self.view.bringSubview(toFront:?view2);
????self.printSubviews(name:?"bringSubview:toFront");

????self.view.sendSubview(toBack:?view2);
????self.printSubviews(name:?"sendSubview:Toback");

????print("/nview2?&&?view3??isDescendant:?"?+?String(view2.isDescendant(of:?view3)));
????print("view2?&&?self.view?isDescendant:?"?+?String(view2.isDescendant(of:?self.view)));
????print("self.view?&&?view2?isDescendant:?"?+?String(self.view.isDescendant(of:?view2)));
????print("view2?&&?view2?isDescendant:?"?+?String(view2.isDescendant(of:?view2)));

}

func?createView(tag:?NSInteger)?->?UIView?{

????let?view?=?UIView.init(frame:?CGRect.init(x:?100,?y:?100,?width:?200,?height:?200));
????view.tag?=?tag;

????return?view;
}

func?printSubviews(name:?String)?{

????print("/n/(name):/n"?+?String(describing:?self.view.subviews.map?{?(view:?UIView)?->?NSInteger?in?return?view.tag;?}));
}

结果如下:

superView:
Optional(>)

subviews:
[0,?1,?2]

window2:
Optional(;?layer?=?>)

removeFromSuperview:
[1,?2]

insertSubviewAtIndex:
[1,?0,?2]

exchangeSubview:
[0,?1,?2]

addSubview:
[0,?1,?2,?3]

insertSubview:aboveSubview:
[0,?1,?2,?4,?3]

insertSubview:belowSubview:
[0,?1,?5,?2,?4,?3]

bringSubview:toFront:
[0,?5,?2,?4,?3,?1]

sendSubview:Toback:
[1,?0,?5,?2,?4,?3]
view2?&&?view3??isDescendant:?false
view2?&&?self.view?isDescendant:?true
self.view?&&?view2?isDescendant:?false
view2?&&?view2?isDescendant:?true

注意:isDescendant 这个属性在对比两个 View 的时候,前者是后者的同一 View 或子 view 才为 true 。

六、配置自动布局行为

//?控制autoresizingMask模式的开启与关闭
open?var?autoresizesSubviews:?Bool
//?子视图相对于父视图的调整模式
open?var?autoresizingMask:?UIViewAutoresizing
//?视图计算最合适的size(容纳子视图)并返回
open?func?sizeThatFits(_?size:?CGSize)?->?CGSize
//?计算合适Size,并更改本视图的size去包含子视图
open?func?sizeToFit()
//?当一个view的bounds变化的时候用于决定其内容怎么变化(变化模式)
open?var?contentMode:?UIViewContentMode

1、autoresizingMask && autoresizesSubviews

public?struct?UIViewAutoresizing?:?OptionSet?{

????public?init(rawValue:?UInt)

????//?自动调整view与父视图左边距,以保证右边距不变
????public?static?var?flexibleLeftMargin:?UIViewAutoresizing?{?get?}
????//?自动调整view的宽度,保证左边距和右边距不变
????public?static?var?flexibleWidth:?UIViewAutoresizing?{?get?}
????//?自动调整view与父视图右边距,以保证左边距不变
????public?static?var?flexibleRightMargin:?UIViewAutoresizing?{?get?}
????//?自动调整view与父视图上边距,以保证下边距不变
????public?static?var?flexibleTopMargin:?UIViewAutoresizing?{?get?}
????//?自动调整view的高度,以保证上边距和下边距不变
????public?static?var?flexibleHeight:?UIViewAutoresizing?{?get?}
????//?自动调整view与父视图的下边距,以保证上边距不变
????public?static?var?flexibleBottomMargin:?UIViewAutoresizing?{?get?}
}

结构体的各个属性,如上所述,具体测试代码如下:

var?firstView?=?UIView.init();

func?testAutoresizingMask()?{

????self.firstView.frame?=?CGRect.init(x:?20,?y:?80,?width:?200,?height:?200);
????self.firstView.backgroundColor?=?UIColor.red;

????self.view.addSubview(self.firstView);

????let?secondView?=?UIView.init(frame:?CGRect.init(x:?10,?y:?10,?width:?180,?height:?20));
????secondView.backgroundColor?=?UIColor.brown;
????secondView.autoresizingMask?=?[.flexibleWidth,?.flexibleBottomMargin];
????self.firstView.addSubview(secondView);

????let?thirdView?=?UIView.init(frame:?CGRect.init(x:?10,?y:?40,?width:?180,?height:?20));
????thirdView.backgroundColor?=?UIColor.cyan;
????thirdView.autoresizingMask?=?.flexibleLeftMargin;
????self.firstView.addSubview(thirdView);

????let?fourthView?=?UIView.init(frame:?CGRect.init(x:?10,?y:?70,?width:?180,?height:?20));
????fourthView.backgroundColor?=?UIColor.blue;
????fourthView.autoresizingMask?=?.flexibleRightMargin;
????self.firstView.addSubview(fourthView);

????let?fifthView?=?UIView.init(frame:?CGRect.init(x:?10,?y:?110,?width:?180,?height:?50));
????fifthView.backgroundColor?=?UIColor.yellow;
????fifthView.autoresizingMask?=?[.flexibleTopMargin,?.flexibleHeight];
????self.firstView.addSubview(fifthView);

????let?changeBtn?=?UIButton.init(type:?.custom);
????changeBtn.setTitle("更改frame",?for:?.normal);
????changeBtn.frame?=?CGRect.init(x:?20,?y:?500,?width:?120,?height:?40);
????changeBtn.backgroundColor?=?UIColor.green;
????changeBtn.addTarget(self,?action:?#selector(changeAutoresizingMaskFrame),?for:?.touchUpInside);
????self.view.addSubview(changeBtn);

}

func?changeAutoresizingMaskFrame()?{

????let?framesArray?=?[220,250,270,300,320];

????let?index?=?Int(arc4random()?%?5);

????self.firstView.frame?=?CGRect.init(x:?20,?y:?80,?width:?framesArray[index],?height:?framesArray[index]);
}

不断点击按钮,得到如下图结果:

玩转swift -- UIKit 之 UIView(1)

由结果可知,API与其描述相符,设置 flexibleRight 则左侧距离保持不变,设置 flexibleTop 则子View距离底部距离保持不变,即关键字(如 left、right 、top 、bottom)代表的反方向的相对距离保持不变。

接着上面的测试,添加如下代码:

self.firstView.autoresizesSubviews?=?false;

重复上面的测试方法,不断点击按钮,得到结果如下图:

玩转swift -- UIKit 之 UIView(1)

由上图可知,之前设置的 autoresizingMask没有效果了,autoresizesSubviews就是控制 autoresizingMask模式的开关,默认是开启的。

2、sizeThatFits && sizeToFit

测试代码如下:

func?testSizeFits()?{

????let?sizeLabel?=?UILabel.init(frame:?CGRect.init(x:?20,?y:?100,?width:?0,?height:?0));
????sizeLabel.font?=?UIFont.systemFont(ofSize:?20);
????sizeLabel.text?=?"王隆帅的简书?王隆帅的博客?王隆帅!!!";
????self.view.addSubview(sizeLabel);

????let?fitSize?=?sizeLabel.sizeThatFits(CGSize.zero);
????print("sizeThatFits?-------?/n"?+?String(describing:?fitSize));
????print("sizeThatFits?后?Label的尺寸?-------?/n"?+?String(describing:?sizeLabel.frame.size));
????sizeLabel.sizeToFit();
????print("sizeToFit?后?Label的尺寸?-------?/n"?+?String(describing:?sizeLabel.frame.size));
}

结果如下:

sizeThatFits?-------?
(333.5,?24.0)
sizeThatFits?后?Label的尺寸?-------?
(0.0,?0.0)
sizeToFit?后?Label的尺寸?-------?
(333.5,?24.0)

由结果可知 sizeThatFits 方法得到的自适应后的尺寸,但是并没有更改标签的实际大小,而sizeToFit将自适应得到的尺寸(内部也是调用sizeThatFits获取自适应尺寸)应用到相应的 label 上,是label的实际尺寸更改为自适应的尺寸。

3、contentMode

public?enum?UIViewContentMode?:?Int?{?

????//?改变内容的高宽比例,缩放内容,UIView中完整显示内容,填满UIView?
????case?scaleToFill
????//?保持内容的高宽比,缩放内容,完整显示内容,最大化填充UIview,没填充上的区域透明
????case?scaleAspectFit?
????//?保持内容高宽比,缩放内容,超出视图的部分内容会被裁减,填充UIView
????case?scaleAspectFill
????//?当View的bounds改变,系统会调用setNeedsDisplay,重新绘制视图
????case?redraw?
????//?不缩放,内容在视图中间
????case?center?
????//?不缩放,内容在视图头部
????case?top
????//?不缩放,内容在视图底部
????case?bottom
????//?不缩放,内容在视图左侧
????case?left
????//?不缩放,内容在视图右侧
????case?right
????//?不缩放,内容在视图头部及左侧
????case?topLeft
????//?不缩放,内容在视图头部及右侧
????case?topRight
????//?不缩放,内容在视图底部及左侧
????case?bottomLeft
????//?不缩放,内容在视图底部及右侧
????case?bottomRight
}

测试代码就忽略不计了,具体效果如下图(图片来源是这里):

玩转swift -- UIKit 之 UIView(1)

七、待续...

篇幅所限,因为UIView的内容略多,所以接下来会分篇来整理。

正文到此结束
Loading...