1. 程式人生 > >《iOS Drawing Practical UIKit Solutions》讀書筆記(二) —— The Language of Geometry

《iOS Drawing Practical UIKit Solutions》讀書筆記(二) —— The Language of Geometry

Points VS Pixels

Point是UIKit中的邏輯位置,並不代表畫素。主要是在Retina螢幕中,一個Point會對應2或3個Pixels,這取決於PPI(DPI):pixel per inch ,畫素密度PPI,指每英尺的畫素數,表示了清晰度。

這裡寫圖片描述

Scale

UIScreen類的屬性scale, 表明了當前裝置點與畫素間的關係。因此,對於非Retain屏,scale == 1.0 ,而Retain屏則是2.0 或 3.0 。

UIScreen提供兩種屬性來表示display-size:
1. bounds : 表示Screen的bounding,以Point為單位,表示整個螢幕的大小。
2. applicationFrame: 表示實際可用螢幕的Frame,這是除掉status bars,navigation bars 或 tab bars的大小。

View Coordinates

在iOS 7及以後,view的座標原點一般都是在navigation bar下面開始。這取決於VC的edgesForExtendedLayout屬性設定。

Coordinate System之間的轉換

我們說所的Frame,Point,都是基於一個UIView中的座標系統來說的。如果想在UIView之間轉換座標值使得在另一個UIView中定位同樣的位置,iOS SDK 提供了一系列
toView, fromView方法。

注意,View之間的座標轉換是針對於位於同一個UIWIndow中的View來說的,只要位於一個View,就可以進行座標的轉換,而View之間不必有父子關係。

Key Structures

iOS 用四種主要的資料結構來描述幾何資訊,分別是:

  • CGPoint
  • CGSize
  • CGRect
  • CGAffineTransform
    仿射變換,代表了a,b,c,d,tx和ty矩陣。
    這裡寫圖片描述

輸出Transforms

UIKit預設給出方法

NSStringFromCGAffineTransform()
CGAffineTransformFromString()

來使得NSString 和 CGAffineTransform之前轉換,但輸出的內容並不直觀:
這裡寫圖片描述

我們可以用如下的函式來計算Scale和Rotate的數值:

CGFloat TransformGetXScale(CGAffineTransform t) {
    return sqrt(t.a * t.a + t.c * t.c);
}

CGFloat TransformGetYScale(CGAffineTransform t) {
    return sqrt(t.b * t.b + t.d * t.d);
}

CGFloat TransformGetRotation(CGAffineTransform t) {
    return atan2f(t.b, t.a);
}

預定義常量

這裡寫圖片描述
這裡寫圖片描述

Conversion to Objects

由於所有的幾何物件都是以struct的形式,所以為了支援Object的方法,Core Graphics和Core Foundation均提供了若干函式將幾何物件轉換為真正的Object物件。
可以轉換為的物件是:
1. Strings
2. Dictionaries
3. Vaules

String

這裡寫圖片描述

Dictionaries

注意,這裡轉換的Dictionary是CFDictionaryRef,需要bridge到NSDictionary
這裡寫圖片描述

Value

NSValue為C型別的資料提供了一個面向物件的容器。它可以包含標量型別(int, float),指標和結構體。UIKit擴充套件了NSVaule,使之能夠接受Core Graphics資料。
這裡寫圖片描述

這裡寫圖片描述

Geometry Tests

Core Graphics提供了一系列函式,可以對幾何資料型別進行測試:
這裡寫圖片描述
這裡寫圖片描述

這裡寫圖片描述
這裡寫圖片描述

Rectangle 工具方法

針對CGRect結構,下面補充了一些工具方法,可用於平常開發時使用
1. 建立Rect

CGRect RectMakeRect(CGPoint origin, CGSize size) {
    return (CGRect){.origin = origin, .size = size};
}
  1. 獲取Rect 的 Center Point
CGRect RectGetCenter(CGRect rect) {
    return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
}
  1. 建立一個指定center point, size的Rect
CGRect RectAroundCenter(CGPoint center, CGSize size) {
    CGFloat halfWidth = size.width / 2.0f;
    CGFloat halfHeight = size.height / 2.0f;
    return CGRectMake(center.x - halfWidth, center.y - halfHeight, size.width, size.height);
}

Fitting and Filling

有時候我們需要調整物件的大小,來讓它draw到比實際內容要大(小)的空間中。
常用的方法有:
1. centering
2. fitting
3. filling
4. squeezing

Centering

Centering的內容會顯示在矩形的正中,同時按照原始大小顯示。對於大出的部分,會被裁掉,而小的部分,其餘會預設填充灰色。

這裡寫圖片描述

Fitting

Fitting意味著展示的內容會按照原比例壓縮(或放大),其餘的地方則會留空。

如圖
這裡寫圖片描述

CGSize SizeScaleByFactor(CGSize aSize, CGFloat factor) {
    return CGSizeMake(aSize.width * factor, aSize.height * factor);
}

CGFloat AspectScaleFit(CGSize sourceSize, CGRect destRect) {
    CGSize destSize = destRect.size;
    CGFloat scaleW = destSize.width / sourceSize.width;
    CGFloat scaleH = destSize.height / sourceSize.height;
    return MIN(scaleW, scaleH);
}

CGRect RectByFittingInRect(CGRect sourceRect, CGRect destinationRect){
    CGFloat aspect = AspectScaleFit(sourceRect.size, destinationRect);
    CGFloat targetSize = SizeScaleByFactor(sourceRect.size, aspect);
    return RectAroundCenter(RectGetCenter(destinationRect), targetSize);
}

Filling

Filling 如圖所示,它會保證每一個畫素都會對應到目標空間的對應位置,但是卻不能保證圖片被顯示完全。為了實現畫素到目標空間的對應,原始圖片會被拉伸或縮小。

這裡寫圖片描述

CGSize SizeScaleByFactor(CGSize aSize, CGFloat factor) {
    return CGSizeMake(aSize.width * factor, aSize.height * factor);
}

CGFloat AspectScaleFill(CGSize soruceSize, CGRect destRect){
    CGSize destSize = destRect.size;
    CGFloat scaleW = destSize.width / sourceSize.width;
    CGFloat scaleH = destSize.height / sourceSize.height;
    return MAX(scaleW, scaleH);
}

CGRect RectByFillingRect(CGRect sourceRect, CGRect destinationRect){
    CGFloat aspect = AspectScaleFill(sourceRect.size, destinationRect);
    CGFloat targetSize = SizeScaleByFactor(sourceRect.size, aspect);
    return RectAroundCenter(RectGetCenter(destinationRect), targetSize);
}

Squeezing

Squeezing會調整原始影象的大小與比例,使之能夠在目標空間中顯示出來。我們不需要對原始影象做任何調整,只需要將其draw到目的空間中,Quartz會做剩餘的事情。
這裡寫圖片描述