All Posts

Messages Framework 开发文档(Sticker Packs 与 iMessage Apps)

概述

开发者可以为 iOS 10 的 Messages app 创建 extension。用户在其中可以发送文字、表情、媒体文件、交互式消息(交互式消息即 interactive messages,是一种允许被对话的参与者更新状态的消息)。

第三方软件可以使用 Messages framework 创建两种 app extension: 表情包(Sticker packs)和 iMessage apps。这两种 app extension 都可以作为现有主 app 的 extension 来发布,也可以单独发布。

为了方便理解后文内容,强烈建议先玩一下:
1. Sticker pack 比如知乎刘看山
2. GamePigeon中的台球
3. 大众点评的订电影票

written imessage apps, ios, messages extension Read on →

Dispatch_once造成的死锁—-分析、解决与自动检测

现象

最近遇到了一个死锁crash,主线程在dispatch_once时卡住了:

1
2
3
4
5
6
7
8
9
10
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   __ulock_wait + 8
1   _dispatch_unfair_lock_wait + 48
2   _dispatch_gate_wait_slow + 56
3   dispatch_once_f + 124
4   +[OTPolicyCenter sharedInstance] (once.h:68)
7   +[OTWebViewUtil completeUrlScheme:] (OTWebViewUtil.m:26)
...
30  start + 4

卡死的代码很简单,世界上的单例基本上都是这么开的:

1
2
3
4
5
6
7
8
+ (OTPolicyCenter *)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        policyCenterInstance = [[OTPolicyCenter alloc] init];
    });
    return policyCenterInstance;
}

written ios, macos, runtime Read on →

检查iOS App是否支持IPv6-only Network

从2016年6月1号开始,苹果强制要求上架AppStore的应用支持IPv6-only network。
本文分为两部分:通过代码检查是否兼容IPv6-only网络,以及如何搭建IPv6-only网络的测试环境。旨在帮助快速检查app是否支持IPv6-only Network。可以当做Supporting IPv6 DNS64/NAT64 NetworksEnsuring IPv6 DNS64/NAT64 Compatibility一节的速成版本。

通过搭建IPv6-only网络的测试环境测试

最便捷的检查方式是:找一台Mac,共享Internet到IPv6 DNS64/NAT64网络,然后让待测试设备连接到此网络,并测试app功能是否正常。
需注意,OS X 10.11后才支持创建IPv6 DNS64/NAT64网络。如果待测试设备不是iOS或OS X或macOS设备(比如要测试Andr**d上的app是否兼容IPv6-only网络),请确保设备支持RFC6106

written ios, ipv6, network Read on →

Objective-c使用运行时dump Class的方法列表

最近需要对UIWebView内请求的静态文件做缓存,还要把Gif替换成静态图以节约CPU资源等,于是查了一下私有API可不可以抓取UIWebView内的请求打个标记,以便NSURLProtocol中可以准确判断是不是UIWebView发出的请求。
经调试发现hookwebThreadWebView:resource:willSendRequest:redirectResponse:fromDataSource:可以做这件事。调试过程就不细说了。

首先是dump类方法列表的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <objc/runtime.h>
void DumpObjcMethods(Class clz)
{
    unsigned int methodCount = 0;
    Method *methods = class_copyMethodList(clz, &methodCount);
    printf("Found %d methods on '%s'\n", methodCount, class_getName(clz));

    for (unsigned int i = 0; i < methodCount; i++)
    {
        Method method = methods[i];

        printf("\t'%s' has method named '%s' of encoding '%s'\n",
               class_getName(clz),
               sel_getName(method_getName(method)),
               method_getTypeEncoding(method));
        /**
         *  Or do whatever you need here...
         */
    }
    free(methods);
}

written ios, macos, objective-c, runtime Read on →

iOS抓包(使用BurpSuite和tcpdump)

Introduce

开发过程中我们经常会需要对网络请求抓包,本次介绍的是使用BurpSuite抓取HTTP/HTTPS包,以及不越狱使用tcpdump抓取iPhone的网络包。

使用BurpSuite对HTTP/HTTPS抓包

开发中我们经常会需要对HTTP/HTTPS请求进行抓包。
抓包实际上是在中间机器开了一个代理服务,让需要抓包的请求经过代理,我们就可以看到这些请求了。本质上是中间人攻击。
BurpSuite是一个常用的调试工具。

