高級繪圖要領(lǐng)-幾何語言

目錄
  • 視圖坐標
一 視圖坐標
1.1 Frames 和 Bounds

一個視圖的frame是通過bounds,center和其他transform計算來的。他描述了可以包括整個視圖的最小矩形。

1.2 坐標系轉(zhuǎn)換

iOS SDK提供了很多坐標系轉(zhuǎn)換的方法。比如說,你也許會希望吧一個點從視圖坐標系轉(zhuǎn)換到它的父視圖的坐標系中,來確定在它的父視圖的哪個位置進行繪制。你可以如下這么做:

CGPoint convertedPoint = [outerView convertPoint:samplePoint fromView:grayView];

你可以使用任何視圖實例調(diào)用這個方法。尤其是你想要轉(zhuǎn)換一個點到另一個坐標系上時(toView:)或事從另一個坐標系轉(zhuǎn)移過來(fromView:),

像例子中那樣。 這些視圖必須存在于同一個UIWindow,否則這個方法不會奏效。這些視圖不需要有任何其他的相關(guān)關(guān)系了。然而,他們可以是兄弟姐妹,父子,祖先,或其他關(guān)系。

這個方法會返回一個你想要得到的點坐標。 轉(zhuǎn)換矩形的方法和轉(zhuǎn)換點的方法也是類似的。把一個矩形從一個視圖轉(zhuǎn)換到另一個坐標系,使用convertRect:fromView:。轉(zhuǎn)換回來,使用convertRect:toView:

二 關(guān)鍵結(jié)構(gòu)體

iOS繪圖使用四個關(guān)鍵的結(jié)構(gòu)體來定義幾何基元:,尺寸矩形,轉(zhuǎn)換。這些結(jié)構(gòu)體都使用同一的單位:邏輯點。點通過CGFloat值來定義。在iOS中用float,而OS X使用double。 不像像素,固定整數(shù)點且與設(shè)備硬件無關(guān)。他們的值和亞像素精度提供的數(shù)學(xué)坐標有關(guān)。iOS繪圖系統(tǒng)使用你習慣的數(shù)學(xué)。

你會用到的四個基元如下:

  • CGPoint: 點結(jié)構(gòu)由x和y組成,他們定義了邏輯位置。
  • CGSize : 尺寸結(jié)構(gòu)由width和height組成,他們定義了橫縱軸上的延展
  • CGRect : 矩形由使用點定義的origin屬性和一個size屬性組成。
  • CGAffineTransform : 放射變換結(jié)構(gòu)描述了幾何項的改變——特別是,一個項目如何放置,放縮,旋轉(zhuǎn),他儲存了a,b,c,d,tx和ty六個值的矩陣來定義變換。
2.1 轉(zhuǎn)換

轉(zhuǎn)換是iOS幾何學(xué)中最強大一個部分。他允許點從一個坐標系轉(zhuǎn)移到另一個坐標系。也允許你放縮旋轉(zhuǎn),鏡像,位移等等當你繪制的時候,通過保存線性和相關(guān)比例。轉(zhuǎn)換提供了復(fù)雜的架構(gòu)來解決幾何問題。核心圖像的版本(CGAffineTransform)使用3x3的矩陣,來解決2D問題。而在3D中,使用Core Animation中的圖層定義的4x4的矩陣。Quartz轉(zhuǎn)換允許你進行幾何的位移放縮旋轉(zhuǎn)。 所有的轉(zhuǎn)換都可以用如下所示的底層轉(zhuǎn)換矩陣表示:

image.png

用C語言機構(gòu)提表示為:

struct CGAffineTransform{
       CGFloat a;
       CGFloat b;
       CGFloat c;
       CGFloat d;
       CGFloat tx;
       CGFloat ty;
}
2.1.1 創(chuàng)建轉(zhuǎn)換

