Auto Layout 使用心得

此系列文章代码仓库在 https://github.com/johnlui/AutoLayout ,有不明白的地方可以参考我的 Auto Layout 设置哦,下载到本地打开就可以了。

简介

Auto Layout 是苹果在 Xcode 5 (iOS 6) 中新引入的布局方式,旨在解决 3.5 寸和 4 寸屏幕的适配问题。屏幕适配工作在 iPhone 6 及 plus 发布以后变得更加重要,而且以往的“笨办法”的工作量大幅增加,所以很多人开始学习使用 Auto Layout 技术。

初体验

0. 开发环境

本系列文章的开发环境为:

  • OS X 10.10.2
  • Xcode Version 6.2 (6C131e)

1. 新建应用

新建一个 Single View Application,命名为 AutoLayout,如下:

点击选中 Main.storyboard,右侧内容如下:

1、2 两个按钮将会在未来的开发中产生巨大的作用,他们将拥有本系列文章的全局名称:按钮1,按钮2。请先记下他们的位置。

2. 直接上手,开始使用

这也是我对学习新的软件编程技术的基本学习方法:有一个具体客观驱动的目标,例如做一个真正要给客户用的软件,而不是“为了学习新技术提高自己”这类伪目标。

让我们直接上手:绘制一个距离左右边都有一定距离、固定高度、垂直居中的按钮,叫“Swift on iOS”。

1. 第一步,从右侧拖过来一个按钮,置于页面最中间。会有参考线出现,这一步很容易:

2. 选中这个 button,将按钮背景色和前景色进行如下设置:

3. 将按钮左侧边界往左拖动直到自动吸附,留下一定的距离。右侧进行同样操作:

4. 选中这个 button,修改文字为 Swift on iOS:

5. 选中这个 button,点击 按钮2 ,选择这一项:

这时候 button 周围会出现一些蓝色的线条,这些就是 Auto Layout 的约束项。

3. 大功告成,查看效果

3.5:

4:

4.7:

5.5:

4. 分析

选中这个 button,在右侧查看自动生成的约束项:

只有三项,这三项的意思分别是:和父视图纵向居中对齐、右侧和父视图对齐、左侧和父视图对齐。

我们很容易就能理解这样可以定位一个按钮,但是总感觉少了点什么。实际上这三个自动生成的约束项并不能描述一个 button 的位置,因为少了一个关键的属性:button 的高度。以后我们会详细地讨论。

5. 核心思想

本质分析

Auto Layout 的本质是依靠 某几项约束条件 来达到对某一个元素的定位。我们可以在某个地方只使用一个约束,以达到一个小目的,例如防止内容遮盖、防止边界溢出等。但我的最佳实践证明,如果把页面上每一个元素的位置都用 Auto Layout 进行 “严格约束” 的话,那么 Auto Layout 可以帮我们省去非常多的计算 frame 的代码。

“严格约束” 是什么?

简单来说,严格约束就是对某一个元素的绝对定位,让它在任一屏幕尺寸下都有着唯一的位置。这里的绝对定位不是定死的位置,而是对一个元素 完善的约束条件。

让我们看图说话:

  1. 我们要在一个直角坐标系里描述一个矩形。
  2. 那么只需要指定这个矩形的位置和大小。
  3. 那么只要给出上图中的四个值即可:到左边界的距离,到上边界的距离,宽度,高度。
  4. 这四个约束是最简单的情况。在对一个元素进行严格约束时,请直接在脑中构建这个元素,并且加上几条约束条件,如果他无法缩放和动弹,那么严格约束就是成功的!
  5. 必须牢记,使用 Auto Layout 时最重要的是:对页面上每一个元素都进行严格约束,不严格的约束是万恶之源。

Auto Layout 使用心得(二)--实现三等分

此系列文章代码仓库在 https://github.com/johnlui/AutoLayout ,有不明白的地方可以参考我的 Auto Layout 设置哦,下载到本地打开就可以了。
准备

上一篇文章中,我们共同进行了 Auto Layout 的初体验,在本篇我们将一起尝试用 Auto Layout 实现三等分。

Auto Layout 的本质原理

Auto Layout 的本质是用一些约束条件对元素进行约束,从而让他们显示在我们想让他们显示的地方。

