恶梦

想象你正在干活,你的上级要求你在工程中加入一些图片。你找到团队中的设计师(如果你们有一个的话),鼓起勇气,问他要这些图片。不过,通常他都会忙得根本没时间帮你。因为他要做的活儿比你还多。说实话,你到底见过一个设计师有多少活儿要做吗?!简直多到令人发指啊!所以你最不想做的就是成为那个再给他加活儿的人了。特别是那些活儿对设计师来说简单得让人厌烦。更不用说,设计师也要按顺序干活,你拿到那些图片也是几天之后了。所以我们还是来看看如何用Xcode 的Asset Catalog来处理这些麻烦事吧。

麻烦事No.1:“能改一下这个图片的颜色吗?”

目前为止iOS已经提供了一些相当复杂的方法来处理工程中的图片,但很不幸的是,却没有用来处理这种情况的方法。我已经数不清楚有多少次我们因为什么便捷性支持或者是客户不喜欢而改变工程的颜色了。现在在工程中全局改变UIColor已经不是什么难题了,但是我们还要改变工程中图片的颜色。这样我们就不得不回去找已经忙得要死的设计师了,就因为我们太懒而不去学学怎么用Photoshop。当然我们也可以写代码来完成,不过看起来相当复杂,而且还容易造成泄漏。在iOS7 之前,写代码改变图片的颜色和下面差不多(还有很多其他的方法):

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
class func image(name: String, withColor color: UIColor) -> UIImage? {
     if var image = UIImage(named: name) {
         // begin a new image context, to draw our colored image onto. Passing in zero for scale tells the system to take from the current device's screen scale.
         UIGraphicsBeginImageContext(image.size, false, 0)
          
        // get a reference to that context we created
         let context = UIGraphicsGetCurrentContext()
          
        // set the context's fill color
         color.setFill()
          
        // translate/flip the graphics context (for transforming from CoreGraphics coordinates to default UI coordinates. The Y axis is flipped on regular coordinate systems)
         CGContextTranslateCTM(context, 0.0, image.size.height)
         CGContextScaleCTM(context, 1.0, -1.0)
          
        // set the blend mode to color burn so we can overlay our color to our image.
         CGContextSetBlendMode(context, kCGBlendModeColorBurn)
         let rect = CGRect(origin: CGPointZero, size: image.size)
         CGContextDrawImage(context, rect, image.CGImage)
          
        // set a mask that matches the rect of the image, then draw the color burned context path.
         CGContextClipToMask(context, rect, image.CGImage)
         CGContextAddRect(context, rect)
         CGContextDrawPath(context, kCGPathFill)
          
        // generate a new UIImage from the graphics context we've been drawing onto
         image = UIGraphicsGetImageFromCurrentImageContext()
         UIGraphicsEndImageContext()
          
    return image
     }
     return nil
 }

在两个大版本升级之后,我还能看到这样的代码。在iOS7中,我们有了imageWithRenderingMode,这是UIImage的一个方法,参数是有三个选项的枚举值UIImageRenderingMode。

1
2
3
4
5
typedef NS_ENUM(NSInteger, UIImageRenderingMode) {
     UIImageRenderingModeAutomatic,          // Use the default rendering mode for the context where the image is used
     UIImageRenderingModeAlwaysOriginal,     // Always draw the original image, without treating it as a template
     UIImageRenderingModeAlwaysTemplate,     // Always draw the image as a template image, ignoring its color information
 } NS_ENUM_AVAILABLE_IOS(7_0);
  • UIImageRenderingModeAlwaysOriginal  就和字面的意思一样,这个模式告诉系统按照图片文件原来的样子渲染图片。

  • UIImageRenderingModeAlwaysTemplate 这是最有意思的模式。首先会扫描你的图片,然后从图片中所有不透明的像素创建一个模板。这同时也会忽略图片的所有颜色信息。你可以使用UIView子类的tintColor属性来给图片填充你选择的颜色。

  • UIImageRenderingModeAutomatic 这个模式由系统根据图片的使用环境来决定如何渲染图片。如果你的图片是用在比如UITabBar、UINavigationBar、UIToolbar 和UISegmentedControl这些地方,图片使用AlwaysTemplate渲染模式。图片用在其他的地方则会使用AlwaysOriginal渲染模式。