1. 下载BurpSuite

BurpSuite官网下载jar包,右键点击,运行:

written burpsuite, debug, ios, tcpdump Read on →

在lldb中一键打开模拟器sandbox路径

打开~/.lldbinit,在里面加入一行:

1
command alias sb script from subprocess import call; call(["open", '{0:s}'.format(lldb.frame.EvaluateExpression("NSHomeDirectory()")).split("\"")[1]]);

然后中断时,在lldb里打sb回车,就能打开模拟器当前运行的app的沙箱路径了。

written ios, lldb, macos, xcode Read on →

OTHTTPRequest v2.0 Release

这几天重写了OTHTTPRequest的大部分代码,发布了2.0版。
改动主要是上传文件功能,以前做的比较粗,是整个文件读到内存再上传的,不能支撑大文件上传,现在multipart/form请求全部改为流上传。此外增加了上传速度计算的功能。

最早做这个的原因是13年5月份做网易云音乐的下载性能优化,ASIHTTPRequest下载文件占用CPU时间太长,而当时iPhone4还有一定占有量,做了这个库来提升下载性能。
这次发布2.0,修改上传功能,是因为用ASIHTTPRequest做大文件上传时,进度回调有bug,尝试修结果没修成,于是花了点时间做了这个版本。

和1.0一样,整个框架基于NSURLRequest和NSURLConnection。

Github链接

written ios, network, othttprequest Read on →

Xcode/lldb自动导入UIKit

lldb导入UIKit

使用lldb调试app时,lldb经常提示view.bounds无法打印:

实际上是lldb启动时没有默认导入UIKit,所以UIKit中的方法无法识别。在lldb中使用@import手动导入

1
expr @import UIKit

即可解决:

在开发Mac App时也是一样的,比如不导入AppKit则无法打印frame属性,手动导入AppKit即可:

1
expr @import AppKit

工程自动导入

但是大部分人都换有懒癌,对于每次调试都要手打一行命令是难以接受的。我们可以在main上加个断点:

在main中断时执行expr @import UIKit,并自动continue:

然后在工程启动时就会自动导入UIKit到lldb了。

全局自动导入

对于懒癌晚期的患者,每个工程单独配置还是挺麻烦的,可以在lldbinit中设置全局自动导入UIKit。在命令行中执行:

1
2
echo display @import UIKit >> ~/.lldbinit
echo display @import AppKit >> ~/.lldbinit

再重新启动调试即可(Xcode7测试时无需重启Xcode)。

Over

written ios, lldb, macos, xcode

GCD异步同步互转

同步转异步

这个是GCD最常见的用法之一,耗时长的操作放在global queue中做,完成之后将结果交给主线程处理:

1
2
3
4
5
6
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  id result = [self longTimeTask];
  dispatch_async(dispatch_get_main_queue(), ^{
      [self handleResultOnMainThread:result];
  });
});

异步转同步

这个本人并不常用,偶尔有不得不用的场景。
在异步操作开始前create信号量,然后使用dispatch_semaphore_wait等待此信号量。在异步操作完成后对此信号量发送signal:

1
2
3
4
5
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self someAsyncTaskWithCallback:^(BOOL successed) {
      dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

Over

written gcd, ios, macos

Self在执行方法的过程中dealloc引起的崩溃处理

场景和产生原因

在我的一个view controller里(为方便我们成为controller A),有一个doSomething方法,每次调用时,会延迟5秒调用delayAction,如果在5秒之内再次调用,会取消上次的延迟调用delayAction,再在5秒之后调用delayAction。

这在UI操作中是一个稀松平常的需求,代码如下:

1
2
3
4
5
- (void)doSomething
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayAction) object:nil];
    [self performSelector:@selector(delayAction) withObject:nil afterDelay:5];
}

然而就在某种情况下,这个正常工作的代码崩溃了,原因是第4行代码self已经释放,成为了僵尸指针,调用performSelector时造成了崩溃。

written ios, macos, objective-c Read on →

使用nomad cli测试iOS的Push Notification

安装和使用

Nomad cli是一个ruby工具,涵盖了多项打包,测试功能。
首先,安装nomad cli:

1
gem install nomad cli