不像其他核心圖像結(jié)構(gòu)體,你幾乎很少直接使用放射變換。大部分人甚至都不需要使用CGAffineTransformMake()(需要六個參數(shù))來創(chuàng)建結(jié)構(gòu)體。 取而代之的是,CGAffineTransformMakeScale()CGAffineTransformMakeRotate()或是CGAffineTransformMakeTranslation()。這些方法分別通過參數(shù)創(chuàng)建了放縮,旋轉(zhuǎn),位移矩陣。這些方法可以允許你根據(jù)你想完成的變換執(zhí)行對應(yīng)的操作。需要旋轉(zhuǎn)一個對象?直接輸入特定的選擇程度即可。需要移動對象?直接輸入移動位移即可。每一個方法創(chuàng)建了一個transform來完成指定操作。

2.1.2 分層轉(zhuǎn)換

相關(guān)方法允許你把一個轉(zhuǎn)換覆蓋在另一個轉(zhuǎn)換之上,完成一個復(fù)雜的變換。不像第一個函數(shù),他們只取一部分變換作為參數(shù)。他們定義一個變換會層疊另一個變換在頂上。不像make方法,他們總是按一定序列擺放的,且每一個變換的結(jié)果都會傳遞到下一個變換。比方說,你也許想創(chuàng)建一個圍繞他的中心選擇和放縮的事物,你可以如下列代碼這樣處理:

CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformTranslate(t, -center.x, -center.y);
t = CGAffineTransformRotate(t, M_PI_4);
t = CGAffineTransformScale(t, 1.5, 1.5);
t = CGAffineTransformTranslate(t ,center.x, center.y);

開始是一個默認的變換。默認的transfrom是個基礎(chǔ),就像加法中的0或者乘法中的1一樣。在使用幾何對象時,一般以此開始。是用來也確保一個固定的起始點以服務(wù)于之后的操作。 因為轉(zhuǎn)換應(yīng)用于對象的起始點,并不是中心點。你需要把中心點移動到起始位置。放縮和選擇總是與坐標為(0,0)的點關(guān)聯(lián)。如果你想讓他們關(guān)聯(lián)到另一個點,比如說一個視圖或是路徑的中心點,你需要把你想要的點移動到(0,0)的位置。這一連串的變換都會存儲在唯一的變換t中。

2.1.3 曝露轉(zhuǎn)換

UIKit庫提供了大量的輔助圖像和繪制操作的方法。還包括一些轉(zhuǎn)為放射變換特定的方法。你可以通過UIKit的NSStringFromCGAffineTransform()方法打印出一個視圖的變換。

這里沒有做中心在定位,所以只有兩個操作。 這些數(shù)字并不是那么直觀。特別是,他沒有直接告訴你放縮了多少選擇了多少。不過幸運的是,有很簡單方法可以處理,可直接展示更加直觀的值。下面展示如何計算X,Y方向上的放縮,以及旋轉(zhuǎn)角度。 當然,沒有必要去計算translation(位移)的值,因為這些值直觀地儲存在txty中。

//Extract the x scale from transform
CGFloat TransformGetXScale(CGAffineTransform t)
{
     retrun sqrt(t.a * t.a + t.c * t.c);
}

//Extract the y scale from transform
CGFloat TransformGetYScale(CGAffineTransform t)
{
      retrun sqrt(t.b * t.b + t.d * t.d);
}