在了解了上面的内容之后,之前我们改变图片颜色的代码就可以简化成下面这样的了:

1
2
3
4
if var imageToChange = imageView.image?.imageWithRenderingMode(.AlwaysTemplate) {
     imageView.image = imageToChange
     imageView.tintColor = .redColor() //Setting the tint color is what changes the color of the image itself!
 }

是不是很神奇?用代码改变图片的颜色,现在变得简单多了。

等等,还没完呢!其实不需要代码也可以改变图片的颜色。

从Xcode 6开始,imageWithRenderingMode已经集成到Asset Catalog里了。如果你在Asset Catalog里选择了一个图片,在右边的Attributes Inspector里,就可以像下图那样把Render As选项改成Template Image。

就是这么简单。甚至你还可以通过在Storyboard中,在Attributes Pane中改变UIImageView的tintColor属性,来改变imageView中的图片的颜色。

麻烦事No.2:“能给我这个的3X分辨率的图吗?”

这个的确很烦,因为每个app的每个设计师被问这个问题都至少一年了。貌似苹果每年都会增加一种新的屏幕分辨率,今年我也持怀疑态度。随着硬件技术的发展,苹果总是走在前沿,总是在尽可能地提高屏幕的ppi。很不幸,这意味着我们不能直接在“预览”中放大已有的图片,因为这会造成诸如图片像素化和产生锯齿等问题。通俗点,就是我们的图片变丑了,噁!每次你叫设计师出一张已有图片的3x分辨率图,某个地方就又要死一只独角兽了。这实际上也解释了为什么现在看不到这种神奇的生物了。

所以去年我在WWDC上提到的最好的消息,就是Xcode 6 及以上版本支持在Asset Catalog中使用矢量PDF了。你的设计师知道这是什么意思,但是大致上,PDF是矢量元素的事实标准。矢量文件包含一个元素的很多元数据,用来告诉系统如何渲染这些内容,而这些和屏幕分辨率无关。举个通俗易懂例子,一个圆形的矢量PDF图,当它渲染成5像素宽和渲染成5000000像素宽时是一样清晰的。

在iOS平台,Xcode是在编译时,根据你的矢量PDF图的大小,生成1x、2x和3x图。如果你的PDF图是45*45px,那么Xcode会在编译时生成下面3个PNG:

  • 45*45px      :1x设备用的(iPhone 3G and 3GS)

  • 90*90px      :2x或Retina显示设备用的(iPhone 4, 4S, 5, 5S, and 6)

  • 135*135px  :3x设备用的(iPhone 6 Plus 及以上)

这也意味着当有更高的屏幕分辨率时,Xcode可以根据已有的矢量PDF放大图片,这样自动就支持以后的设备了。还有,如果你是OS X开发者,那么矢量PDF就更好用了,OS X app完全支持矢量PDF,你可以用代码缩放图片而不会失真。

而你需要做的就是,找你的好基友设计师拿到这些矢量PDF文件,然后在Asset Catalog的Attribtues Pane中,在Scale Factor的下拉框中选择Single Vector就行了。

你可以直接把PDF拖到Asset Catalog中,然后进行设置。

麻烦事No.3:“能给我新设备的启动图吗?”

