Size Classes with Xcode 6

本文深入解析了iOS中的SizeClasses概念,介绍了其如何简化不同设备尺寸的UI适配问题,并通过实例展示了如何在Interface Builder中利用SizeClasses调整布局。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、基本概念

在 iPad 和 iPhone 5 出现之前,iOS 设备就只有一种尺寸。我们在做屏幕适配时需要考虑的仅仅有设备方向而已。而很多应用并不支持转向,这样的话就完全没有屏幕适配的工作了。随着 iPad 和 iPhone 5,以及接下来的 iPhone 6 的推出,屏幕尺寸也变成了需要考虑的对象。在 iOS 7 之前,为一个应用,特别是 universal 的应用制作 UI 时,我们总会首先想我们的目标设备的长宽各是多少,方向变换以后布局又应该怎么改变,然后进行布局。iOS 6 引入了 Auto Layout 来帮助开发者使用约束进行布局,这使得在某些情况下我们不再需要考虑尺寸,而可以专注于使用约束来规定位置。

既然我们有了 Auto Layout,那么其实通过约束来指定视图的位置和尺寸是没有什么问题的了,从这个方面来说,屏幕的具体的尺寸和方向已经不那么重要了。但是实战中这还不够,Auto Layout 正如其名,只是一个根据约束来进行布局的方案,而在对应不同设备的具体情况下的体验上还有欠缺。一个最明显的问题是它不能根据设备类型来确定不同的交互体验。很多时候你还是需要判断设备到底是 iPhone 还是 iPad,以及现在的设备方向究竟是竖直还是水平来做出判断。这样的话我们还是难以彻底摆脱对于设备的判断和依赖,而之后如果有新的尺寸和设备出现的话,这种依赖关系显然显得十分脆弱的(想想要是有 iWatch 的话..)。

所以在 iOS 8 里,Apple 从最初的设计哲学上将原来的方式推翻了,并引入了一整套新的理念,来适应设备不断的发展。这就是 Size Classes。

不再根据设备屏幕的具体尺寸来进行区分,而是通过它们的感官表现,将其分为普通 (Regular) 和紧密 (Compact) 两个种类 (class)。开发者便可以无视具体的尺寸,而是对这这两类和它们的组合进行适配。这样不论在设计时还是代码上,我们都可以不再受限于具体的尺寸,而是变成遵循尺寸的视觉感官来进行适配。

 

Size Classes有三个值:Regular,Compact 和Any。Any是什么意思呢?如果weight设为Any,height设置为Regular,那么在该状态下的界面元素在只要height为Regular,无论weight是Regular还是Compact的状态中都会存在。这种关系应该叫做继承关系,具体的四种界面描述与可继承的界面描述如下:

w:Compact h:Compact 继承 (w:Any h:Compact , w:Compact h:Any , w:Any h:Any)

w:Regular h:Compact 继承 (w:Any h:Compact , w:Regular h:Any , w:Any h:Any)

w:Compact h:Regular 继承 (w:Any h:Regular , w:Compact h:Any , w:Any h:Any)

w:Regular h:Regular 继承 (w:Any h:Regular , w:Regular h:Any , w:Any h:Any)

 

这么多设备(iPhone4S,iPhone5/5s,iPhone6,iPhone6 Plus,iPad,Apple Watch)的尺寸,就通过Size Classes简单的表达出来了:

  • iPhone4S,iPhone5/5s,iPhone6
    • 竖屏:(w:Compact h:Regular)
    • 横屏:(w:Compact h:Compact)
  • iPhone6 Plus
    • 竖屏:(w:Compact h:Regular)
    • 横屏:(w:Regular h:Compact)
  • iPad
    • 竖屏:(w:Regular h:Regular)
    • 横屏:(w:Regular h:Regular)
  • Apple Watch(猜测)
    • 竖屏:(w:Compact h:Compact)
    • 横屏:(w:Compact h:Compact)

PS:附上图形:



 

 

 

 

 

 

2、UITraitCollection 和 UITraitEnvironment (Size Classes手写代码)

为了表征 Size Classes,Apple 在 iOS 8 中引入了一个新的类,UITraitCollection。这个类封装了像水平和竖直方向的 Size Class 等信息。iOS 8 的 UIKit 中大多数 UI 的基础类 (包括UIScreenUIWindowUIViewController 和 UIView) 都实现了 UITraitEnvironment 这个接口,通过其中的 traitCollection 这个属性,我们可以拿到对应的 UITraitCollection 对象,从而得知当前的 Size Class,并进一步确定界面的布局。

和 UIKit 中的响应者链正好相反,traitCollection 将会在 view hierarchy 中自上而下地进行传递。对于没有指定 traitCollection 的 UI 部件,将使用其父节点的 traitCollection。这在布局包含 childViewController 的界面的时候会相当有用。在 UITraitEnvironment 这个接口中另一个非常有用的是 -traitCollectionDidChange:。在 traitCollection 发生变化时,这个方法将被调用。在实际操作时,我们往往会在 ViewController 中重写 -traitCollectionDidChange: 或者 -willTransitionToTraitCollection:withTransitionCoordinator: 方法 (对于 ViewController 来说的话,后者也许是更好的选择,因为提供了转场上下文方便进行动画;但是对于普通的 View 来说就只有前面一个方法了),然后在其中对当前的 traitCollection 进行判断,并进行重新布局以及动画。代码看起来大概会是这个样子:

 

    override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator){
        super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)
    
        coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext!) -> Void in
            if (newCollection.verticalSizeClass == UIUserInterfaceSizeClass.Compact) {
                //To Do: modify something for compact vertical size
            } else {
                //To Do: modify something for other vertical size
            }
            self.view.setNeedsLayout()
        }, completion: nil)
    }

 

在两个 To Do 中,我们应该删除或者添加或者更改不同条件下的 Auto Layout 约束 (当然,你也可以干其他任何你想做的事情),然后调用 -setNeedsLayout 来在上下文中触发转移动画。如果你坚持用代码来处理的话,可能需要面临对于不同 Size Classes 来做移除旧的约束和添加新的约束这样的事情,可以说是很麻烦 (至少我觉得是麻烦的要死)。但是如果我们使用 IB 的话,这些事情和代码都可以省掉,我们可以非常方便地在 IB 中指定各种 Size Classes 的约束 (稍后会介绍如何使用 IB 来对应 Size Classes)。另外使用 IB 不仅可以节约成百上千行的布局代码,更可以从新的 Xcode 和 IB 中得到很多设计时就可以实时监视,查看并且调试的特性。可以说手写 UI 和使用 IB 设计的时间消耗和成本差距被进一步拉大,并且出现了很多手写 UI 无法实现,但是 IB 可以不假思索地完成的任务。从这个意义上来说,新的 IB 和 Size Classes 系统可以说无情地给手写代码判了个死缓。

另外,新的 API 和体系的引入也同时给很多我们熟悉的 UIViewController 的有关旋转的老朋友判了死刑,比如下面这些 API 都弃用了:

@property(nonatomic, readonly) UIInterfaceOrientation interfaceOrientation

- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:
- shouldAutomaticallyForwardRotationMethods

现在全部统一到了 viewWillTransitionToSize:withTransitionCoordinator:,旋转的概念不再被提倡使用。其实仔细想想,所谓旋转,不过就是一种 Size 的改变而已,我们都被 Apple 骗了好多年,不是么?

3、Interface Builder 中使用 Size Classes

创建一个新的通用项目。如果你想要早在一个已经创建了的Xcode6项目,你需要激活size classes选项。你可以在Interface Builder中的属性面板 勾选autolayout 的选项的下面找到它。



 首先,让我们在Xcode中看一下size class的网格。这是一个你可以在不同的布局排列间切换的区域。当你查看storyboard的时候,看到视图的底部,并且点击‘wAny hAny’字样的标签。你将会看到一些类似网格的画面。



 默认的,我们以一个基础的设置开始,也就是any width和any height。很多事情都将在这里安置和改变,包括了iphone和ipad的所有方向的默认布局。苹果建议把大多数的设置都在这个界面中进行设置。这个是因为减少工作量而显得特别的简单。让我们布局一个超级宽的按钮在画面的中间。给它一个绿色的背景,从而让我们看到它真实的尺寸,给它一个约束来让他居中。



 并且给它一个夸张的固定宽度600。



 好了,现在在ipad和iphone的模拟器都运行一下,你将会看到都是居中,但对于iphone的两个方向都太宽了,(这里你设置了页面中button的宽度 但并没有马上更新是因为 你在做添加约束的时候没有更新图形,导致了如下图的情况,storyboard里面没有更新,而在模拟器运行时候更新了,左边大纲栏目里面也有警告说明,可以直接点击警告里面的黄色三角来更新画面其实就是 Updata Frame)



让我们使用size classes来修正吧。回到刚才那个第一张图的网格选择iphone的纵向(portrait)设置,就是紧凑的宽度+ 常规的高度。网格中的红色矩形。

 

 

你将会注意到你在网格中选中之后底部的bar改变为蓝色。那是在警告你:“Hey ,你并不是在一个基础的设置,有些改变将会只在你运行的时候显示。所以这个bar现在是蓝色的!” 我所说的一些改变是因为有四项你能改变的size classes:1约束常数,2字体,3约束的开/关,4子视图的开/关。

前两个是不言而喻的,但是让我来告诉你如何让后两者工作。在当前的size class (compact width 和 regular height)状况下让我们试着把一个约束关闭。在文档的提纲栏里,点击设置在我们的button的Centre X 校准约束:



 现在看一下我们的属性检查栏,在底部我们可以看到带标记的一个单词“Installed”,并且左侧有额外的加号按钮。点击额外的加号并且点选'Compact Width| Regular Height'(当前的就是)。

现在你将会看到2个标记物,把刚刚添加的哪一个取消勾选(wC hR)



 现在我们的约束不再安置并且做任何事情来配置size classes。就像你看到的,Xcode正在控诉我们的约束太混乱了(左边的大纲会有错误提示表示你缺少了约束-译者),如果你这时候运行app在iphone的模拟器上的话,按钮不在X方向居中了。但是在ipad的上面还是居中的,因为约束仍然安置在基本的设置里面。这个约束将会一直配置着除非我们把它取消勾选。你甚至能够旋转你的iphone模拟器,并且发现button将会神奇的回到居中,因为iphone的横向是不同的size class配置,好了,让我们把勾选回来,让button回到居中。

现在让我们改变我们设置在button宽度的约束,选择button,并且来到Size的属性检查栏,下拉到底部,我们可以看到所有的约束。点击Width原本是600的 使用Edit设置为100:



 在iPhone的模拟器上运行,你将会看到button已经具备了正确的宽度。运行在ipad的模拟器的时候却展示了600的宽度,因为我们没有改变基本设置里面的宽度。但是,在iphone的横向landscape仍然看着不怎么样,因为iphone的横向设置来自基本的Any Any 的设置。让我们修正一下。在网格里面我们选择compact Width和Compact Height。也就是第一张图的蓝色网格。

 

现在我们在这个设置下改变width 的约束,就像我们为了compact x regular改变的一样。给予一个400的宽度。运行一下iphone的模拟器,并且旋转到横向,按钮有了400的宽度,看上去很棒。达到了我们的预想。有一点很好就是你能看到一个所有的约束的列表,这些都是不同的设置的。仅仅选择你想要在文档大纲里面看到的约束,然后来到属性检查栏,他们整齐的排列在初始的常数下面。它标注了每一个基于它所应用的设置。

 

即使我们决定我们想要只在iphone横向landscape模式下button消失,使用size classes 我们只要反向安置views就像我们反向安置一个约束。选择我们的UIbutton,滚动到属性检查器的底部。通过点击 加号按钮 给我们当前的设置添加一个新的安置选项,然后取消勾选它。

 

就像你看到的,那个view 立马消失了,因为我们在设置里面反向安置了它,我们立马就能看到。运行app,你能看到它在纵向的portrait iphone上消失了,但是当你旋转到横向的landscape的时候又回来了。当然它也一直安置在ipad上面因为ipad仍然使用的是基本的设置。

 

4、Size Classes 和 Image Asset 及 UIAppearence

