本文由CocoaChina译者 @唧唧歪歪 翻译,做者:Hector Matosios
恶梦xcode
想象你正在干活,你的上级要求你在工程中加入一些图片。你找到团队中的设计师(若是大家有一个的话),鼓起勇气,问他要这些图片。不过,一般他都会忙得根本没时间帮你。由于他要作的活儿比你还多。说实话,你到底见过一个设计师有多少活儿要作吗?!简直多到使人发指啊!因此你最不想作的就是成为那个再给他加活儿的人了。特别是那些活儿对设计师来讲简单得让人厌烦。更不用说,设计师也要按顺序干活,你拿到那些图片也是几天以后了。因此咱们仍是来看看如何用Xcode 的Asset Catalog来处理这些麻烦事吧。app
麻烦事No.1:“能改一下这个图片的颜色吗?”布局
目前为止iOS已经提供了一些至关复杂的方法来处理工程中的图片,但很不幸的是,却没有用来处理这种状况的方法。我已经数不清楚有多少次咱们由于什么便捷性支持或者是客户不喜欢而改变工程的颜色了。如今在工程中全局改变UIColor已经不是什么难题了,可是咱们还要改变工程中图片的颜色。这样咱们就不得不回去找已经忙得要死的设计师了,就由于咱们太懒而不去学学怎么用Photoshop。固然咱们也能够写代码来完成,不过看起来至关复杂,并且还容易形成泄漏。在iOS7 以前,写代码改变图片的颜色和下面差很少(还有不少其余的方法):ui
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。spa
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渲染模式。3d
在了解了上面的内容以后,以前咱们改变图片颜色的代码就能够简化成下面这样的了:
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逻辑。
结论
我很肯定,咱们开发者几乎天天都还会作不少其余事去麻烦设计师们,但至少咱们能多用用这些功能,让他们稍微休息一下子。毕竟编程能解决一切问题,何乐而不为呢?