重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Xcode升级后,发现跑旧项目调试遇到自定义View部分会卡死,找到原因是在layoutSubviews给self.frame赋值,导致循环调用。
发展壮大离不开广大客户长期以来的信赖与支持,我们将始终秉承“诚信为本、服务至上”的服务理念,坚持“二合一”的优良服务模式,真诚服务每家企业,认真做好每个细节,不断完善自我,成就企业,实现共赢。行业涉及成都VR全景等,在成都网站建设、成都全网营销推广、WAP手机网站、VI设计、软件开发等项目上具有丰富的设计经验。
其实在我们的项目中,难免会用到自动布局,简单的使用frame就可以搞定,但是复杂的控件,如果外面用的自动布局,自定义控件使用frame就很复杂。
一般我们都会在View内部算好大小,通知外部去更新布局,这种方案是可以实现,但是复杂了点。我们希望自定义的View像UILabel那样,autolayout时方便快捷。
在了解如何让自定义View支持自动布局,我们需要了解intrinsicContentSize这个属性,也就是控件的内置大小。比如UILabel,UIButton等控件,他们都有自己的内置大小。控件的内置大小往往是由控件本身的内容所决定的,比如一个UILabel的文字很长,那么该UILabel的内置大小自然会很长。控件的内置大小可以通过UIView的intrinsicContentSize属性来获取内置大小,也可以通过invalidateIntrinsicContentSize方法来在下次UI规划事件中重新计算intrinsicContentSize。如果直接创建一个原始的UIView对象,显然它的内置大小为0。
那么我们就可以使用这个属性来让我们的View支持自动布局,只需要简单的几步
1、重写intrinsicContentSize返回内部大小
2、在大小变化的时候调用 invalidateIntrinsicContentSize 通知内部大小变化。
于是就有了下面这个自定义的tagView
1、支持自动布局,不需要计算高度,也不需要指定frame,自适应高度
2、支持单行,多行显示
3、支持分页显示
4、支持单选,多选,禁用选择
5、支持添加和删除Tag
6、支持字典数据类型
使用也比较简单
具体Demo见
一、autoresizingMask
这是IOS旧的约束实现方式,而且只能用于约束父子关系的控件,约束子控件相对于父控件的位置关系。支持的约束种类有:
typedefNS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone =0,
UIViewAutoresizingFlexibleLeftMargin =10,
UIViewAutoresizingFlexibleWidth =11,
UIViewAutoresizingFlexibleRightMargin =12,
UIViewAutoresizingFlexibleTopMargin =13,
UIViewAutoresizingFlexibleHeight =14,
UIViewAutoresizingFlexibleBottomMargin =15
};
UIViewAutoresizingNone:不随父View的变化而变化
UIViewAutoresizingFlexibleLeftMargin:相对父布局左边距弹性变化,右边距固定
UIViewAutoresizingFlexibleRightMargin:相对父布局右边距弹性变化,左边距固定
UIViewAutoresizingFlexibleTopMargin:相对父布局上边距弹性变化,底部边距固定
UIViewAutoresizingFlexibleBottomMargin:相对父布局底部边距弹性变化,顶部边距固定
UIViewAutoresizingFlexibleWidth:相对父布局的宽度等比缩放
UIViewAutoresizingFlexibleHeight:想对父布局的高度等比缩放
注意:所有的属性都是位移运算,因此可以叠加
如:设置约束,距离左边弹性,距离上部弹性以及宽度弹性
self.view1.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin| UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
二、AutoLayout
AutoLayout是从autoresizingMask演化而来的,因此需要禁用autoresizingMask的方式。
translatesAutoresizingMaskIntoConstraints = NO;
AutoLayout的功能强大,可以设置任意View的约束关系。但添加约束有对应的规则,规则如下:
1、针对View的独立属性,不需要依赖其他View,则将约束添加到自己上面
//设置View的宽度
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:200];
//设置View的高度
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:200];
[self.view1addConstraint:widthConstraint];
[self.view1addConstraint:heightConstraint];
2、针对有依赖关系的情况,有以下规则:
(1)两个同层级view之间的约束关系,添加到他们共同的父view上
(2)两个不同层级上的view之间的约束关系,添加到他们最近的共同的父view上
(3)两个有层次关系的view之间的约束关系,添加到层次较高的view上
请谨记此规则,如果添加错了则会崩溃:( 我将约束添加到了自己上面,没有添加到父View上 )
2020-04-17 17:35:42.950274+0800 ConstraitLayoutTest[1216:429986] [LayoutConstraints] The view hierarchy is not prepared for the constraint:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
2020-04-17 17:35:42.951995+0800 ConstraitLayoutTest[1216:429986] [LayoutConstraints] View hierarchy unprepared for constraint.
Constraint:
Container hierarchy:
UIView: 0x101407f90; frame = (0 0; 0 0); layer = CALayer: 0x28344f560
View not found in container hierarchy:
That view's superview: NO SUPERVIEW
2020-04-17 17:35:42.953478+0800 ConstraitLayoutTest[1216:429986] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view. Does the constraint reference something from outside the subtree of the view? That's illegal. constraint: view:'
*** First throw call stack:
(0x237f97ea0 0x237169a40 0x237e9e674 0x23897bd60 0x2651378c8 0x265137d94 0x265138494 0x100d9a3f0 0x100d99bac 0x26479620c 0x26479663c 0x2646c91f8 0x2646bd4cc 0x2647a7b24 0x2647aaa38 0x2647ab09c 0x2647aa990 0x2647aaccc 0x100d989e8 0x264d5e768 0x2647ec6d0 0x2647ec9f0 0x2647eb9f0 0x264d9818c 0x264d993f0 0x264d786ec 0x264e4457c 0x264e46f74 0x264e3fa64 0x237f281cc 0x237f2814c 0x237f27a30 0x237f228fc 0x237f221cc 0x23a199584 0x264d5d054 0x100d98be8 0x2379e2bb4)
libc++abi.dylib: terminating with uncaught exception of type NSException
Message from debugger: The LLDB RPC server has crashed. The crash log is located in ~/Library/Logs/DiagnosticReports and has a prefix 'lldb-rpc-server'. Please file a bug and attach the most recent crash log.
目录:
注意:
- 不管以上那种情况,scrollView 本身的大小和位置一定要相对固定!
- 可以使用frame直接设置,也可以使用自动布局设置。
根据排列组合,共有 2*2 = 4 中情况,下面分别分析:
这种情况没什么好说的,直接设置即可。
这种情况相当于是第四种情况的简化版,故请先看[第四种方式]。
我们知道,只要设置 scrollView 的 contentSize,就限定了 scrollView 的可滚动范围。所以这种情况,并不能很好的根据子控件的变化而动态显示完整的内容。
所以子控件bounds变化的情况,需要使用[第四种方式]设置。
首先来做个分析:
第1条,两种设置方式,自己根据实际情况选择一种即可;
第2条,将所有子视图塞进一个容器视图中。即先给scrollView添加一个 唯一直接子视图 。
通俗点说就是创建一个 sizeView 设置其大小(直接设置frame或者使用autolayout设置皆可),然后将其添加到scrollView的子视图,其他七七八八的所有小子视图都添加在这个直接子视图 sizeView 中,这个直接子视图就相当于 contentView。
这样只要设置这个直接子视图 sizeView 的大小和约束就好了。
如果设置好了这个直接子视图 sizeView 的约束,那么这个直接子视图 sizeView 的范围就是 scrollView 的滚动范围喽!
第3条,其实设置子视图布局就2个要点:
如图1、图2
图2为竖直、水平方向皆可滚动,注意观察滚动条位置。
图2为竖直、水平方向皆可滚动,注意观察滚动条位置。
如此设置布局是不是很方便呢?
os系统和ios系统相比之下,ios系统较为复杂。
Mac OS 和iOS都是基于Darwin(苹果的一个开源的系统内核,基于Unix),但这只是操作系统部分,前者只能运行在X86\X86-64构架的硬件上(过去的版本还支持PowerPC构架),而iOS只能运行在ARM构架的设备上,比如iPhone、iPod Touch、iPad和Apple TV 2/3代上。
因为构架不同,二者之间完全不能通用,所以iPad上自然无法运行OSX,也不能运行基于OSX开发的。
当然很多/应用都有对应的iOS版本,比如常见的iWork办公套件、iLife娱乐套件、Photoshop和CAD等,也有很多的编程,只是这些都是全新版本,并不是直接移植拿来用。
而且平板更多的是日常应用和娱乐,传统意义上的工作,比如处理文档、 变成和平面/3D设计并不是iPad的本职工作。
iOS开发之自定义表情键盘(组件封装与自动布局)
iOS开发之自定义表情键盘(组件封装与自动布局)
原理:IOS6.0 之后,苹果优化了UI界面的布局方式,提出了自动布局的概念,和之前的autoresizing相比功能更强大。子视图基于父视图的自动布局显示。都是父视图去添加对子视图的约束。
在这里主要说的是通过代码对自动布局视图的实现。
代码中一般用到的有两个添加约束的方式:
1.- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
2.- (void)addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
/span
在使用自动布局之前要对子视图的布局方式进行调整,用到这个UIView的属性。
- (BOOL)translatesAutoresizingMaskIntoConstraints NS_AVAILABLE_IOS(6_0); // Default YES
需要将其设置为NO;
下面用简单例子说明一下:
UIView *v1 = [[UIView alloc] initWithFrame:CGRectZero];
v1.translatesAutoresizingMaskIntoConstraints = NO;
v1.backgroundColor = [UIColor redColor];
[self.view addSubview:v1];
UIView *v2 = [[UIView alloc] initWithFrame:CGRectZero];
v2.backgroundColor = [UIColor grayColor];
v2.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:v2];//添加两个允许自动布局的子视图
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0]];//设置子视图的宽度和父视图的宽度相同
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];//设置子视图的高度是父视图高度的一半
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[v1][v2(==v1)]-0-|" options:0 metrics:nil views:views]];//通过addConstraints 添加对水平方向上v1的控制--距离父视图左侧距离为0(距离为0的话也可省略)同时将v2的水平方向的宽度和v1设置成相同
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[v1][v2(==v1)]|" options:0 metrics:nil views:views]];/通过addConstraints 添加对垂直方向上v1的控制--距离父视图上侧距离为0(距离为0的话也可省略)同时将v2的垂直方向的高度和v1设置成相同
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[v1]-0-[v2]-0-|" options:0 metrics:nil views:views]];//最后是垂直布局两个子view
这样就可以实现上下两个view,各占一半。旋转屏幕的情况下也会自动处理布局。这样看起来代码多,但是可以适应多种分辨率的屏幕。不排除以后苹果出更大更多分辨率的手机。
关于constraintsWithVisualFormat:函数介绍:
constraintsWithVisualFormat:参数为NSString型,指定Contsraint的属性,是垂直方向的限定还是水平方向的限定,参数定义一般如下:
V:|-(=XXX) :表示垂直方向上相对于SuperView大于、等于、小于某个距离
若是要定义水平方向,则将V:改成H:即可
在接着后面-[]中括号里面对当前的View/控件 的高度/宽度进行设定;
options:字典类型的值;这里的值一般在系统定义的一个enum里面选取
metrics:nil;一般为nil ,参数类型为NSDictionary,从外部传入 //衡量标准
views:就是上面所加入到NSDictionary中的绑定的View
在这里要注意的是 AddConstraints 和 AddConstraint 之间的区别,一个添加的参数是NSArray,一个是NSLayoutConstraint
使用规则
|: 表示父视图
-:表示距离
V: :表示垂直
H: :表示水平
= :表示视图间距、宽度和高度必须大于或等于某个值
= :表示视图间距、宽度和高度必须小宇或等于某个值
== :表示视图间距、宽度或者高度必须等于某个值
@ :=、=、== 限制 最大为 1000
1.|-[view]-|: 视图处在父视图的左右边缘内
2.|-[view] : 视图处在父视图的左边缘
3.|[view] : 视图和父视图左边对齐
4.-[view]- : 设置视图的宽度高度
5.|-30.0-[view]-30.0-|: 表示离父视图 左右间距 30
6.[view(200.0)] : 表示视图宽度为 200.0
7.|-[view(view1)]-[view1]-| :表示视图宽度一样,并且在父视图左右边缘内
8. V:|-[view(50.0)] : 视图高度为 50
9: V:|-(==padding)-[imageView]-=0-[button]-(==padding)-| : 表示离父视图的距离
为Padding,这两个视图间距必须大于或等于0并且距离底部父视图为 padding。
10: [wideView(=60@700)] :视图的宽度为至少为60 不能超过 700
11: 如果没有声明方向默认为 水平 V: