如何支援 Mac OS X
 的高解析度模式
   楊維中 a.k.a zonble
萬惡的根源
開發環境

• MacBook Pro 2012
• 沒有新機器怎麼辦?
• 使用模擬環境
模擬環境
模擬環境
模擬環境




Quartz Debug
模擬環境
模擬環境
如何支援?


• 使用兩倍圖
• 改用向量圖形
使用兩倍圖
• 跟 iOS 一樣
• 所有用到的圖片
 的   名加上
 @2x,然後加到
使用向量圖
• 向量圖     格式很
 多:Adobe
 Illustrator、
 Corel Draw、
 SVG…
• Mac OS X 系統
 支援的向量格
Core Animation

• CALayer 必須要手動調整屬性,才有辦
 法支援 Retina Display

• contents 直接給 @2x 圖片是沒用的
• 必須手動設定 contentScale
iOS 上面
• aLayer. contentsScale = [UISCreen
  mainScreen].scale
• 反正一般來說,我們的 UI 只會出現在
  mainScreen 上。雖然可以外接投影機,
  使用另外一個 UIScreen,但是一般不會
  有人去寫        種 App
Mac OS X
•   CALayer 不一定會出現在 個 Screen 裡頭,而是要由
    目前 layer 所在的 Window 決定是否要使用高解析度

    •   詢問 NSWindow 的 -backingScaleFactor

•   Window 還可以在不同 Screen (主螢幕、延伸螢幕)
    之間移動

    •   contentsScale 需要隨時改變

•   還有…向下相容,你應該不會只支援 10.7.4 以上的系
    統
改變解析度的時機
• Layer 被放到某個 Window 的時候
• 因為 Window 移動而造成 Window 的
 scale 改變的時候(Notification)

• 以下範例是假設 rootLayer   個物件被擺
 在某個 NSView 裡頭
Layer 被放到某個

- (void)viewDidMoveToWindow {
   if ([rootLayer respondsToSelector:@selector(contentsScale)]) {
// 舊版的 OS 的 CALayer 不一定會有 contentsScale
// 舊版的 OS 的 Window 也不一定會有 backingScaleFactor…
        CGFloat scale = [(id)[self window] backingScaleFactor];
        [(id)rootLayer setContentsScale:scale];
    }
}
訂閱 Window 移動而
• 訂閱
 NSWindowDidChangeBackingPropertiesN
 otification
• 但因為舊版系統沒有,所以直接訂閱字
 串 @”
 NSWindowDidChangeBackingPropertiesN
 otification”
訂閱 Window 移動而

• 在 viewDidMoveToWindow 訂閱
• 在 removeFromSuperview 取消
• 我們先來把剛剛那段修改 contentsScale
 的程式拆出來
拆出來的 private
- (void)_updateContentScale {
   if (![self window]) {
       return;
   }

    if ([rootLayer respondsToSelector:@selector(contentsScale)]) {
        CGFloat scale = [(id)[self window] backingScaleFactor];
        [(id)rootLayer setContentsScale:scale];
    }
}
訂閱通知
- (void)viewDidMoveToWindow {
   [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scale
DidChange:) name:@"NSWindowDidChangeBackingPropertiesNotification"
object:[self window]];
   [self _updateContentScale];
}

- (void)removeFromSuperview {
   [super removeFromSuperview];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)scaleDidChange:(NSNotification *)n {
   [self _updateContentScale];
}
向下相容


• 用舊版的 SDK 編譯,compiler 會跟你抱
 怨沒有 contentsScale 與
 backingScaleFactor… 很討厭
為了向下相容弄出來

@interface NSObject (RetinaDisplay)
- (void)setContentsScale:(CGFloat)inScale;
- (CGFloat)backingScaleFactor;
@end
That’a all!

Retina mac