Image Asset 里也加入了对 Size Classes 的支持,也就是说,我们可以对不同的 Size Class 指定不同的图片了。在 Image Asset 的编辑面板中选择某张图片,Inspector 里现在多了一个 Width 和 Height 的组合,添加我们需要对应的 Size Class, 然后把合适的图拖上去,这样在运行时 SDK 就将从中挑选对应的 Size 的图进行替换了。不仅如此,在 IB 中我们也可以选择对应的 size 来直接在编辑时查看变化。



实际做起来实在是太简单了..但拿个 demo 说明一下吧,比如下面这个实现了竖直方向 Compact 的时候将笑脸换成哭脸 -- 当然了,一行代码都不需要。



  另外,在 iOS 7 中 UIImage 添加了一个 renderingMode 属性。我们可以使用imageWithRenderingMode: 并传入一个合适的 UIImageRenderingMode 来指定这个 image 要不要以Template 的方式进行渲染。在新的 Xcode 中,我们可以直接在 Image Asset 里的 Render As 选项来指定是不是需要作为 template 使用。而相应的,在 UIApperance 中,Apple 也为我们对于 Size Classes 添加了相应的方法。使用 +appearanceForTraitCollection: 方法,我们就可以针对不同 trait 下的应用的 apperance 进行很简单的设定。比如在上面的例子中,我们想让笑脸是绿色,而哭脸是红色的话,不要太简单。首先在 Image Asset 里的渲染选项设置为 Template Image,然后直接在 AppDelegate 里加上这样两行:

UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Compact)).tintColor = UIColor.redColor()  
UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Regular)).tintColor = UIColor.greenColor()  

 



 

完成,只不过拖拖鼠标,两行简单的代码,随后还能随喜换色,果然是大快所有人心的大好事。

 

 

感谢:

http://www.cnblogs.com/fengquanwang/p/3998526.html

http://onevcat.com/2014/07/ios-ui-unique/

http://www.cnblogs.com/wfwenchao/p/4015333.html

http://www.cocoachina.com/ios/20140926/9766.html

 

 

 

  • 大小: 14.2 KB
  • 大小: 14.2 KB
  • 大小: 14 KB
  • 大小: 13.7 KB
  • 大小: 14 KB
  • 大小: 23.6 KB
  • 大小: 46.2 KB
  • 大小: 296.4 KB
  • 大小: 170.1 KB
  • 大小: 14.1 KB
  • 大小: 66.4 KB
  • 大小: 75.2 KB
  • 大小: 9.3 KB
  • 大小: 29.7 KB
  • 大小: 33.2 KB
  • 大小: 193.6 KB
  • 大小: 169.7 KB
内容概要:该研究通过在黑龙江省某示范村进行24小时实地测试,比较了燃煤炉具与自动/手动进料生物质炉具的污染物排放特征。结果显示,生物质炉具相比燃煤炉具显著降低了PM2.5、CO和SO2的排放(自动进料分别降低41.2%、54.3%、40.0%;手动进料降低35.3%、22.1%、20.0%),但NOx排放未降低甚至有所增加。研究还发现,经济性和便利性是影响生物质炉具推广的重要因素。该研究不仅提供了实际排放数据支持,还通过Python代码详细复现了排放特征比较、减排效果计算和结果可视化,进一步探讨了燃料性质、动态排放特征、碳平衡计算以及政策建议。 适合人群:从事环境科学研究的学者、政府环保部门工作人员、能源政策制定者、关注农村能源转型的社会人士。 使用场景及目标:①评估生物质炉具在农村地区的推广潜力;②为政策制定者提供科学依据,优化补贴政策;③帮助研究人员深入了解生物质炉具的排放特征和技术改进方向;④为企业研发更高效的生物质炉具提供参考。 其他说明:该研究通过大量数据分析和模拟,揭示了生物质炉具在实际应用中的优点和挑战,特别是NOx排放增加的问题。研究还提出了多项具体的技术改进方向和政策建议,如优化进料方式、提高热效率、建设本地颗粒厂等,为生物质炉具的广泛推广提供了可行路径。此外,研究还开发了一个智能政策建议生成系统,可以根据不同地区的特征定制化生成政策建议,为农村能源转型提供了有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值