然后即可使用apn push命令发送push了:

1
apn push devicetoken8fd63xxxxxxxxxxxxxxxxxxe27bc82a3b313475bb488ee92092d3c -c push_cert.pem -n -m "A message from openthread"

written ios, push Read on →

Xcode 中手动 Symbolicate Crash Log

从 Xcode7 开始 Organizer 集成了查看崩溃日志的功能,若 Xcode 可以定位到 crash 的 build,则可以直接在 Organizer 中查看崩溃是在具体哪一行。
但是不能自动定位到 build 的情况也非常常见,此时地址不能自动转换成符号:

此时即需要手动转换。

written ios, macos, xcode Read on →

[UIWindow setRootViewController:]后view无限叠加的问题修复

工程中有时会直接修改window.rootViewController,来导航到新的页面。
按理说对window的rootViewController属性设好了新值,老的rootViewController被释放了,UIKit应当自动把老的rootViewController的view一并remove掉,然而实测并非如此。
在iOS8/9中(更老版本没有测试),当window有rootViewController时,把新的controller赋值给window.rootViewController,老的rootViewController的view还是会留在window上。这些被遗留的view虽然看不见,但是浪费了内存,造成了view泄露;而且view的controller已经dealloc,此时view如果回调或通知controller的话,有造成崩溃的隐患。

多次设置rootViewController后,view结构如图,window上加了多个UILayoutContainerView:

written ios, uikit Read on →

iOS8的UITextView输入光标显示不全的hack

在iOS8及以下版本的系统上,在定高的UITextView中,输入内容超过Text View高度后,输入光标有时会在Text View的底部显示不全,如how-to-make-a-uitextview-scroll-while-typing-editing中截图所描述。

尝试了各种方案,挑选了一种体验较好的。在textViewDidChanged:中,检测到正在编辑的区域在文字最下行,无动画滚动到结尾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)textViewDidChange:(UITextView *)textView
{
    //hack for iOS8
    if (isLessThanIOS9)//in iOS9 Apple has already fixed this bug
    {
        CGRect line = [textView caretRectForPosition:
                       textView.selectedTextRange.start];
        CGFloat overflow = line.origin.y + line.size.height
        - (textView.contentOffset.y + textView.bounds.size.height
           - textView.contentInset.bottom - textView.contentInset.top);
        if (overflow > 0)//If at the bottom of text view
        {
            //disable animation. Otherwise, when a input confirm scroll animation is doing, input new text, animation will re-do from animation beginning, which looks strange.
            [UIView setAnimationsEnabled:NO];

            //scroll to text end
            [textView scrollRangeToVisible:NSMakeRange([textView.text length], 0)];
            [UIView setAnimationsEnabled:YES];
        }
    }
}

Over

written ios, uikit

Objective-c中property以new开头报错

在ARC中,属性名使用new开头会报错。比如说更改密码中原始密码的输入框叫oldPasswordTextField,可以;新密码的输入框叫newPasswordTextField,对不起,不行,编译错误。

在ARC出现之前,方法名如果以alloc、copy、init、mutableCopy和new开头,标准做法是返回一个retain count+1的对象。在ARC中,默认声明时强制遵守了这一规范:方法名如果以alloc、copy、init、mutableCopy和new开头,会被隐式声明为attribute((ns_returns_retained))。此行为可被显示声明attribute((ns_returns_not_retained))覆盖。

一般情况下不要使用attribute((ns_returns_not_retained))更改这一行为,除非有什么不得不履行的大义。

引用Clang 3.5 documentation | Objective-C Automatic Reference Counting | Retained return values:

A function or method which returns a retainable object pointer type may be marked as returning a retained value, signifying that the caller expects to take ownership of a +1 retain count.

Methods in the alloc, copy, init, mutableCopy, and new families are implicitly marked attribute((ns_returns_retained)). This may be suppressed by explicitly marking the method attribute((ns_returns_not_retained)).

参考帖子:http://stackoverflow.com/questions/24308162/property-name-starting-with-new-prefix-leads-to-bad-access-error-in-ios

Over

written ios, macos, objective-c

Strong-weak Dance 错误两则

第一则:RAC中strong-weak dance不完整造成内存泄露

今天在工程中发现了RAC导致的retain cycle:

1
2
3
4
@weakify(self)
[RACObserve(self, fooProperty) subscribeNext:^(id fooProperty) {
    [self doSomething];
}];

相对于正常的RAC用法:

1
2
3
4
5
@weakify(self)
[RACObserve(self, fooProperty) subscribeNext:^(id fooProperty) {
    @strongify(self)
    [self doSomething];
}];

少了一行@strongify(self)即造成了循环引用,即对于RAC来说,strong-weak dance是必须做的,不做strong-weak dance就会循环引用。

written ios, rac Read on →

iPhone SDK Bug Collection

  1. convertRect:fromView 返回乘以screen scale以后的结果:
    在iOS8上,view如果未添加到任何window,调用此方法可能会出现此后果。

  2. 定高UITextView输入时,文字超过text view高度,输入光标被遮挡或截断,显示不全,继续输入时正在输入的内容也被遮挡:
    参考iOS8的UITextView输入光标显示不全的hack

  3. window.rootViewController设置后,老的rootViewController的view仍然贴在window上:
    参考[UIWindow setRootViewController:]后view无限叠加的问题修复

Over

written ios, iphone, uikit

在Debina VPS上安装3proxy,支持HTTP、HTTPS、SOCKS代理

3proxy是俄罗斯人写的代理软件。

首先在debian上安装,推荐安装脚本

1
2
3
4
wget --no-check-certificate https://raw.github.com/benjamin74/3proxy/master/3proxyinstaller.sh
chmod +x 3proxyinstaller.sh
./3proxyinstaller.sh
  

然后编辑设置:

1
vim /etc/3proxy/3proxy.cfg

修改登录帐号密码,将users行改为如下内容,后面即可使用用户名user1/密码passwd1,或用户名user2/密码passwd2登录:

1
2
users user1:CL:passwd1
users user2:CL:passwd2

修改代理端口,在3128端口开启HTTP和HTTPS代理,1080端口开启SOCKS代理:

1
2
proxy -a -p3128
socks -a -p1080

启动3proxy:

1
/etc/3proxy/3proxy /etc/3proxy/3proxy.cfg &

另外安装脚本已经自带开机启动设置了。

更多设置参考3proxy manual

Over

written vps, 翻墙

在Debian VPS上安装dnsmasq解决DNS污染

warning: 我的有台DNS因为装了dnsmasq没做加密,被服务商提示有被DDoS攻击风险,强制断线了。

1. 安装

首先连上VPS,安装dnsmasq:

1
sudo apt-get install dnsmasq

我的VPS连接debian的APT服务器IPv6地址连不上,如果同样卡在连接 http://http.debian.net 不动的话,建议关掉IPv6再重试安装。

2. 配置

打开配置文件:

1
vim /etc/dnsmasq.conf

填入以下设置,将google的服务器作为上级DNS:

1
2
server 8.8.8.8
server 8.8.4.4

较新版本dnsmasq只破DNS污染,无需特殊服务的话,没必要更改其他设置,直接重启即可:

1
service dnsmasq restart

written vps, 翻墙 Read on →

10.11 EI Capitan兼容老使用习惯的一些设置

Terminal在非视网膜屏幕的字体平滑

Preferences -> Profiles -> Text -> Antialias text 打开(视网膜屏安装10.11时默认关闭了)

Safari command+数字键开启书签栏页面

Preferences -> Tabs -> Use command-1 through command-9 to switch tabs 关闭(10.11默认改为切换tab了)

Over

written macos

Xcode插件

1. Alcatraz

其他插件的 package manager, 可以从此插件中搜索、安装、删除其他插件。
Github地址

2. VVDocumenter-Xcode

///生成 Javadoc-Style 注释,可被 Xcode 识别,可生成文档。
Github地址

3. MCLog

Log 的 filter,支持正则,响应式交互,尚有崩溃,不过值得一用。
Github地址

4. Clang-Format

一键格式化脏乱差代码。还支持自动按段落排序#import “headers”。
安装后在工程中建立 .clang-format 文件,在其中配置代码格式,再使用此插件格式化代码。
前几天发pr修复了和 oh-my-zsh 同时使用的一处 crash,作者刚刚merge了,建议更新到最新。
推荐格式:

