为移动Web应用创建快速响应按钮

英文原文出自Google Deveploers《Creating Fast Buttons for Mobile Web Applications》,由TracyYih翻译,并首发于EsoftMobile.com。如需转载,请注明译者及出处信息。

背景

在Google,我们不断地突破移动Web应用能够达到的效果,HTML5这类技术让原生应用和Web应用的界线开始变得模糊。为了这个目标,我们开发了一种新技术让纯HTML按钮能够有更快的响应。这之前,我们可能只是为按钮或者其他可以点击的元素增加点击处理,如:

1
<button onclick='signUp()'>Sign Up!</button>

使用这种方法存在一个问题就是,移动浏览器会在你点击按钮后300ms才触发事件,原因是浏览器需要区分你是否是要执行双击。但是对于大多数按钮,我们并不需要它处理双击事件,所以300ms的延时时间对于只是想执行点击事件的用户来说太长了。我们最早开发这种技术是在Google Voice mobile web app中,我们希望能够更加迅速的调用拨号事件。

处理触摸事件

该技术涉及一点JavaScript的东西来让按钮响应触摸(Touch)事件而不是点击(Click)事件。触摸事件响应不会有延时所以感受会比点击事件快很多,但是我们也需要考虑以下几个问题:

  1. 如果用户是点击屏幕上其他的元素而触发了按钮的触摸事件,这种情况我们不应该去执行按钮的事件。
  2. 如果用户按下按钮然后拖到屏幕其他位置而触发了触摸事件,我们也不应该执行按钮的事件。
  3. 我们希望按钮在按下的时间能够高亮来表示点击状态。

查看更多

iOS7程序后台运行

介绍

这次iOS7对程序后台运行进行了加强,但是仅仅是加强而已,要想像Android程序那样自由当然就别想了,苹果这么做主要还是出于电池使用时间考虑,但是这次的加强对大部分程序基本够用。

在介绍之前, 我们先回顾一下在iOS7之前的后台运行相关的知识。在iOS7之前(iOS4之后)主要有三类的应用程序能够后台运行:

  1. 音频播放
  2. 后台定位服务
  3. IP电话

除了这三种应用,其他程序只能是在进入后台之前向系统请求一个额外的运行时间(最长为10分钟),并在该时间内来进行后台运行操作,如保存用户信息,上传或下载数据,进行视频编码等操作。

