系统学习iOS动画之二:自动布局

栏目: IOS · 发布时间: 5年前

内容简介:本文是我学习自动布局(Auto Layout)在iOS 6中首次推出,已经存在了一段时间,每次发布新版本的iOS和Xcode都经历了一系列成功的迭代。自动布局背后的核心理念非常简单:它允许您根据布局中的每个元素之间创建的关系来定义应用程序的UI元素的布局。

本文是我学习 《iOS Animations by Tutorials》 笔记中的一篇。 文中详细代码都放在我的Github上 andyRon/LearniOSAnimations

自动布局(Auto Layout)在iOS 6中首次推出,已经存在了一段时间,每次发布新版本的iOS和Xcode都经历了一系列成功的迭代。

自动布局背后的核心理念非常简单:它允许您根据布局中的每个元素之间创建的关系来定义应用程序的UI元素的布局。

我们平常开发时已将自动布局用于静态的布局,在本文中将学习使用约束来设置动画。

6-自动布局的介绍

本章节是用自动布局完成下一章节需要使用的项目 Packing List 。关于自动布局,可参考我之前的文章 开始用Swift开发iOS 10 - 3 介绍Auto Layout ,这里就不重复了。

7-约束动画

约束动画(Animating Constraints) 并不比属性动画困难; 它只是有点不同。 通常,只需使用新约束替换现有约束,然后让 Auto Layout 为两个状态之间的UI设置动画就可以了。

设置约束动画

开始项目 Packing List 大概如下:

系统学习iOS动画之二:自动布局

导航栏高度变化

ViewController 中添加约束接口:

@IBOutlet weak var menuHeightConstraint: NSLayoutConstraint!
复制代码

并让它与导航栏视图的高度约束关联:

系统学习iOS动画之二:自动布局

在右上角加号按钮的Action方法 actionToggleMenu() 中添加:

isMenuOpen = !isMenuOpen
menuHeightConstraint.constant = isMenuOpen ? 200.0 : 60.0
titleLabel.text = isMenuOpen ? "Select Item" : "Packing List”
复制代码

点击加号按钮后导航栏高度变大,并且title变化。

布局变化的动画

继续在 actionToggleMenu() 添加布局变化的弹簧动画:

UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, options: .curveEaseIn, animations: {
    // 强制更新布局
    self.view.layoutIfNeeded()
}, completion: nil)
复制代码

menuHeightConstraint.constant = isMenuOpen ? 200.0 : 60.0 已经更新了约束值,但iOS还没有机会更新布局。通过从动画闭包中调用 layoutIfNeeded() 强制更新布局,可以设置布局中涉及的每个视图的中心和边界。比如table view也随着Menu的收缩或增大而收缩或增大,这就是约束的效果,现在相当于一次设置两个动画:blush:。

效果:

系统学习iOS动画之二:自动布局

旋转

+ 旋转45°变成 x 在上面的动画闭包中添加:

let angle: CGFloat = self.isMenuOpen ? .pi/4 : 0.0
self.buttonMenu.transform = CGAffineTransform(rotationAngle: angle)
复制代码

查看约束

直接用可视化的方式为视图约束添加代码接口(outlet)是相对简单的方式。有的时候不方便在Interfa Builder使用 Control-drag 方式添加接口或者不方便添加有太多outlet,这时可以利用 UIView 提供的 constraints 属性,它是当前视图所有约束的数组。

比如下面代码:

titleLabel.superview?.constraints.forEach { constraint in
    print("-> \(constraint.description)\n")
}
复制代码

打印结果:

-> <NSLayoutConstraint:0x600002d04320 UIView:0x7ff7df530c00.height == 200   (active)>

-> <NSLayoutConstraint:0x600002d02210 UILabel:0x7ff7df525350'Select Item'.centerX == UIView:0x7ff7df530c00.centerX   (active)>

-> <NSLayoutConstraint:0x600002d02a30 UILabel:0x7ff7df525350'Select Item'.centerY == UIView:0x7ff7df530c00.centerY + 5   (active)>

-> <NSLayoutConstraint:0x600002d02d00 H:[UIButton:0x7ff7df715d20'+']-(8)-|   (active, names: '|':UIView:0x7ff7df530c00 )>

-> <NSLayoutConstraint:0x600002d030c0 UIButton:0x7ff7df715d20'+'.centerY == UILabel:0x7ff7df525350'Select Item'.centerY   (active)>
复制代码

看上去有点乱,不过仔细看还是能看出有五个约束分别对应于:

系统学习iOS动画之二:自动布局

设置UILabel的约束动画

actionToggleMenu()isMenuOpen = !isMenuOpen 下添加:

titleLabel.superview?.constraints.forEach { constraint in
    if constraint.firstItem === titleLabel && constraint.firstAttribute == .centerX {
        constraint.constant = isMenuOpen ? -100.0 : 0.0
        return
    }
}                              
复制代码