1
2
3
4
5
BasedOnStyle: WebKit
BreakBeforeBraces: Allman
PointerAlignment: Right
IndentCaseLabels: true
AlignAfterOpenBracket: true

Github地址

5. FuzzyAutocomplete

代码自动补全的增强工具,输入反应稍微减慢,不装其他输入增强工具时卡顿尚可接受,值得一用。
Github地址

Over

written xcode

Architectures 与 Valid Architectures 在 Xcode 中的设置

关于这几个设置项,先看官方说明:

  1. Architecture:
    Space-separated list of identifiers. Specifies the architectures (ABIs, processor models) to which the binary is targeted. When this build setting specifies more than one architecture, the generated binary may contain object code for each of the specified architectures.

  2. Vaild Architecture:
    Space-separated list of identifiers. Specifies the architectures for which the binary may be built. During the build, this list is intersected with the value of ARCHS build setting; the resulting list specifies the architectures the binary can run on. If the resulting architecture list is empty, the target generates no binary.

  3. Build Active Architecture Only:
    Boolean value. Specifies whether the product includes only object code for the native architecture.

written ios, macos, xcode Read on →

在OpenWRT上搭建ssh代理

首先为何要用ssh代理,而不是ShadowSocks或VPN:GFW会针对ShadowSocks或各种VPN协议做解析,却不一定有勇气禁止全部ssh连接(国家曾经有次物理断开到国外的全部网络连接造成了巨大经济损失)。

前段时间开发了SSHMole,用作OS X上的ssh代理客户端。然而MAC给iPhone开ssh代理,或在iPhone上直接开ssh代理还是很麻烦的。所以在家里路由器上开了个ssh -D。

0. 首先要有一台VPS

没有的话到vpsadd买一台。

1. OpenWRT上安装ssh-client, openssh-keygen

登录到OpenWRT,移除ssh到dropbear的软连接

1
2
mv ssh dropbear-ssh
mv scp dropbear-scp

written macos, ssh, vps, 翻墙 Read on →

难用的CocoaPods

前几天从网易云音乐离职,加入了 Rush。然后新 app 用了CocoaPods。
老实说,我对这个软件一直挺抵触的。做大 app 时,这个东西并没什么卵用,维护 pod 的时间很可能比手动维护几个 lib 还要时间长。这个软件的最佳使用场景,按我的理解,做外包频繁创建新工程时,可能会比较顺手。
然后在同事机器上,pod 工作的很正常,到了我的机器上,就 link error,原因是他的 pod 自动生成的.a 和我的 pod 自动生成的 .a 带的前缀不一样。

written cocoapods, ios Read on →

WWDC 2015 带来的新玩法

WWDC 2015 更新了五大产品线:OS X EI,iOS 9,watchOS 2,Xcode 7,Swift 2.

1. OS X EI

Metal APIs

Metal是一个开销极低的一种访问 GPU 的框架。
Metal framework 提供了 GPU 加速的先进的3D渲染和数据并行计算能力。
Metal 提供了流水线式的 API(streamlined API),预编译着色器(precompiled shaders),以及高性能的多线程支持。这些 features 可以帮助你把你的游戏或 app 的性能提升一个档次。
Metal 最主要的目标是把一部分的CPU 工作交给 GPU 做。

相关资源:
Metal Home Page
Metal Programming Guide
Metal Shading Language Guide
Metal Framework Reference

written ios, macos, wwdc Read on →

OSX各种实现毛玻璃效果窗口的方式与比较

毛玻璃窗体是10.10中新增的效果. 比如Finder的左边栏.
本文只对10.8, 10.9, 10.10三个版本进行讨论. 我的App未对10.6和10.7做任何支持, 此处也不予讨论.

第一种方式: NSVisualEffectView

这是10.10中新开放的API, 只能在10.10的runtime上使用.
如果需要上架App Store, 这也是在10.10上完成毛玻璃效果的唯一方式.
首先为NSView增加扩展:

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
@interface NSView (Vibrancy)

//Returns NSVisualEffectView
- (instancetype)insertVibrancyViewBlendingMode:(NSVisualEffectBlendingMode)mode;

@end

@implementation NSView (Vibrancy)

