热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS自动布局学习(UIView+AutoLayout)

自动布局虽然在iOS6的时候已经推出,不过由于各个原因并没有被开发组广泛使用。一方面是大家的app支持版本都是低于iOS6的,另一方面来说是Xcode支持木有现在这么好。以前由于iPhone设备相

  自动布局虽然在iOS6的时候已经推出,不过由于各个原因并没有被开发组广泛使用。一方面是大家的app支持版本都是低于iOS6的,另一方面来说是Xcode支持木有现在这么好。以前由于iPhone设备相对固定,所以在纯代码,纯坐标的布局下很流行,不过现在随着iPhone6发布,如果还要写一大堆乱七八糟的绝对坐标去适配,那工作量和维护成本是很大的。

下面的一些基础直接拿小伙伴分享整理的吧,之后向大家推荐一个开源的库,对AutoLayout进行了封装,用起来很简单也高效。

1.AutoLayout是什么?

使用一句Apple的官方定义的话
 
  AutoLayout是一种基于约束的,描述性的布局系统。
  Auto Layout Is a Constraint-Based, Descriptive Layout System.
 
关键词:
  • 基于约束 - 和以往定义frame的位置和尺寸不同,AutoLayout的位置确定是以所谓相对位置的约束来定义的,比如x坐标为superView的中心,y坐标为屏幕底部上方10像素
  • 描述性 - 约束的定义和各个view的关系使用接近自然语言或者可视化语言(VFL)的方法来进行描述
  • 布局系统 - 即字面意思,用来负责界面的各个元素的位置。
总而言之,AutoLayout为开发者提供了一种不同于传统对于UI元素位置指定的布局方法。以前,不论是在IB里拖放,还是在代码中写,每个UIView都会有自己的frame属性,来定义其在当前视图中的位置和尺寸。使用AutoLayout的话,就变为了使用约束条件来定义view的位置和尺寸。这样的 最大好处是一举解决了不同分辨率和屏幕尺寸下view的适配问题,另外也简化了旋转时view的位置的定义,原来在底部之上10像素居中的view,不论在旋转屏幕或是更换设备(iPad或者iPhone5或者以后可能出现的mini iPad)的时候,始终还在底部之上10像素居中的位置,不会发生变化。总结 
使用约束条件来描述布局,view的frame会依据这些约束来进行计算Describe the layout with constraints, and frames are calculated automatically.

1.1AutoLayout和Autoresizing Mask的区别

在iOS6之前,关于屏幕旋转的适配和iPhone,iPad屏幕的自动适配,基本都是由Autoresizing Mask来完成的。但是随着大家对iOS app的要求越来越高,以及已经以及今后可能出现的多种屏幕和分辨率的设备来说,Autoresizing Mask显得有些落伍和迟钝了。AutoLayout可以完成所有原来Autoresizing Mask能完成的工作,同时还能够胜任一些原来无法完成的任务,其中包括:
  • AutoLayout可以指定任意两个view的相对位置,而不需要像Autoresizing Mask那样需要两个view在直系的view hierarchy中。
  • AutoLayout不必须指定相等关系的约束,它可以指定非相等约束(大于或者小于等);而Autoresizing Mask所能做的布局只能是相等条件的。
  • AutoLayout可以指定约束的优先级,计算frame时将优先按照满足优先级高的条件进行计算。
总结
Autoresizing Mask是AutoLayout的子集,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout还具备一些Autoresizing Mask不具备的优良特性,以帮助我们更方便地构建界面
 
 
2.基本原理
假设有一个按钮,你想把它放置在屏幕的中央。视图中心和按钮中心的相对位置可以简
单地定义成如下:

·按钮的 center.x 相当于视图中心的 center.x

 
·按钮的 center.y 相当于视图中心的 center.y

苹果发现很多的 UI 组件的位置可以使用一个简单的方程等式得到解决:
 
item1.attribute = multiplier ⨉ item2.attribute + constant

例如:使用这个方程式,我们可以很容易地将一个按钮放置到他的父视图中,如下所 示:

    Button.center.x=(button.superview.center.x*1)+0
    Button.center.y=(button.superview.center.y*1)+0
 
    
即:y = m*x + b
3.相关知识
3.1 使用线性公式添加描述
iOS6 引入类来描述约束条件:
NSLayoutConstraint NS_CLASS_AVAILABLE_IOS(6_0) 
方法:constraintWithItem
[NSLayoutConstraint constraintWithItem:(id)
                                 attribute:(NSLayoutAttribute)
                                 relatedBy:(NSLayoutRelation)
                                    toItem:(id)
                                 attribute:(NSLayoutAttribute)
                                multiplier:(CGFloat)
                                  constant:(CGFloat)];

 