约束主要分为以下几种(欢迎补充):

  1. 相对于父 view 的约束。如:距离上边距 10,左边距 10。
  2. 相对于前一个元素的约束。如:距离上一个元素 20,距离左边的元素 5 等。
  3. 对齐类约束。如:跟父 view 左对齐,跟上一个元素居中对齐等。
  4. 相等约束。如:跟父 view 等宽。

三等分设计思路

许多人刚开始接触 Auto Layout,可能会以为它只能实现上面的1、2功能,其实后面3、4两个功能才是强大、特别的地方。接下来我们将尝试设计横向三等分:

  1. 第一个元素距离左边一定距离。
  2. 最后一个元素距离右边一定距离。
  3. 三者高度恒定,宽度相等。
  4. 1和2、2和3的横向间距固定。

干货,实现过程的动图:

运行结果

4 寸:

4.7 寸:

纵向三等分实现方式类似,大家可以自己尝试一下哦~

Auto Layout 使用心得(三)—— 自定义 cell 并使用 Auto Layout

此系列文章代码仓库在 https://github.com/johnlui/AutoLayout ,有不明白的地方可以参考我的 Auto Layout 设置哦,下载到本地打开就可以了。
简介

本篇中我们将尝试自定义一个 UITableViewCell,并使用 Auto Layout 对其进行约束。

自定义 cell 基础

在前面的项目中,我们采用 StoryBoard 来组织页面,StoryBoard 可以视为许多个 xib 的集合,所以我们可以得到两个信息:

  1. 这个项目通过初始化主 StoryBoard 文件来展现 APP,而 UIViewController 类文件是通过 StoryBoard 文件的绑定来初始化并完成功能的。
  2. 我们可以创建新的 StoryBoard 文件或者新的 xib 文件来构造 UI,然后动态地加载进页面。

创建文件

我们可以一次性创建 xib 文件和类的代码文件。

新建 Cocoa Touch Class:

设置和下图相同即可:

检查成果

分别选中上图中的 1、2 两处,检查 3 处是否已经自动绑定为 firstTableViewCell,如果没有绑定,请先检查选中的元素确实是 2,然后手动绑定即可。

完成绑定工作

切换一页,如下图进行 Identifier 设置:

新建 Table View Controller 页面

新建一个 Table View Controller 页面,并把我们之前创建的 Swift on iOS 那个按钮的点击事件绑定过去,我们得到:

然后创建一个名为 firstTableViewController 的 UITableViewController 类,创建流程跟前面基本一致。不要创建 xib。然后选中 StoryBoard 中的 Table View Controller(选中之后有蓝色边框包裹),在右侧对它和 firstTableViewController 类进行绑定:

调用自定义 cell

修改 firstTableViewController 类中的有效代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import UIKit

class firstTableViewController: UITableViewController {

    override func viewDidLoad() {

        super.viewDidLoad()

        var nib = UINib(nibName: "firstTableViewCell", bundle: nil)

        self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return 1

    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 10

    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as firstTableViewCell

        cell.textLabel?.text = indexPath.row.description

        return cell

    }

}

viewDidLoad() 中添加的两行代码是载入 xib 的操作。最下面的三个 func 分别是定义:

  1. self.tableView 中有多少个 section
  2. 每个 section 中分别有多少个条目
  3. 实例化每个条目,提供内容

如果你得到以下页面,说明你调用自定义 cell 成功了!

给自定义 cell 添加元素并使用 Auto Layout 约束

首先向 Images.xcassets 中随意加入一张图片。

然后在左侧文件树中选中 firstTableViewCell.xib,从右侧组件库中拖进去一个 Image View,并且在右侧将其尺寸设置如下图右侧:

给 ImageView 添加约束:

选中该 ImageView(左箭头所示),点击自动 Auto Layout(右箭头所示),即可。

给 ImageView 设置图片:

再从右侧组件库中拖入一个 UILabel,吸附到最右侧,垂直居中,为其添加自动约束,这一步不再赘述。

在 firstTableViewCell 类中绑定 xib 中拖进去的元素

选中 firstTableViewCell.xib,切换到双视图,直接进行拖动绑定:

绑定完成!

约束 cell 的高度

在 firstTableViewController 中添加以下方法:


1

2

3

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    return 50

}

给自定义的 UILabel 添加内容

修改 firstTableViewController 中以下函数为:


1

2

3

4

5

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as firstTableViewCell

    cell.firstLabel.text = indexPath.row.description

    return cell

}

查看结果

4.0 寸:

4.7 寸:

如果你得到以上结果,那么恭喜你自定义 cell 并使用 Auto Layout 成功!

Auto Layout 使用心得(四)—— 22 行代码实现拖动回弹

此系列文章代码仓库在 https://github.com/johnlui/AutoLayout ,有不明白的地方可以参考我的 Auto Layout 设置哦,下载到本地打开就可以了。
简介

本文中,我们将一起使用 UIPanGestureRecognizer 和 Auto Layout,通过 22 行代码实现拖动回弹效果。

搭建界面

删除首页中间的按钮,添加一个 View ,设置一种背景色便于辨认,然后对其进行绝对约束:

拖动一个 UIPanGestureRecognizer 到该 View 上:

界面搭建完成。

属性绑定

切换到双向视图,分别右键拖动 UIPanGestureRecognizer 和该 View 的 Top Space 的 Auto Layout 属性到 ViewController 中绑定:

然后将 UIPanGestureRecognizer 右键拖动绑定:

编写代码


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

class ViewController: UIViewController {

    

    var middleViewTopSpaceLayoutConstant: CGFloat!

    var middleViewOriginY: CGFloat!

    

    @IBOutlet weak var middleView: UIView!

    @IBOutlet weak var middleViewTopSpaceLayout: NSLayoutConstraint!

    @IBOutlet var panGesture: UIPanGestureRecognizer!

    override func viewDidLoad() {

        super.viewDidLoad()

        

        panGesture.addTarget(self, action: Selector("pan"))

        middleViewTopSpaceLayoutConstant = middleViewTopSpaceLayout.constant

        middleViewOriginY = middleView.frame.origin.y

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    func pan() {

        if panGesture.state == UIGestureRecognizerState.Ended {

            UIView.animateWithDuration(0.4, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in

                self.middleView.frame.origin.y = self.middleViewOriginY

                }, completion: { (success) -> Void in

                    if success {

                        self.middleViewTopSpaceLayout.constant = self.middleViewTopSpaceLayoutConstant

                    }

            })

            return

        }

        let y = panGesture.translationInView(self.view).y

        middleViewTopSpaceLayout.constant = middleViewTopSpaceLayoutConstant + y

    }

}

查看效果

22 行代码,拖动回弹效果完成!

Auto Layout 使用心得(五)--根据文字、图片自动计算 UITableViewCell 高度

此系列文章代码仓库在 https://github.com/johnlui/AutoLayout ,有不明白的地方可以参考我的 Auto Layout 设置哦,下载到本地打开就可以了。
简介

本文中,我们将一起使用 Auto Layout 技术,让 UITableViewCell 的高度随其内部的 UILabel 和 UIImageView 的内容自动变化。

搭建界面

恢复之前删除的按钮

放置一个按钮,恢复到 firstTableViewController 的连接:

别忘了添加约束让他居中哦。

修改 firstTableViewCell

将 firstTableViewCell 的尺寸设置为 600 * 81,将 logo 的尺寸设置为 80 * 80。将 logo 的约束修改为如下图所示:

修改 label 的尺寸和位置,添加约束如下图:

给 ViewController 增加 UINavigationController 嵌套

为了便于返回。操作如下图:

查看结果

根据 label 自动计算 firstTableViewCell 高度

选中 label,设置 lines 行数为 0,表示不限长度自动折行:

修改 label 的文字内容让其超出一行:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

import UIKit

class firstTableViewController: UITableViewController {

    

    var labelArray = Array() // 用于存储 label 文字内容

    override func viewDidLoad() {

        super.viewDidLoad()

        var nib = UINib(nibName: "firstTableViewCell", bundle: nil)

        self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")

        

        // 循环生成 label 文字内容

        for in 1...10 {

            var text = ""

            for in 1...i {

                text += "Auto Layout"

            }

            labelArray.append(text)

        }

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

    // MARK: - Table view data source

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        return 50

    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return 1

    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return labelArray.count

    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as! firstTableViewCell

        cell.firstLabel.text = labelArray[indexPath.row]

        return cell

    }

}