启动图对于app来说还是蛮重要的。这是启动app后最先看到的,它会给用户一个app其余部分是如何设计的第一印象。如果我看到一个设计得很糟糕的启动图,我会认为app其他地方也好不到哪去,当然这只是我的情况。对于我们那可怜的设计师来说,每次有新设备出来时,他们都知道要放大启动图来支持新设备的分辨率。对于iPhone 6 和 iPhone 6 Plus,如果你没有为这两个设备准备对应的启动图,那么app就会工作在放大模式。启动图还在Asset Catalog中,但是我建议把它拆出来,因为启动图也升级了。现在,你可以使用LaunchScreen xibs。

在工程文件中,你可以指定app在启动时加载的xib,这样你就不需要准备9张启动图了。LaunchScreen.xib还支持自动布局,这样我们就能分块构建启动屏幕了。按如下这样设置:

首先创建一个xib文件。你可以在如下图所示的地方选择Launch Screen类型的xib。

然后打开工程文件,选择app的target,在Launch Screen file处选择你的Launch Screen .xib文件。

尽可能地利用Launch Screen吧。你肯定不想被抓到在问设计师要一年后新出的手机平板的8x启动图。

麻烦事No.4:“能把这些按钮的图片拉长一点吗?”

这种情况发生的概率比你想象的要高得多。对于一张pattern image或者是有圆角的图片,考虑到有更大的屏幕,你需要重新调整图片的大小,以免图片拉伸出现失真。Natasha发布了一篇很棒的文章来说明如何编程解决这个问题,但是我们也可以在Xcode 6的Asset Catalog中搞定它。顺便说一下,我强烈建议你在继续往下读之前,看一下Natasha的文章,这样你就能理解到底发生了什么。免责声明:下面的图片等是直接从Natasha的文章中拷贝过来的。Sorry!

好了,我们继续。

在之前,一般用类似下面的代码来获得可改变大小的图片:

1
2
3
4
let edgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
let backgroundButtonImage = UIImage(named:"purple_button")?.resizableImageWithCapInsets(edgeInsets)
  
purpleButton.setBackgroundImage(backgroundButtonImage, forState: .Normal)

这将会得到一张和下面类似的图片:

在运行时,会拉伸距离UIImageView的container的边框8像素的中间部分,这样就能保留圆角,得到下面这样的:

多亏了Xcode中Asset Catalog的slice和dice,我们不需要代码也能拉伸图片。首先在Xcode中选中图片,然后点击右下角的Show Slicing:

你现在应该能看到slicing 面板和一个按钮"Start Slicing"。

在你点击按钮之后,会显示下面的三个选项:

左边的按钮用于horizontal edge insets,右边的按钮用于vertical edge insets,中间的则是两个都有。在我们的例子中要保留圆角,所以我们按中间的按钮,告诉系统我们想要按钮的中间在水平和垂直方向拉伸。在按下按钮之后,就能看到一些可以拖动的细条,这可以设置从哪里开始拉伸图片。

系统会保留深紫色的区域,浅紫色的区域会被拉伸。

更厉害的是,Xcode自动找到了圆角,所以我们不需要设置从哪里开始拉伸图片。最后别忘了在Attribtues pane中设置图片是可拉伸的。

如果我是你的话,我就会尝试并习惯这个功能。有了这个无价之宝,你就不用再在resizableImageWithCapInsets方法中填写那些神奇的数字了,也能帮助你分离view逻辑和app逻辑。

结论

我很确定,我们开发者几乎每天都还会做很多其他事去麻烦设计师们,但至少我们能多用用这些功能,让他们稍微休息一会儿。毕竟编程能解决一切问题,何乐而不为呢?