(item1.attribute = multiplier ⨉ item2.attribute + constant)
NSLayoutAttribute:
typedef  NS_ENUM(NSInteger, NSLayoutAttribute) {
     NSLayoutAttributeLeft = 1,                      //左侧
     NSLayoutAttributeRight,                         //右侧
     NSLayoutAttributeTop,                           //上方
     NSLayoutAttributeBottom,                        //下方
     NSLayoutAttributeLeading,                       //首部
     NSLayoutAttributeTrailing,                      //尾部
     NSLayoutAttributeWidth,                         //宽度
     NSLayoutAttributeHeight,                        //高度
     NSLayoutAttributeCenterX,                       //X轴中心
     NSLayoutAttributeCenterY,                       //Y轴中心
     NSLayoutAttributeBaseline,                      //文本底标线
                                                                                                                                                    
     NSLayoutAttributeNotAnAttribute = 0             //没有属性
};
 
NSLayoutAttributeLeading/NSLayoutAttributeTrailing的区别是left/right永远是指左右,
leading/trailing在某些从右至左习惯的地区(希伯来语等)会变成,leading是右边,trailing是左边
 
NSLayoutRelation:
 
typedef  NS_ENUM(NSInteger, NSLayoutRelation) {
     NSLayoutRelatiOnLessThanOrEqual= -1,           //小于等于
     NSLayoutRelatiOnEqual= 0,                      //等于
     NSLayoutRelatiOnGreaterThanOrEqual= 1,         //大于等于
};
 
 
 
需求:button始终处于屏幕中心位置
 
 
 
@interface UIView (UIConstraintBasedLayoutInstallingConstraints)

- (NSArray *)constraints NS_AVAILABLE_IOS(6_0);

- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
- (void)removeConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)removeConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
@end
 
 
 
在添加时需要注意的是添加的目标view要遵循以下规则:
 
1.对于两个同层级view之间的约束关系,添加到他们的父view上
 
2.对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
 
3.对于有层次关系的两个view之间的约束关系,添加到层次较高的父view
 
简单来说,找父类。
 
 
 
两个button
 
 
 
 
3.2     使用 Visual Format Language 定义水平和垂直约束
 
Visual Format Language 可视格式语言,用来描述约束条件,这种语言是对视觉描述的一种抽象,大概过程看起来是这样的:
 
比如两个按钮的间距
 
 
 
cancel 与 accept之间水平标准间距 :[cancelButton]-[acceptButton]
 
 
官方文档:

Visual Format Syntax

The following are examples of constraints you can specify using the visual format. Note how the text visually matches the image.

Standard Space

[button]-[textField]

Width Constraint

[button(>=50)]

Connection to Superview

|-50-[purpleBox]-50-|

Vertical Layout

V:[topField]-10-[bottomField]

Flush Views

[maroonView][blueView]

Priority

[button(100@20)]

Equal Widths

[button1(==button2)]

Multiple Predicates

[flexibleButton(>=70,<=100)]

A Complete Line of Layout

|-[find]-[findNext]-[findField(>=20)]-|

 
 
 
 
例:
 
如图所示VFL:
email:
H:|-[_textFieldEmail]-|
V:|-100-[_textFieldEmail]
 
confirm email:
H:|-[_textFieldConfirmEmail]-|
V:[_textFieldEmail]-[_textFieldConfirmEmail]
 
register button:
V:[_textFieldConfirmEmail]-[_registerButton]
 
 
 
缺陷:
1.不能表达是一个固定的长宽比,如:imageView.width = 2 * imageView.height
2.UI 组件在 水平方向的居中条件不能由 Visual Format Language 定义
3. ...
 
 
 
方法:
visualFormat:     VFL 可视格式语言
 
 
 
NSLayoutFormatOptions:
 
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) {
    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),
    NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline),
   
    NSLayoutFormatAlignmentMask = 0xFFFF,
   
    /* choose only one of these three
     */
    NSLayoutFormatDirectiOnLeadingToTrailing= 0 << 16, // default
    NSLayoutFormatDirectiOnLeftToRight= 1 << 16,
    NSLayoutFormatDirectiOnRightToLeft= 2 << 16, 
   
    NSLayoutFormatDirectiOnMask= 0x3 << 16, 
};
 
 
 
注:
在vertical方向 为 left,right,leading,trailing,centerX
在horizontal方向为 top,bottom,centerY,baseLine (baseLine即为文本底部对齐) 
 
 
 