//Extract the rotation in radians
CGFloat TransformGetRotation(CGAffineTransform t)
{
      return atan2f(t.b, t.a);
}
2.1.4 其他基礎(chǔ)方法
  • CGRectInset(rect, xinset, yinset) :這個方法會得到一個與原矩形同中心但更大或者更小的矩形。inset為正則變小,反之變大。這個方法在移動繪制圖形和子圖像離開邊界來提供一個空白空間是會顯得很有用。

  • CGRectOffset (rect, xoffset, yoffset):這個方法會得到一個與原矩形偏離x,y距離的矩形。Offset可以很好的應(yīng)用于拖動矩形在屏幕上移動,或是創(chuàng)建一個陰影效果。

  • CGRectGetMidX(rect)CGRectGetMidY(rect) : 這兩個方法返回矩形x方向和y方向上的中心坐標。這個方法可以很容易的獲取bounds或者frames的中心點。相關(guān)方法還有minX,maxX,minY,maxY,width和height。中心點可以很好的在繪圖中心項中使用。

  • CGRectUnion(rect1,rect2): 這個方法返回一個可以所有源矩形都包括進去的最小矩形。這個方法可以幫你確定你所繪制的元素最小的邊界盒子。這個方法可以把你所有的繪制路徑和元素結(jié)合起來并且作為他們的模板。

  • CGRectIntersection(rect1,rect2) : 這個方法返回兩個矩形的交叉部分,若無交叉則返回CGRectNull。交叉可以在使用AutoLayout計算矩形插入時起到很好的作用。路徑邊界和圖像邊界之間的交叉,可以得到兩者間固有的內(nèi)容。

  • CGRectIntegral(rect) 這個方法把源矩形轉(zhuǎn)換為整數(shù)。所有的orign值都會四舍五入到整數(shù)。而size值則會向上舍入。如此來確保新的矩形會把原始的矩形完整的包括進去。整數(shù)話矩形可以為繪制提速。視圖繪制通過像素邊界,這樣操作可以更好的減少鋸齒和模糊。

  • CGRectStandardize(rect) 這個方法會返回一個正數(shù)寬高值的基礎(chǔ)矩形。標準化可以幫助你更好地簡化數(shù)學(xué)運算當操作一些交叉繪制,特別當用戶交叉在左上中而非右下時。

  • CGRectDivide(rect, &sliceRect, &remainderRect, amount, edge)這個方法是核心圖像這些方法中最復(fù)雜的一個了,但是它也是舉足輕重的。除法可以交叉切割一個矩形成一個部分,以便于你可以細化繪制區(qū)域。

2.1.4 使用CGRectDivide

CGRectDivide方法是極其便利的。它提供了非常簡單的方法來分割和細化矩形成幾部分。每一步,你都會定義需要分割多少,分割哪一部分。你可以用從任何邊界進行分割,包括CGRectMinXEdge,CGRectMinYEdge,CGRectMaxXEdgeCGRectMaxYEdge。 一系列的代碼之后會得到如圖下圖所示的圖案。下面代碼展示了具體如何實現(xiàn)。這段代碼先是分割出矩形左邊的一小部分,然后縱軸對半分割剩下的部分。然后再在剩下的部分左右兩側(cè)再分出一小塊。

  • 核心代碼如下
  • 運行結(jié)果如下
2.1.5 矩形公用自定義方法

你使用CGRectMake()來創(chuàng)建frames,bounds或者其他矩形參數(shù)。它接收4個浮點數(shù)參數(shù)x,y,width和height。這是Quartz繪圖中最為重要的一個方法。 但常常,你總是想通過你經(jīng)常使用的對象,如points和size,來直接創(chuàng)建矩形。但你可以通過組合域來重寫方法。你會發(fā)現(xiàn)有一個實用的簡化方法是多么有用,通過使用結(jié)構(gòu)體為參數(shù)。代碼如下

出奇意料地,Quartz沒有一個內(nèi)建方法來獲取矩形的中心點。雖然你可以很容易地通過核心圖像方法取到x,y的中點。直接通過rect返回一個點坐標會更加方便一些。

圍繞著一個中心點來創(chuàng)建一個矩形也是常常會遇到的挑戰(zhàn)。比方說,你想讓一個文本中心對齊某點,或事圍繞一個點來擺放某個形狀。下面代碼即為實現(xiàn)方法。你提供一個中心點和一個尺寸,返回的矩形會描述你的目標位置。

將上述代碼結(jié)合來給指定矩形繪制一個中心對齊的字符串。它計算了字符串的尺寸(使用iOS7的API),然后和矩形的中心一起來創(chuàng)建了一個中心圍繞的對象。

  • 運行結(jié)果如下

另一個解決中心對齊的方法,它使用一個已存在的矩形和它的中心來得到另一個矩形,而非通過尺寸和目標點。你可以使用這個方法通過Bezier曲線得到的bounds矩形來計算。通過下列方法來讓路徑位于一個矩形中心處。

三 適配填充

往往,你需要重新改變繪制的尺寸來適配更小或更大的空間,而非使用他的原始尺寸。為了完成這些,你需要計算繪制的目標,不論你是在繪制路徑,圖像或是上下文。下面有四個基礎(chǔ)程序你需要完成:中心對齊,適配,填充,壓縮。 如果你曾經(jīng)使用過view content mode,這些代碼看起來會比較相似。當繪制的時候,你會使用和UIKit一樣的填充視圖和上下文的策略。他們對應(yīng)的填充策略分別是:center,scale aspect fit,scale aspect fillscale to fill