【译】4个你需要知道的Asset Catalog的秘密的更多相关文章

  1. 关于WPF你应该知道的2000件事

    原文 关于WPF你应该知道的2000件事 以下列出了迄今为止为WPF博客所知的2,000件事所创建的所有帖子. 帖子总数= 1,201 动画 #7 - 基于属性的动画 #686 - 使用动画制作图像脉 ...

  2. [技术翻译]您应该知道的13个有用的JavaScript数组技巧

    本次预计翻译三篇文章如下: 01.[译]9个可以让你在2020年成为前端专家的项目 02.[译]预加载响应式图像,从Chrome 73开始实现 03.[译]您应该知道的13个有用的JavaScript ...

  3. 程序员必须要知道的Hadoop的一些事实

    程序员必须要知道的Hadoop的一些事实.现如今,Apache Hadoop已经无人不知无人不晓.当年雅虎搜索工程师Doug Cutting开发出这个用以创建分布式计算机环境的开源软...... 1: ...

  4. 【转载】在IT界取得成功应该知道的10件事

     在IT界取得成功应该知道的10件事 2011-08-11 13:31:30 分类: 项目管理 导读:前面大多数文章都是Jack Wallen写的,这是他的新作,看来要成为NB程序员还要不停的自我总结 ...

  5. 理工科应该的知道的C/C++数学计算库(转)

    理工科应该的知道的C/C++数学计算库(转) 作为理工科学生,想必有限元分析.数值计算.三维建模.信号处理.性能分析.仿真分析...这些或多或少与我们常用的软件息息相关,假如有一天你只需要这些大型软件 ...

  6. 你应该知道的10个奇特的 HTML5 单页网站

    网页设计师努力寻找新的方式来展现内容.其中一个大的趋势是单页网站,现在被世界上的一些大的品牌广泛采用,使用它们来为用户提供一个快速,干净和简单的而且​​美丽的网站. 下面是10个令人惊叹的单页 H​​ ...

  7. Git / 程序员需要知道的12个Git高级命令

    众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...

  8. 你应该知道的RPC原理

    你应该知道的RPC原理 在学校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互 ...

  9. 希望早几年知道的5个Unix命令

    原文: http://spin.atomicobject.com/2013/09/09/5-unix-commands/ 希望早几年知道的5个Unix命令 使用*nix系统已经有一段时间了.但是还是有 ...

随机推荐

  1. linux-多线程

    一.什么是线程? 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立执行的基本单位.线程自己基本上不拥有系统资源,仅仅拥有一点在执行中不可缺少的资源(如程序计数器,一组寄存器和 ...

  2. paip. mysql如何临时 暂时 禁用 关闭 触发器

    paip. mysql如何临时 暂时 禁用 关闭 触发器 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn ...

  3. H5上传文件

    XMLHttpRequest 在Html5 规范中已经有全新的变化,规定了XMLHttpRequest Level 2规范(目前最新版本)包含下列新的特性: 处理字节流,例如作为上传或者下载的File ...

  4. php插件机制实现原理

    插件,亦即Plug-in,是指一类特定的功能模块(通常由第三方开发者实现) 它的特点: 1. 随时安装.卸载.激活.禁用 2. 无论什么状态都不影响系统核心模块的运行, 3. 是一种非侵入式的模块化设 ...

  5. 自定义标签 tld

    初学者可能在不借助IDE工具的情况下 编写自定义标签库 tld  程序可能遇到找不到class 的错误,下面讲解一下如何解决该问题 步骤一:新建一个自定义标签类 HelloWorldTag,该类放到s ...

  6. NC portal怎么重新开始入门,整个配置过程包括配置一个节点

    有一份文档,之后会上传,暂时不上传

  7. Android的Manifest配置文件介绍

    一.关于AndroidManifest.xml       AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(ac ...

  8. Unity3D题目,Unity中利用GUI输出九九乘法表

    网上看到的这题,下面贴出源代码 using UnityEngine;using System.Collections; public class c99 : MonoBehaviour//C#脚本名: ...

  9. c#简体繁体转换

     方法一已经亲测,使用正常,方法二貌似不能用. 方法一 /// <summary> /// 中文字符工具类 /// </summary> public static class ...

  10. 什么情况下会调用到session_destroy()

    https://segmentfault.com/q/1010000000191102 首先 ... session_destory() 是一个函数 ... 这个函数在任何情况下都不会被 php 引擎 ...