现在到了最关键的时刻,驱动 UITableViewCell 适应 Label 内容:

1. 使用 estimatedHeightForRowAtIndexPath 替代 heightForRowAtIndexPath

estimatedHeightForRowAtIndexPath 是 iOS 7 推出的新 API。如果列表行数有一万行,那么 heightForRowAtIndexPath 就会在列表显示之前计算一万次,而 estimatedHeightForRowAtIndexPath 只会计算当前屏幕中显示着的几行,会大大提高数据量很大时候的性能。

2. 新建一个 prototypeCell 成员变量以复用,并在 viewDidLoad 中初始化


1

2

3

4

5

6

7

8

9

10

11

12

13

14

class firstTableViewController: UITableViewController {

    

    var labelArray = Array() // 用于存储 label 文字内容

    

    var prototypeCell: firstTableViewCell!

    override func viewDidLoad() {

        super.viewDidLoad()

        var nib = UINib(nibName: "firstTableViewCell", bundle: nil)

        self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")

        

        // 初始化 prototypeCell 以便复用

        prototypeCell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell") as! firstTableViewCell

        

......

3. 计算出高度


1

2

3

4

5

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    let cell = prototypeCell

    cell.firstLabel.text = labelArray[indexPath.row]

    return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1

}

4. 查看效果

超级大坑

上面让 firstTableViewCell 根据 label 自动计算高度的过程中,有一个超级大坑:如果给左侧 UIImageView 赋的图片较大(大于 80px),将看到如下奇怪的结果:

这只是因为图片把 UITableViewCell 撑大了,并不是我们的计算没有效果。

解决大坑:进攻是最好的防守!根据图片自动计算 firstTableViewCell 高度

首先,把图片的渲染模式改成 Aspect Fit:

给 Images.xcassets 增加三张图片,命名为 0、1、2,尺寸从小到大:

给 cellForRowAtIndexPath 增加代码:


1

2

3

if indexPath.row < 3 {

    cell.logoImageView.image = UIImage(named: indexPath.row.description)

}

查看效果:

前两个 cell 看起来比较正常,第三个为什么多出了那么多空白?这就是使用 Auto Layout 限制图片宽度为 80px 的原生问题:宽度虽然限制了,高度却依然是原图的高度。解决办法也很简单:如果图片宽度大于 80px,就重绘一张 80px 宽度的图片填充进去。

新建一个 Group(虚拟文件夹),叫 Extensions,并在其内部新建 UIImage.swift 文件,内容如下:


1

2

3

4

5

6

7

8

9

10

11

import UIKit

extension UIImage {

    func resizeToSize(size: CGSize) -> UIImage {

        UIGraphicsBeginImageContext(size)

        self.drawInRect(CGRectMake(0, 0, size.width, size.height))

        let newImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()

        

        return newImage

    }

}

给 UIImage 类扩展了一个名为 resizeToSize 的方法,返回一个按照要求的大小重绘过的 UIImage 对象。修改 cellForRowAtIndexPath 的代码为:


1

2

3

4

5

6

7

8

9

10

11

12

13

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as! firstTableViewCell

    cell.firstLabel.text = labelArray[indexPath.row]

    

    if indexPath.row < 3 {

        var image = UIImage(named: indexPath.row.description)!

        if image.size.width > 80 {

            image = image.resizeToSize(CGSizeMake(80, image.size.height * (80 / image.size.width)))

        }

        cell.logoImageView.image = image

    }

    return cell

}

搞定!

查看效果

从上图可以看出,cell 已经可以根据图片和文字中比较高的一个完全自适应。

时间: 2024-10-22 00:21:16

Auto Layout 使用心得的相关文章

Auto Layout 使用心得(五)—— 根据文字、图片自动计算 UITableViewCell