3.1 中心對齊

中心對齊會按照矩形的原始比例擺放視圖,直接擺放在目標的中心點位置。素材比展示區(qū)域大會被裁剪。素材比區(qū)域小則會修邊(在四邊都會留下多余的區(qū)域,在繪制中叫做windowboxing)。 在每個情況中,圖像都是用RectAroundCenter()來完成中心對齊的。在外部的大圖會被裁剪,而內(nèi)部小圖則會留下很多額外的空間。

3.2 適配

當你需要適配一個項目到某個目標時,你需要保持它原有的比例并讓它的所有部分都可見。由于需要維持原有比例,結(jié)果也并不會是全覆蓋的,也往往會留下一些額外的空間。 在物理世界中,修邊是指在一個框架圖中使用的背景或邊界。這個背景或邊界在畫和物理尺寸中留下空間。這里,我使用一個matting(修邊)來表示繪圖區(qū)域和目標矩形之間的額外區(qū)域。

灰色背景為目標矩形。淡紫色背景是需要適配的圖案通過下述代碼的計算得到的結(jié)果。除非兩者比例恰好切合,繪制區(qū)域需要做中心對齊操作,把額外的區(qū)域留在上下或左右兩端。

3.3 填充

填充,如下圖所示,確保你的目標區(qū)域每一個像素都有源圖像等一部分。不要被繪圖的fill(填充,用特定顏色或圖案填充路徑)操作迷惑,。 為了完成這個操作,填充或擴充或縮放源矩形來精確地覆蓋在目標上。他會裁剪掉所有在目標以外的部分,不論是在上下或是左右。結(jié)果矩形會是中心對齊的,但只有一個維度會完整的展示出來。 下面代碼計算出來目標目標矩形的過程。

3.4 壓縮

壓縮會調(diào)整源圖像的比例來適配到整個目標區(qū)域。目標區(qū)域的每一個像素都會有源圖像里的內(nèi)容。如下圖所示。素材會在一個方向上重新設(shè)置尺寸,以便可以容納得下。這里,兔子圖像壓縮了橫向來匹配目標矩形。 不像之前的適配風格,壓縮不需要額外的計算。直接繪制即可,Quartz會做其他剩下的工作。

四 總結(jié)

本章介紹了一些基本的術(shù)語和一些操作核心繪圖的方法。你已經(jīng)了解了像素和點的區(qū)別,探索了常見的數(shù)據(jù)類型,學(xué)習了如何計算繪圖位置。這里有一些最后需要注意的地方,在你離開本章之前可以進行思考:

  • 在編寫代碼是總是要把屏幕比例考慮進去。相關(guān)的設(shè)備如iPad2和第一代iPad mini沒有使用Retina屏幕。專注于邏輯空間(點)而不是設(shè)備空間(像素)可以讓你更靈活地支持更新和新的設(shè)備幾何。

  • 大部分繪制都是相互關(guān)聯(lián)的,尤其是當你需要觸摸操作時。學(xué)習如何把點從一個坐標系轉(zhuǎn)移到另一個坐標系能更好的按照你的想法來定位,而不是定位到繼承中。

  • 放射變換總是被看成是其他的核心圖像結(jié)構(gòu)體,但是它和其他更常見的兄弟(點,尺寸和矩形)一樣重要。變換是十分強大的可以很大程度上簡化繪制操作。如果你有多余的時間,很值得花費一些經(jīng)歷來學(xué)習變換和矩陣數(shù)學(xué)(線性代數(shù))。Khan Academy(www.khanacademy.org) 在這個課題上提供了很好的教程。

  • 一個強健的幾何方法代碼庫可以在很多項目中都使用到。基礎(chǔ)幾何是永遠不會過時的,相似的任務(wù)總是會反復(fù)出現(xiàn)。學(xué)會如何中心對齊矩陣和計算適配和填充目標會在很多時候節(jié)約你的時間,并且保證代碼長久高效運行。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容