1
2
3
4
5
6
7
8
9
- (void)applicationDidEnterBackground:(UIApplication *)application
{
static UIBackgroundTaskIdentifier task;
task = [application beginBackgroundTaskWithExpirationHandler:^{
task = UIBackgroundTaskInvalid;
};
//执行后台操作
[application endBackgroundTask:task];
}

这次iOS7支持了两种新的程序后台运行模式:

查看更多

JavaScriptCore.framework

JavaScriptCore简介

iOS7中新加入的JavaScriptCore.framework可能被大多数开发人员所忽略,但是如果你之前就在项目中用过自己编译JavaScriptCore来处理JavaScript,那么你需要重新关注一下JavaScriptCore.framework。

JavaScriptCore是苹果Safari浏览器的JavaScript引擎,或许你之前听过Google的V8引擎,在WWDC上苹果演示了最新的Safari,据说JavaScript处理速度已经大大超越了Google的Chrome,这就意味着JavaScriptCore在性能上也不输V8了。

其实JavaScriptCore.framework在OS X平台上很早就存在的,不过接口都是纯C语言的,而在iOS平台,苹果没有开放该framework,所以不少需要在iOS app中处理JavaScript的都得自己从开源的WebKit中编译出JavaScriptCore.a,接口也是纯C语言的。可能是苹果发现越来越多的程序使用了自编译的JavaScriptCore,干脆做个顺水人情将JavaScriptCore.framework开放了,同时还提供了Objective-C的接口。

Objetive-C -> JavaScript

1
2
3
4
5
6
7
8
@import JavaScriptCore;
int main() {
JSContext *context = [[JSContext alloc] init];
JSValue *result = [context evaluateScript:@"2 + 2"];
NSLog(@"2 + 2 = %d", [result toInt32]);
return 0;
}

查看更多

Convert to Objective-C ARC

今天在进行代码走查时,竟然发现了下面这段代码:

1
2
3
for (int i = 0; i < someObj.retainCount; i++) {
[someObj release];
}

顿时感觉吐槽无力,虽然我反复强调内存管理问题,无非就是谁申请谁释放,利用强弱引用避免retain-cycles,但是还是会有这样那样的问题,leaks每次就是一片红。本来是计划等他们交易都开发完了,进行一次集体代码走查,好好给他们上一课,集中来解决内存问题。但是由于个人原因我7月份会离开项目组,恐怕没有时间来这么做了,所以最终还是决定将工程转成ARC模式。

该项目是某行手机银行客户端,iOS开发这块除了我,其他的所有7个开发人员都是项目组临时招聘的,技术参差不齐,毕竟公司招聘标准就是:便宜 + 能干事。我的职责就是负责客户端架构,公共机制的设计与实现,公共组件的封装,开发过程中的解疑。其他开发人员每人负责一两个模块的交易开发,其实无非就剩下请求数据绘界面的事了。

其实项目开始时就打算尝试用ARC的,但是项目组内其他人员之前都没接触过ARC,迫于项目进度压力也没有时间做培训,就使用大家比较熟悉的MRR,还是太信任他们了。

查看更多

iOS7适配之设计篇

(注:文章简要翻译自Apple 《iOS 7 UI Transition Guide》,由于该文档为开发者预览版,并非最终文档,所以iOS7正式上线可能有部分不同)

准备工作

iOS7带来了很多界面上的改变,如:没有边框(圆角)的按钮,半透明的工具条(UINavigationBar, UIToolBar等),视图控制器的全屏布局等。使用Xcode5,你就可以创建iOS7工程,并在iOS7模拟器中看看iOS7的界面。

UIStyle

从iOS7系统应用的改变可以看出,iOS7这次改变不是小打小闹,是彻彻底底的变化。苹果认为之前的UI风格辨识度很高,但是视觉体验太不一致(comment:你们换个设计师,我们苦逼的程序员就得按照你的审美来修改、适配)。

Note: 尽管iOS7所有的界面元素都看起来不一样,可能增加了新的功能,但是之前你所熟悉UIKit APIs的大多都是一样的。

查看更多

将UIWebView显示的内容转为图片和PDF

今天开发Evermark时要用到将UIWebView中显示的内容转为图片,方便转发到各个社交网络(Twitter,Facebook,Weibo),这样内容就不受长度限制,类似于长微博。 之前关于视图转图片我知道可以通过QuartzCore里截图的形式,但是截图只能截取当前屏幕所显示的区域(UIGraphicsGetCurrentContext()),而UIWebView的内容可能比屏幕长得多,在网上搜了一下,没有找到更好的方法,所有只有将UIWebView分屏截取,然后将截取的图片拼接成一张图片。

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
- (UIImage *)imageRepresentation{
CGSize boundsSize = self.bounds.size;
CGFloat boundsWidth = self.bounds.size.width;
CGFloat boundsHeight = self.bounds.size.height;
CGPoint offset = self.scrollView.contentOffset;
[self.scrollView setContentOffset:CGPointMake(0, 0)];
CGFloat contentHeight = self.scrollView.contentSize.height;
NSMutableArray *images = [NSMutableArray array];
while (contentHeight > 0) {
UIGraphicsBeginImageContext(boundsSize);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[images addObject:image];
CGFloat offsetY = self.scrollView.contentOffset.y;
[self.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)];
contentHeight -= boundsHeight;
}
[self.scrollView setContentOffset:offset];
UIGraphicsBeginImageContext(self.scrollView.contentSize);
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) {
[image drawInRect:CGRectMake(0, boundsHeight * idx, boundsWidth, boundsHeight)];
}];
UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return fullImage;
}

查看更多