约束表达式的通用形式如下:

firstItem.firstItemAttribute == secondItem.secondItemAttribute * multiplier + constant
复制代码

对应于 NSLayoutConstraint 的各种属性,名字看着很明显,其中 == 对应于属性 relation ,当然也可以是 <=>= 等。

系统学习iOS动画之二:自动布局

实际例子:

Superview.CenterX = 1.0 * UILabel.CenterX + 0.0
复制代码

这边的效果:

系统学习iOS动画之二:自动布局

替代约束

每个约束可以添加 Identifier 属性,在代码中就可以通过 Identifier 获取这个约束。

系统学习iOS动画之二:自动布局

继续在上面的约束后添加:

if constraint.identifier == "TitleCenterY" {
    constraint.isActive = false
    let newConstraint = NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: titleLabel.superview!, attribute: .centerY, multiplier: isMenuOpen ? 0.67 : 1.0, constant: 5.0)
    newConstraint.identifier = "TitleCenterY"
    newConstraint.isActive = true
    return
}
复制代码

新加的约束可以表示为 Title.CenterY = Menu.CenterY * 0.67 + 0.0 ,图示:

系统学习iOS动画之二:自动布局

运行后效果:

系统学习iOS动画之二:自动布局

添加导航栏内容

actionToggleMenu() 中添加:

if isMenuOpen {
    slider = HorizontalItemList(inView: view)
    slider.didSelectItem = { index in
                            print("add \(index)")
                            self.items.append(index)
                            self.tableView.reloadData()
                            self.actionToggleMenu(self)
                           }
    self.titleLabel.superview!.addSubview(slider)
} else {
    slider.removeFromSuperview()
}
复制代码

HorizontalItemList 是自定义的一个 UIScrollView 子类,用于menu中左右滚动的视图,

系统学习iOS动画之二:自动布局

动态创建视图

当点击TableView的cell时,会调用 showItem(_:) ,在这个方法中添加:

// 点击后创造图片
let imageView = UIImageView(image: UIImage(named: "summericons_100px_0\(index).png"))
imageView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
imageView.layer.cornerRadius = 5.0
imageView.layer.masksToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
复制代码

添加约束代码:

let conx = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
复制代码

此方法使用新的 NSLayoutAnchor 类,这使得创建常见约束非常容易。 在这里,您将在图像视图的中心x锚点和视图控制器的视图之间创建约束。

添加图片底部约束:

let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
复制代码

此约束设置图像视图的底部以匹配视图控制器视图的底部,加上图像高度; 这会将图像定位在屏幕底部边缘之外,这将作为动画的起点。

添加图片宽度约束:

let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
复制代码

这将图像宽度设置为屏幕宽度的1/3减去50磅。 目标尺寸是屏幕的1/3; 你将动画50磅的差异,使图像“成长”到位。

最后,添加高度和宽度相等约束,并激活上面所有约束:

let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
NSLayoutConstraint.activate([conx, conBottom, conWidth, conHeight])
复制代码

此时点击TableView的Cell,只能看到下面:

系统学习iOS动画之二:自动布局

为动态创建的视图创建动画

showItem(_:) 添加:

UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
    conBottom.constant = -imageView.frame.size.height/2
    conWidth.constant = 0.0
    self.view.layoutIfNeeded()
}, completion: nil)
复制代码

但是此时的效果是:

系统学习iOS动画之二:自动布局

**想一想:**添加了一个视图,设置了一些约束,然后改变了这些约束并设置了布局变化的动画。 但是,视图从未有机会执行其初始布局,因此图像从其左上角的 (0, 0) 的默认位置开始 。

要解决此问题,只要在动画开始之前进行初始布局,在动画前添加:

view.layoutIfNeeded()
复制代码

效果变成从下面上来:

系统学习iOS动画之二:自动布局

移出已经出现的图片

上面的弹出图片会重叠在一起,下个图片出来之前,需要把上一个图片移出。

在之前的代码下添加:

UIView.animate(withDuration: 0.8, delay: 1.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
            conBottom.constant = imageView.frame.size.height
            conWidth.constant = -50.0
            self.view.layoutIfNeeded()
        }) { (_) in
            imageView.removeFromSuperview()
        }
复制代码

效果为::stuck_out_tongue_closed_eyes:

系统学习iOS动画之二:自动布局

本文在我的个人博客中地址: 系统学习iOS动画之二:自动布局动画


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning Apache Struts

Beginning Apache Struts

Arnold Doray / Apress / 2006-02-20 / USD 44.99

Beginning Apache Struts will provide you a working knowledge of Apache Struts 1.2. This book is ideal for you Java programmers who have some JSP familiarity, but little or no prior experience with Ser......一起来看看 《Beginning Apache Struts》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具