Drawing前言
view有backgroundColor,可以设置颜色,但是view拥有drawing的属性,我们可以可以更加增添色彩.
你可以事先准备你的drawing as an image file,你可以draw 一个image当你运行你的app的时候,使用code.你可以在一个UIView的子类中展示一张image,比如UIView或者UIButton.一个纯粹的UIView是全部关于drawing的,而且它leaves that drawing 大部分取决于你;你的code决定了这个view怎么draw,也就是它在界面怎么展示.(第10章会讲解怎么draw text)
Images and ImageViews
最基本最常用的UIKit image 类是UIImage, UIImage能够从硬盘读出一个file.所以 一个 image 不需要动态创建,它已经在你运行app之前就创建了,然后,drawing 会尽可能简单地提供一个image file 在你的app 的bundle.系统会知道怎么和许多标准的image file 类型打交道,比如TIFF
, JPEG
, GIF
, PNG
;当一个Image 文件包含在你的app buddle 的时候,iOS 有一个特别的亲和力对png
文件,你也应该更多的使用png
图尽可能的.你也可以获得image data 使用其他的方式,比如下载它,把它转化到UIImage.(相反的操作,保存image data到disk 作为一个image file,将会在23章讨论)
Image Files
一个预存的image文件在你的app bundle中能够通过UIImage 的初始化构造方法init(named:)
来创建.这个方法可以操两个地方取得image
Asset catalog
在asset catalog中虚招提供的name,这个name 是大小写敏感的.
Top level of app bundle
就是文件目录下,也可以,但是需要提供后缀名,.jpg
等,默认是.png
.
当调用 init(named:)
,首先会寻找asset catalog,然后再是app's bundle.如果有多个asset bundle,那么全部会寻找.但是这个search 顺序是没有限制的,而且不能设定,所以不能设置image set 的名字相同.
一个好的事情是init(named:)
,image data 可能会缓存到内存中,如果你再次调用init(named:)
在不久之后,缓存的image data将会马上被供应.非此即彼,你也能直接读一个imge file从你的app bundle 的任何地方不适用缓存,使用init(contentsOfFile:)
,参数是一个pathname string;你可以去得到一个app 的 bundle引用,使用Bundle.main
,Bundle然后提供实例方法去取得pathname of a file,比如 path(forResource:ofType)
.
指定一个资源在app bundle中,比如init(named:)
和path(forResource:ofType)
,respond to suffixes in the name of an actual resource file.
这样,你的app 能够包含多种版本的versions of an image file在不同的解决方法中.感谢 scale
属性,a high-resolution version of an image is drawn at the same size as the single-resolution image. 因此,在一个高分辨率的屏幕上,你的code持续运行without 改变,但是你的图片看起来就像魔术一样,是好的.
同样的,一个文件拥有一个~ipad
的延展,会自动被用在iPad上面,当运行的时候.
在asset catalog 的巨大收益之一上面,可是,你能够忘记所有的后缀的计量单位吗. 一个asset catalog 知道多久去用一个交替image在一个image set 中, 不是从他的名字里面,而是从他的catalog的地方.分别放1.2.3.倍图到asset里面,取名1x,2x,3x.对于一个明显的iPad 版本 of an image,检查iPhone 和 iPad 在 attributes inspector for the image set;sepate slots for those deveice types will appear in the asset catalog.
一个asset catalog 也能够在不同的image 版本中区分 for 不同的size class 境况下.(第一章讲的 size classes 和 trait collections).
在iamge set 的 Attributes inspector 中,使用Width Class 和Height Class 的选择器去指定哪种size class 你想去区分的.因此,比如,如果我们在一个一个iPhone with the app 旋转到landscape 方向,and 如果 there's both an Any Height and a Compact Height alternative 在 image set中, the Compact Height version is used.These features are live as the app runs; if the app 从landscape旋转到portrait,and there's both an Any heihgt and a Compact Height alternative in the image set, the Compact Height version is replaced with the Any Height version in your interface, there and then, automatically.
(!?)注意, Any Height , Compact Height,size classes:见下面的自适应布局.
自适应布局(Adaptive Layout):
自适应布局的介绍引发了ios 开发者们的一个绝大的翻遍。现在,当你设计你的app的时候,你已经可以使用一个单独布局并且不添加任何设备适配的代码,使你的应用工作到所有的iOS设备上面!
storyboard, size classes, layout, font 自定义,preview assistant editor(预览助手编辑器)
一个storyboard可以同时被用于iPad 和iPhone 设备.
storyboard默认是一个接近于正方形的矩形,是一个抽象的尺寸替代的.
注意:size classes
在iOS10中称之为 Trait Variation
注意:
下面可以使小于等于多少.
使用storyboard适配还是有一些不足,比如适配iPad和iPhone等等,
使用自适应布局(Adaptive Layout)可以有很多方法去弥补storyboard的适配不足.
自适应布局(Adaptive Layout)的一个很重要的核心改变,size classes. Size Classes 是一个可以用用于任何能够在横向或者纵向进行显示的视图或者视图控制器的属性.
Xcode使用了2种Size Classes: Regular(常规)和Compact(紧凑).
经管他们与视图的物理尺寸是有关联的,但是他们仍可以用一种语义上的尺寸呈现出来。
这张图应该可以理解吧,我6p的高度是很高的,所以在landscape的时候,可以满足regular.
这些都是设备可以应用的size classe。当然,你也可以在视图结构中的任意一部分中重写size classe
(听起来很不错哦!!)。这在比屏幕要小的容器中是相当有用的。
Size Classes 和 开发者的关系
尽管你的app是有size classes的,但是你构建的布局和size class 是无关的,也就是说,你的布局需要为所有的size class进行调整.
在涉及到自适应布局的设计的时候,这是很重要的一点,首先你需要创建一个基本的布局,然后在为每一个特殊的size class去调整你需要的size class。这里,不是要把每一个size class视为一个全新的东西去从头设计,你可以吧一个自适应的布局,想象为一个树状的层次结构,在这个层次中,你可以吧所有通用的设计放在父节点中,然后把需要单独处理的部分放到子级的size class中.
目前为止,我们几乎还没有提到具体的设备布局适配问题。这是因为在自适应布局的核心概念中,size class是抽象于具体设备的。意思是说,一个支持自适应布局的视图,可以在一个完整的屏幕中显示,也可以被包含于另外一个视图控制器中显示.
这样做的好处是,设备的屏幕返回扩大了,也不需要迫使程序的开发者或者设计人员重新设计他们的 app。
现在我们将使用size class 来布局 iPhone 的lanscape,以解决之前的问题.
使用 Size Class
w Any h Any
在Xcode8中改变了, The UI to select the size classes has changed in xcode 8.
任何你添加的约束没有选择Vary for Traits
这个选项将会被认为是 for all the size classes(Any Any previously).
To add a constraint to a specific size class add it by selecting:
- The device at the bottom and by selecting Vary for Traits options of that size class.
- When you are adding constrants to a specific size class the bar turns blue as below.
在这里你可以通过选择表格单元的行事来选择使用哪种size class来呈现.一共有9个可能的选项:你有三个纵向的选择和三个水平方向的选择(any(任意), regular(正常), compact(紧密)),3*3就有九中size class.
size class通常和horizontal(水平)与vertical(垂直)联系起来的.但是,在这里却用了width 和height. width == horizontal height == vertical
我们现在的布局(就是前面说的问题)还不能很好的工作于compact(紧密)的高上面。
还需要注意size class 的知识点:
1)size class是什么?
iOS8 引入的一种对布局的自适应的解决方案,可以从下面两个地方理解;
- 对于不同的设备的屏幕股尺寸,包括横屏和竖屏,我们可以设定不同的view布局,
系统会检测到具体的设备,帮我们自动切换对应的布局
.
对于一套布局,系统会检测到具体的设备和屏幕方向,遵循autolayout的设定,自动布局。于是我们可以无视具体设备的尺寸和屏幕方向
,根据size classes的规定来布局了. - 在iOS8之后,苹果定义所有的设备的size class只分为2种属性:详见枚举UIUserInterfaceSizeClass:(1)Regular (2)Compact
Any 包含了上面的2种情况.所以nib
中的width:Any
和height:Any
代表:包含了所有情况.
2)trait -UITraitCollection 和其接口UITraitEnvironment
trait:特征的意思.
UITraitCollection也就是关于尺寸,设备屏幕的一些列属性的集合.
UITraitCollection 是接口UITraitEnvironment 的属性,它包含了设备关于屏幕的众多属性.trait,size class也是其中一个属性.
- 水平和垂直上的size class, horizontalSizeClass,verticalSizeClass
- 显示缩放比 displayScale
displayScale (一个逻辑点会映射多少个像素 Point to Pixel:retina 2.0, non retina 1.0, 5.5 Inch iPhone 3.0) 也就是iphone 5 ,6这些retina 屏幕是2x, 6p,7p是3x. - 用户设备的分类: userinterfaceIdiom(枚举UIUserInterfaceIdiom)
public enum UIUserInterfaceIdiom : Int {
case unspecified //未定义
case phone // iPhone and iPod touch style UI
case pad // iPad style UI
case tv // Apple TV style UI apple做电视和车载,我还没看到大陆哪里有买哇-_-
case carPlay // CarPlay style UI
}
UIScreen, UIWindow, UIViewController, UIPresentationController,UIView都实现了UITraitEnvironment接口,因此可以很方便的直接取用:
self.traitCollection
有二个函数在trait 发生改变的时候被调用:
-
public func traitCollectionDidChange(_previousTraitCollection:UITraitCollection?)
它是接口UITraitEnvironment的方法,上面提到的UIScreen,UIWindow,UIViewController,UIPresentationController,UIView都可以实现它.当trait发生改变的时候,进入这个方法,我们通过判断其属性改变布局:
//MARK: UITraitEnvironment
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
//iphone 横屏
if previousTraitCollection?.horizontalSizeClass == .compact && previousTraitCollection?.verticalSizeClass == .compact{
print("iPhone从横屏->竖屏")
}
else if (previousTraitCollection?.horizontalSizeClass == .compact && previousTraitCollection?.verticalSizeClass == .regular){
print("iPhone从竖屏->横屏")
}
}
注意:1)previousTraitCollection是之前的设备状态,不是当前的,而且有可能是nil,比如程序最开始启动的时候还没有之前状态 。 2)横竖屏切换记得先打开手机设置中的横竖屏开关.
-
public func willTransition(to newCollection:UITraitCollection,with coordinator:UIViewControllerTransitionCoordinator)
这个是接口UIContentContainer的方法,UIViewController和UIPresentationController都实现了它,它也是在trait改变时候进入的,它先于traitCollectionDidChange杯垫调用.