简介 本文中,我们将一起使用 Auto Layout 技术,让 UITableViewCell 的高度随其内部的 UILabel 和 UIImageView 的内容自动变化. 搭建界面 恢复之前删除的按钮 放置一个按钮,恢复到 firstTableViewController 的连接: 别忘了添加约束让他居中哦. 修改 firstTableViewCell 将 firstTableViewCell 的尺寸设置为 600 * 81,将 logo 的尺寸设置为 80 * 80.将 logo 的约束修

Auto Layout (一)基于VFL的简单布局

最近正在研究Auto Layout 记录下心得和大家分享下!效果图如下  很简单的效果!只适合新手级别的看              1.初始化三个需要的控件  需要注意的是 translatesAutoresizingMaskIntoConstraints 这个属性.用来禁止AutoresizingMask转换成AutoLayout,简单来说,Autoresizing和AutoLayout用的不是一套东西,但是默认情况下是相互转换的,这里我们要指定使用AutoLayout系统,所以要禁止自动转

Auto Layout Guide----(三)-----Anatomy of a Constraint

Anatomy of a Constraint 剖析约束 The layout of your view hierarchy is defined as a series of linear equations. Each constraint represents a single equation. Your goal is to declare a series of equations that has one and only one possible solution. A samp

Day3 : Auto layout 和 JVFloatLabeledTextfield框架 学习笔记

为了可以优化项目的UI,为了可以使用JVFloatLabeledTextfield框架来构建文本输入框(动画效果超赞),今天重点学习了Auto Layout(以下简称AL)技术,主要是了解AL的工作原理,并且要掌握用代码添加constraints. 1.JVFloatLabeledTextfield JVFloatLabeledTextfield框架可以让文本框呈现一个漂浮的PlaceHolder,简洁.明确.生动.而这个框架另一个让我大开眼界的是他利用AL技术画直线,做出一个简单的表单页面.画

iOS开发之Auto Layout入门

随着iPhone6与iOS8的临近,适配的问题讲更加复杂,最近学习了一下Auto Layout的使用,与大家分享.  什么是Auto Layout? Auto Layout是iOS6发布后引入的一个全新的布局特性,其目的是弥补以往Autoresizing在布局方面的不足之处,以及未来面对更多尺寸适配时界面布局可以更好的适应. 为什么要用Auto Layout? Autolayout能解决不同屏幕(iPhone4,iPhone5,iPad...)之间的适配问题. 在iPhone4时代开发者只需要适

Auto Layout Guide----(二)-----Auto Layout Without Constraints

Auto Layout Without Constraints 没有约束的自动布局 Stack views provide an easy way to leverage the power of Auto Layout without introducing the complexity of constraints. A single stack view defines a row or column of user interface elements. The stack view a

【转】有了Auto Layout,为什么你还是害怕写UITabelView的自适应布局?

Apple 算是最重视应用开发体验的公司了.从Xib到StoryBoard,从Auto Layout到Size Class,每一次的更新,都会给iOS应用的开发带来不小的便利.但是,对于绝对多数iOS攻城狮来说,我们依然还是很害怕写UITabelVIew的自适应布局.当然,害怕不是因为我们不会写,或者本身有什么特殊的技术点,而是因为太麻烦.当然,文章的后半部分,会给出相应的解决方案,毕竟本文不是为了吐槽而吐槽. UITabelView的自适应布局有多麻烦? 数据类型的不确定性:种类越多,页面越复

【Auto Layout】Xcode6创建Auto Layout 约束时产生的一些变化【iOS开发教程】

[#Auto Layout#]Xcode6创建Auto Layout 约束时产生的一些变化 ? ? ? 运行效果: 没有从顶部开始,似乎是从statusbar的20高度以外开始计算的 ? ? ? ? ? 另外在设置顶部约束和底部约束时也尽量不要选择默认的,尽量点击右侧的小箭头,在弹框中选择父视图,如下图所示: ? ? Created: 05/24/2015Link:?http://www.cnblogs.com/ChenYilong/p/4526893.html

iOS 使用LayoutGuide 来限制控件的位置,配合Auto Layout constraints

UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [self.view addSubview:button]; [button setTranslatesAutoresizingMaskIntoConstraints: NO]; // 得到当前视图的最低基准限制,这个是对于Auto Layout constraint来说的. id bottomGuide = self.bottomLayoutGu