為了更高的彈性以及更好的資料自主性,我把部落格搬到 GitHub Page,這邊將不再維護。
想看最新文章的朋友,請移駕到 https://chiahsien.github.io/ ,它也有提供 RSS 訂閱服務喔。
為了更高的彈性以及更好的資料自主性,我把部落格搬到 GitHub Page,這邊將不再維護。
想看最新文章的朋友,請移駕到 https://chiahsien.github.io/ ,它也有提供 RSS 訂閱服務喔。
公司的產品越做越大,前一陣子幫公司的 app 加上阿拉伯文介面,開發過程也累積了一些支援 RTL 語系的心得,藉這個機會跟大家分享。
對於書寫方向是「從左到右」的我們來說,最困難的其實不是看不懂這個語言,而是不知道這樣的 layout 是否正確,因為 layout 絕對不是全部都無腦的換成「從右到左」就好。所以如果情況許可,最好找個 native speaker 讓團隊諮詢(例如找個當地的員工,或是請當地大學生來打工之類),這樣可以節省不少來回確認界面的時間。
很多開發的注意事項都寫在這份蘋果文件裡頭了,開發前跟開發時務必要多次閱讀,會有很大的幫助。另外,不只是 RD 需要閱讀這份文件,PM、QA、Designer 也應該看過,才不會發生 RD 做出正確界面,結果其他人以為是錯的(例如多媒體播放器的控制元件是不需要 RTL 的)。
如果有特殊需求的話,可以考慮對圖片 localized,這樣就可以提供適當翻轉過的圖片給特定語言,或是你也可以透過程式碼去翻轉圖片。但是假如你需要的就只是左右翻轉的圖片(例如 arrow 或 bullet-list 的圖片),可以很簡單的透過以下方式取得:
UIImage *image = [UIImage imageNamed:@"xxx"];
// 對圖片做一些必要處理
// .....
// 最後再翻轉圖片
image = [image imageFlippedForRightToLeftLayoutDirection];
要注意的是,翻轉一定要放在最後一步,這樣才會得到預期結果。
正確使用 auto layout 的 leading
跟 trailing
,加上將 textAlignment
設為 NSTextAlignmentNatural
,就可以解決九成以上的 RTL 佈局。如果你的程式還沒支援 auto layout,可以趁機逐步轉換過去。
由於種種因素讓你還無法轉成 auto layout(例如為了效能考量,或是有些地方就是用 frame 比較容易,或是轉換成本太大),這時候你就需要判斷現在是否在 RTL 環境來調整 frame。
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
// RTL 佈局
} else {
// LTR 佈局
}
還有一種情況是不方便用 auto layout,但是手動計算 frame 又有很多額外因素要考量讓你不想計算。舉個常見的例子:有上下兩個可以左右滑動的 scroll view,上面是多個 tab,下面是點選 tab 之後要捲動到特定範圍。常見的做法是上方每個 tab 都有一個 index,下方則是根據選中的 index 計算 contentOffset
。在 RTL 並且要手動計算 frame 的情況下,你會發現 index 處理起來很麻煩。
這裡有一個小技巧,我們可以先對最外層的 container view(例如 UIScrollView
)左右翻轉,然後再對 subviews 左右翻轉一次。經過兩次翻轉,這些 subviews 就會從右到左排列,而且原有的程式碼幾乎不需要改動。這招或許看起來很 tricky,但它真的很有用,用得好可以節省非常多的時間。
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
scrollView.transform = CGAffineTransformMakeScale(-1, 1);
for (UIView *subview in scrollView.subviews) {
subview.transform = CGAffineTransformMakeScale(-1, 1);
}
}
以上就是我們在支援 RTL 時用到的所有方式,希望對大家有幫助。
最近升上 Swift 4.2,發現我用到的 Pods 有些還沒支援 4.2 導致編譯錯誤。解決方法也很簡單,只要指定每個 Pod target 的 SWIFT_VERSION
為 4.0
即可。
但是我們不能手動在 Xcode 裡頭調整,因為 CocoaPods 會把 Pods 的 SWIFT_VERSION
設為跟你的 project 一樣,所以下次 pod install
又會被改掉。
我們可以在 Podfile
用 post_install
來自動修改,只要在 Podfile
結尾加入以下片段即可。
post_install do |installer|
installer.pods_project.targets.each do |target|
# 我們也可以懶惰不用 if,讓所有 pod 的版本都設為一樣的
if ['RxSwift', 'RxSwiftExt', 'RxCocoa', 'RxDataSources', 'ProtocolBuffers-Swift'].include? target.name
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.0'
end
end
end
end
Git-Flow 是 Vincent Driessen 在 2010 年提出的一套 Git 分支模型,簡單的說,它有 master
跟 develop
這兩個主要的分支,以及 feature
/ release
/ hotfix
這三個支援型分支,至於各個分支的用途看圖片應該就懂了,或是看原文有更詳細的說明。
由於當時大家對如何使用 Git 還處於摸索的階段,所以當這套規範被提出並且大家發現真的滿好用的之後,它很快就被廣泛的接受。
最近在替公司 app 做健康檢查,找到一些 memory leaks 的問題,其中一個就是由 NSTimer
所引起的 retain cycle。
NSTimer
是個很容易造成 retain cycle 的物件,無論是新手或是老手都很可能一個不留意就踩到這個坑。舉個很常見的例子,這樣寫就產生 retain cycle 了:
@interface MyViewController()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
}
- (void)dealloc {
[_timer invalidate];
_timer = nil;
}
- (void)timerFired:(NSTimer *timer) {
// Do something...
}
@end