- (instancetype)insertVibrancyViewBlendingMode:(NSVisualEffectBlendingMode)mode
{
    Class vibrantClass=NSClassFromString(@"NSVisualEffectView");
    if (vibrantClass)
    {
        NSVisualEffectView *vibrant=[[vibrantClass alloc] initWithFrame:self.bounds];
        [vibrant setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
        [vibrant setBlendingMode:mode];
        [self addSubview:vibrant positioned:NSWindowBelow relativeTo:nil];

        return vibrant;
    }
    return nil;
}

@end

written appkit, cocoa, macos Read on →

如何优雅的使用60%键盘—-在所有App中使用Control+P、N、B、F代替上下左右

最近新宠是各种GH60,都没有独立方向键,按FN组合键是非常远的,刷固件修改按键也颇为不便,用软件修改就方便多了。
经常有软件不支持Control+P、N、B、F做上下左右操作,于是想到直接把Control+P、N、B、F改为上下左右。

OSX上可以用Karabiner自定义按键,可定制程度非常高,其前身是Keymap4MacBook,已开源在https://github.com/tekezo/Karabiner

首先从官网下载一个最新版并安装好。我需要将将control+P、N、B、F修改为上下左右,在预置中找不到相应设置。直接在Misc & Uninstall页面点击Open private.xml,修改这个配置文件可以完成各种高度可定制化改键。

written macos, 键盘 Read on →

HHKB布局的GH60

很喜欢HHKB的键位,但是静电容的手感不是太喜欢,type-s一直在吃灰。于是萌生了做一把HHKB布局的GH60的想法。
GH60富有富玩法,穷有穷玩法,这次是最穷的玩法。200块钱的洋垃圾键帽,60块钱的poker2外壳,改好弹簧的内胆400块钱,35块钱的屏蔽滑块,总共700搞定。

written 键盘 Read on →

热熔胶修改键帽菊花

最近很流行复古灰白色键帽,流行到几乎每把客制化键盘都要用,好像不用复古灰白色就不是客制化一样。
通常来说选择有以下几种:
1. 较高档的进口键帽比如GMK要上千而且不一定能买到,逼格是非常高的;
2. 其次是原厂洋垃圾拆机热升华灰白套、二色灰白套键帽,一套要700左右,逼格也是非常高的;
3. 更便宜的是G80拆机薄的pbt键帽,菊花大部分兼容现代键盘,一套要200左右,逼格还算说的过去;
4. 再便宜的是太豪二色,复古色140搞定,带7键增补(7x空格、1.75x shift、阶梯capslock)一般键盘都能用,但是逼格太低;
5. 最便宜的是G81拆机键帽,在100以内就能淘到,但是G81定位比较廉价,键帽大多只兼容平衡杆,并不像现代键盘是卫星轴平衡杆双兼容,这个逼格也是说的过去的。

于是很多人买G81的拆机键帽改菊花给现代键盘用,有逼格又便宜,就是改菊花比较麻烦。
网上搜到几篇AB胶改菊花的经验贴,本人手残党,对使用AB胶完全没有信心。而且AB胶粘歪了想抠下来是非常麻烦的。
于是这次摸石头过河尝试了热熔胶改菊花,操作简单效果好,和我一样同样是手残党的朋友可以尝试一下。
此法改pbt键帽出错的话,可以吹风机吹掉热熔胶,于是可以无限重试,并不像AB胶那样重试会非常麻烦。如果改abs二色要重试的话吹风机吹下键帽变形的危险性比较大,最好是有局部加热工具拿掉热熔胶。话说回来,AB胶改ABS二色键帽出错更难重试,因为AB胶失去粘性需要200摄氏度的温度,出错就只能用刀抠了。
先上张效果图:

written 键盘 Read on →

Apple Watch人机交互指南

翻译自官方文档 - Apple Watch Human Interface Guidelines

文档主要分为三部分:
1. 为Apple Watch设计软件的注意事项。包括介绍了watch软件的特点、watch独有的新的交互方式、Glances、Notifications、Modal Sheets、布局、颜色与排版、动画、app品牌化,也穿插了一些设计原则;
2. 各种控件的使用与设计原则。包括labels、images、groups、tables、buttons、switches、sliders、maps、dates、times和menus。虽然控件种类不多,但是和以往相比区别还是比较大的;
3. Icon、image和menu image的设计原则。

苹果的人机交互文档不只是给码农看的,也是给PD与设计师看的。建议PD和设计师们也读一读。

为Apple Watch设计软件的注意事项:

Apple从未有过的穿戴式设备

  1. Personal: 一方面更隐私,另一方面更个人化。带有心率等其它传感器,可以得到比以往设备更多的个人信息。由于是苹果的首个穿戴式设备,从未有过一款设备像apple watch一样如此与使用者紧密相连,这一点在设计应用时要多加注意。
  2. Holistic: Apple watch是软件与硬件的结合。新的交互方式包括Digtal Crown(表冠)、Taptic Engine(触觉反馈+细微声音反馈)和Force Touch(压力感应触摸屏),官方相关介绍传送门。设计App时应当把以上交互体验一并考虑在内。
  3. Lightweight: Apple watch上的软件定义为轻量级的,要求能快速启动,交互简洁、UI简单。iPhone的人机交互文档也是这么要求的,可惜遵守的人不多。这次苹果重新指出了这一点。

UI架构与交互方式

UI类型

App有两种基本样式,基于继承的,和基于平行页面的。

  1. Hierarchical:更像iOS上的导航模式。有推入推出。适合处理复杂的业务逻辑。

  1. Page-based:几个页面横滑切换的模式。苹果希望并列的页面能尽量少。适合处理简单的业务逻辑。

这两种基本架构不允许同时存在,只能二选其一。

用户交互

  1. Action-based:单击操作。在各种各样的控件上点的事件都是。
  2. Gestures:竖滑、横滑、左边缘右滑后退、点击。
  3. Force Touch:大力按下时系统会弹出context menu,作用类似pc上的右键菜单。App内当使用此menu显示上下文可以操作的actions。
  4. The Digital Crown:第三方应用仅可以使用表冠卷动来支持滚动长页面。

written apple watch, ios, watchkit Read on →

处理iPhone6、6+标准视图和放大视图 & 新增的[UIScreen nativeScale]方法

标准模式与放大模式

iPhone6和6+的设置(Settings)->显示与亮度(Display & Brightness)->显示模式(View)都带有标准模式(Standard)和放大模式(Zoomed)。
这个功能被引入后,依赖[UIScreen bounds][UIScreen scale]并不能完全确定屏幕分辨率是多大、用户选择了放大试图还是标准视图。比如放大模式下iPhone6读到的这两个属性和iPhone5是一模一样的,而放大模式下iPhone6+的[UIScreen bounds]属性和标准模式下iPhone6的一样。

iOS8中苹果引入了[UIScreen screenScale],可以用来区分不同的显示模式。

written ios, uikit Read on →

在Windows上安装Ruby

Mac上使用RVM[https://rvm.io]安装与管理各种版本的Ruby是非常简单易行的。今天心血来潮想在Windows上写Octopress,安装了Ruby,似乎已经没有13年的时候那么难装了。

  1. 下载安装包 访问RubyInstaller下载页,找到适合版本的Ruby Installer和Development Kit。

  2. 安装RubyInstaller:基本上只要双击安装包一直点下一步就可以了。

  3. 安装Development Kit:双击解压下载到的7z自解压文件,将其解压到自己喜欢的路径下。

  4. 按照Development Kit Wiki的指引步骤完成Development Kit安装。
    一般首次安装只要执行以下命令就可以了:

1
2
$ ruby dk.rb init
$ ruby dk.rb install
  1. 检查是否安装成功
1
2
3
4
$ gem install json --platform=ruby
# 成功则最后一行显示 1 gem installed
$ ruby -rubygems -e "require 'json'; puts JSON.load('[42]').inspect"
# 安装成功则显示 [42]

Over

written ruby

使用BFG Repo-Cleaner清除git的历史错误提交

Git中有时会不小心提交大文件或密码到repo中,然而使用git-filter-branch清理以往的全部提交是非常复杂的。
今天介绍一个好用的工具BFG Repo-Cleaner,可以方便清理错误的二进制文件或密码文件提交。

以下命令中所有的 bfg 是 java -jar bfg.jar 的alias。

1. 克隆一份repo到本地

1
git clone --mirror git@github.com:OpenFibers/openfibers.github.com.git

2. 执行bfg命令移除目标文件:

从历史纪录中删除所有文件名是 ‘id_rsa’ 或 ‘id_dsa’ 的文件:

1
$ bfg --delete-files id_{dsa,rsa}  openfibers.github.com.git

从历史纪录中删除所有大于1M的二进制文件 :

1
$ bfg --strip-blobs-bigger-than 1M  openfibers.github.com.git

written git Read on →

三年来用过的键盘

Type-s镇楼

入坑

12年的时候,公司的薄膜键盘实在太烂,按的手机关节疼,IT不给换。于是萌生了撸一把机械键盘的想法。
话说有那么一天节假日老婆回老家(当时还是女友)。
前几天她的电脑硬盘被我摔坏了,于是在这天我自己把她的电脑背出来到售后修理。返程途中过一家颐高数码城。当天数码城门口正在露天打WCG我市星际2的决赛,印象中是三星赞助的。完全不会玩星际,天气又非常热,于是直接进数码城上了四楼外设层。

written 键盘 Read on →

自动选用速度快的CDN播放MV in iOS

为什么要做这件事

最近云音乐用户数量要上亿了,用户量增加的过程中总有许许多多以前没暴露的问题。
比如最近好多人反馈MV经常卡顿。从反馈情况看是某个区域的用户访问我们CDN的速度比较慢。
但是如果只是换CDN服务提供商的话,就算这个区域的用户连接速度康复了,也很容易造成另一个区域的用户访问CDN变慢的情况。挑选新的CDN提供商也是个麻烦事。
不管怎样,先不考虑换CDN的事情,客户端层面的访问速度优化还是有必要做的。
既然不把更换CDN的因素考虑在内,一个特定用户的终端连接到每个CDN节点的速度是硬生生摆在那的。
所以要提升访问速度有两个事情可以做:

  1. 降低视频文件尺寸
  2. 选取最快的CDN节点

written ios Read on →

In-App Purchase Walk Through

1. 适用情况

想使用In-App Purchase(以下简称IAP)完成App内付费前,先确定需求是不是能用这个方案来满足。
除了IAP以外,还有支付宝SDK、信用卡等其他方式完成软件内付费。

在苹果制定的游戏规则中,所有在App内提供的服务需要付费时,都应当使用IAP,比如软件功能、游戏道具;所有在App外提供的服务需要付费时,都应使用其他支付方式,比如Uber的信用卡,淘宝、快的打车的支付宝SDK等。

在IAP里,可以出售:

  1. 数字内容:比如杂志、图片、游戏关卡解锁、相机付费滤镜等;
  2. 软件功能:如各种扩展features;
  3. 一次性服务:比如一次语音通话等。注意是「一次性」服务,不是一次「性服务」。

在IAP里,不能出售:

  1. 现实世界的商品或服务:比如刚才提到的一次「性服务」。严格遵守此方案有个好处:IAP 如果被破解,用户无法得到大量实物,开发商也不会有很大经济损失。非要做的话想绕过也是可以的:用 IAP 购买代币,审核通过后用代币购买实物。
  2. 其他the App Review GuideLines中规定的不允许的内容:比如刚才提到的一次「性服务」。

顺便说下,有次大网易的同事分享时提到:使用兑换码兑换App内服务是一条高压线。像Uber和Amazon里允许有码,是因为他们的码是用在现实世界的产品或服务上的。

如果你确定内购需求符合IAP的使用要求,可以继续往下读了。

written iap, ios Read on →

解忧杂货店

这本书与11年开始连载,12年角川书店发行了单行本,不是新书。这几天亚马逊付费榜排行第一。前几天买了,刚刚看完。

三个穷小偷为了跑路,钻进一家三十多年没开张的杂货店。

世界观大体就是这样的:当杂货店完全关上门时,店内时间流逝是要比店外面慢的。而且杂货店可以收到三十年前人的来信并回复来信。来信都是一些烦恼咨询,三个贼居然有模有样的在店内回复了起来。

整本书讲了几个咨询者身上发生的故事,以及店的来历。几个故事层层交织在一起,稍微带点推理小说的意思。毕竟作者是东野圭吾。
Kindle版带X-Ray,翻交织的线索很方便。

整书透过故事描述了日式的理想、亲情和爱,每段故事都挺感人的。
总之是一本非常好的书,很值得一读。

Over

written 书籍