metrics:
[constraints addObjectsFromArray:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-padding-[messageTextView]-kTopAndBottomPadding-|"
    options:NSLayoutFormatAlignAllTop
    metrics:@{@"padding":@5.0}
    views:variableBindings]];
 
views:
A dictionary of views that appear in the visual format string. The keys must be the string values used in the visual format string, and the values must be the view objects.
 
example 1:
V:[_textFieldEmail]-[_textFieldConfirmEmail]
NSDictionary *viewsDictiOnary= NSDictionaryOfVariableBindings(_textFieldConfirmEmail, _textFieldEmail);
 
example 2:
H:|-[_textFieldEmail]-|
NSDictionary *viewsDictiOnary= NSDictionaryOfVariableBindings(_textFieldEmail);
 
方法原型:
 
/* This macro is a helper for making view dictionaries for +constraintsWithVisualFormat:options:metrics:views:. 
 NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
 */
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
UIKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) NS_AVAILABLE_IOS(6_0); // not for direct use
 
 
两个textfiled,一个按钮
 
参数:NSLayoutFormatOptions:
 
两种方法比较
 
使用不同父类的 view 进行约束
 
 
3.3 NSLayoutConstraint 
(过一遍)
 
 
4.XIB 使用 layout
4.1 使用方法
4.2 问题修复
 
 
5.
容易出现的错误
 
因为涉及约束问题,因此约束模型下的所有可能出现的问题这里都会出现,具体来说包括两种:
 
*)  Ambiguous Layout 布局不能确定
*)  Unsatisfiable Constraints 无法满足约束
 
布局不能确定指的是给出的约束条件无法唯一确定一种布局,也即约束条件不足,无法得到唯一的布局结果。这种情况一般添加一些必要的约束或者调整优先级可以解决。无法满足约束的问题来源是有约束条件互相冲突,因此无法同时满足,需要删掉一些约束。两种错误在出现时均会导致布局的不稳定和错误,Ambiguous可以被容忍并且选择一种可行布局呈现在UI上,Unsatisfiable的话会无法得到UI布局并报错。

 

UIView+AutoLayout (GitHub地址:https://github.com/smileyborg/UIView-AutoLayout)

这个第三方写的类别,将需要代码创建的视图的约束进行了一层封装。如果用代码设置约束的话,用可视化语言,代码量少,如果团队成员都很熟悉的情况下,可读性还行!用另一种方法创建一个个约束,然后加上view上去,虽然可读性强,但是代码量大.而这个类别就是将那些方法进行一层可读性更强的方法,和自动布局的本意一样,只要你调用简单方法去描述各个view之间的关系,而创建的约束的方法,我们不需要在去关心。作者也增加了一个数组的类别,是为了方便多个view一起创建约束使用。具体的可以直接去GitHub上下载,然后查看示例的demo。

 


推荐阅读
  • iOS snow animation
    CTSnowAnimationView.hCTMyCtripCreatedbyalexon1614.Copyright©2016年ctrip.Allrightsreserved.# ... [详细]
  • 在处理大规模数据数组时,优化分页组件对于提高页面加载速度和用户体验至关重要。本文探讨了如何通过高效的分页策略,减少数据渲染的负担,提升应用性能。具体方法包括懒加载、虚拟滚动和数据预取等技术,这些技术能够显著降低内存占用和提升响应速度。通过实际案例分析,展示了这些优化措施的有效性和可行性。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • 无线鼠标应用:Remote Mouse
    Remote Mouse 是一款功能强大的无线鼠标软件,可将您的手机或平板设备变为遥控器,完美模拟键盘和鼠标操作,实现手机远程控制电脑。 ... [详细]
  • Vue 实现表格分页功能详解
    本文将详细介绍如何在 Vue 中实现表格的分页功能,包括代码示例和具体实现步骤,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 在 Vue 应用开发中,页面状态管理和跨页面数据传递是常见需求。本文将详细介绍 Vue Router 提供的两种有效方式,帮助开发者高效地实现页面间的数据交互与状态同步,同时分享一些最佳实践和注意事项。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • 设计实战 | 10个Kotlin项目深度解析:首页模块开发详解
    设计实战 | 10个Kotlin项目深度解析:首页模块开发详解 ... [详细]
  • Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ... [详细]
  • 解决Unreal Engine中UMG按钮长时间按住自动释放的问题
    本文探讨了在Unreal Engine中使用UMG按钮时,长时间按住按钮会导致自动释放的问题,并提供了详细的解决方案。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有