亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

iOS坐標系的深入探究

 更新時間:2018年11月03日 10:16:04   作者:落影l(fā)oyinglin  
這篇文章主要給大家介紹了關于iOS坐標系的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

app在渲染視圖時,需要在坐標系中指定繪制區(qū)域。

這個概念看似乎簡單,事實并非如此。

When an app draws something in iOS, it has to locate the drawn content in a two-dimensional space defined by a coordinate system.
This notion might seem straightforward at first glance, but it isn't.

正文

我們先從一段最簡單的代碼入手,在drawRect中顯示一個普通的UILabel;

為了方便判斷,我把整個view的背景設置成黑色:

- (void)drawRect:(CGRect)rect {
 [super drawRect:rect];
 CGContextRef context = UIGraphicsGetCurrentContext();
 NSLog(@"CGContext default CTM matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));
 UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 28)];
 testLabel.text = @"測試文本";
 testLabel.font = [UIFont systemFontOfSize:14];
 testLabel.textColor = [UIColor whiteColor];
 [testLabel.layer renderInContext:context];
}

這段代碼首先創(chuàng)建一個UILabel,然后設置文本,顯示到屏幕上,沒有修改坐標。

所以按照UILabel.layer默認的坐標(0, 0),在左上角進行了繪制。


UILabel繪制

接著,我們嘗試使用CoreText來渲染一段文本。

- (void)drawRect:(CGRect)rect {
 [super drawRect:rect];
 CGContextRef context = UIGraphicsGetCurrentContext();
 NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));
 NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"測試文本" attributes:@{
             NSForegroundColorAttributeName:[UIColor whiteColor],
             NSFontAttributeName:[UIFont systemFontOfSize:14],
             }];
 CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr); // 根據(jù)富文本創(chuàng)建排版類CTFramesetterRef
 UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 20)];
 CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 創(chuàng)建排版數(shù)據(jù)
 CTFrameDraw(frameRef, context);
}

首先用NSString創(chuàng)建一個富文本,然后根據(jù)富文本創(chuàng)建CTFramesetterRef,結合CGRect生成的UIBezierPath,我們得到CTFrameRef,最終渲染到屏幕上。

但是結果與上文不一致:文字是上下顛倒。


CoreText的文本繪制

從這個不同的現(xiàn)象開始,我們來理解iOS的坐標系。

坐標系概念

在iOS中繪制圖形必須在一個二維的坐標系中進行,但在iOS系統(tǒng)中存在多個坐標系,常需要處理一些坐標系的轉換。
先介紹一個圖形上下文(graphics context)的概念,比如說我們常用的CGContext就是Quartz 2D的上下文。圖形上下文包含繪制所需的信息,比如顏色、線寬、字體等。用我們在Windows常用的畫圖來參考,當我們使用畫筆🖌在白板中寫字時,圖形上下文就是畫筆的屬性設置、白板大小、畫筆位置等等。

iOS中,每個圖形上下文都會有三種坐標:

1、繪制坐標系(也叫用戶坐標系),我們平時繪制所用的坐標系;

2、視圖(view)坐標系,固定左上角為原點(0,0)的view坐標系;

3、物理坐標系,物理屏幕中的坐標系,同樣是固定左上角為原點;

根據(jù)我們繪制的目標不同(屏幕、位圖、PDF等),會有多個context;


Quartz常見的繪制目標

不同context的繪制坐標系各不相同,比如說UIKit的坐標系為左上角原點的坐標系,CoreGraphics的坐標系為左下角為原點的坐標系;


CoreGraphics坐標系和UIKit坐標系的轉換

CoreText基于CoreGraphics,所以坐標系也是CoreGraphics的坐標系。

我們回顧下上文提到的兩個渲染結果,我們產生如下疑問:

UIGraphicsGetCurrentContext返回的是CGContext,代表著是左下角為原點的坐標系,用UILabel(UIKit坐標系)可以直接renderInContext,并且“測”字對應為UILabel的(0,0)位置,是在左上角?

當用CoreText渲染時,坐標是(0,0),但是渲染的結果是在左上角,并不是在左下角;并且文字是上下顛倒的。

為了探究這個問題,我在代碼中加入了一行l(wèi)og:

NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));

其結果是CGContext default matrix [2, 0, 0, -2, 0, 200];

CGContextGetCTM返回是CGAffineTransform仿射變換矩陣:

一個二維坐標系上的點p,可以表達為(x, y, 1),乘以變換的矩陣,如下:

把結果相乘,得到下面的關系


此時,我們再來看看打印的結果[2, 0, 0, -2, 0, 200],可以化簡為

x' = 2x, y' = 200 - 2y

因為渲染的view高度為100,所以這個坐標轉換相當于把原點在左下角(0,100)的坐標系,轉換為原點在左上角(0,0)的坐標系!通常我們都會使用UIKit進行渲染,所以iOS系統(tǒng)在drawRect返回CGContext的時候,默認幫我們進行了一次變換,以方便開發(fā)者直接用UIKit坐標系進行渲染。


我們嘗試對系統(tǒng)添加的坐標變換進行還原:

先進行CGContextTranslateCTM(context, 0, self.bounds.size.height);

對于x' = 2x, y' = 200 - 2y,我們使得x=x,y=y+100;(self.bounds.size.height=100

于是有x' = 2x, y' = 200-2(y+100) = -2y;

再進行CGContextScaleCTM(context, 1.0, -1.0);

對于x' = 2x, y' = -2y,我們使得x=x, y=-y;

于是有 x'=2x, y' = -2(-y) = 2y;

- (void)drawRect:(CGRect)rect {
 [super drawRect:rect];
 CGContextRef context = UIGraphicsGetCurrentContext();
 CGContextTranslateCTM(context, 0, self.bounds.size.height);
 CGContextScaleCTM(context, 1.0, -1.0);
 NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));
 NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"測試文本" attributes:@{
             NSForegroundColorAttributeName:[UIColor whiteColor],
             NSFontAttributeName:[UIFont systemFontOfSize:14],
             }];
 CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr); // 根據(jù)富文本創(chuàng)建排版類CTFramesetterRef
 UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 20)];
 CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 創(chuàng)建排版數(shù)據(jù)
 CTFrameDraw(frameRef, context);
}

通過log也可以看出來CGContext default matrix [2, 0, -0, 2, 0, 0];

最終結果如下,文本從左下角開始渲染,并且沒有出現(xiàn)上下顛倒的情況。

這時我們產生新的困擾:

用CoreText渲染文字的上下顛倒現(xiàn)象解決,但是修改后的坐標系UIKit無法正常使用,如何兼容兩種坐標系?

iOS可以使用CGContextSaveGState()方法暫存context狀態(tài),然后在CoreText繪制完后通過CGContextRestoreGState ()可以恢復context的變換。

- (void)drawRect:(CGRect)rect {
 [super drawRect:rect];

 CGContextRef context = UIGraphicsGetCurrentContext();
 NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));
 CGContextSaveGState(context);
 CGContextTranslateCTM(context, 0, self.bounds.size.height);
 CGContextScaleCTM(context, 1.0, -1.0);
 NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"測試文本" attributes:@{
                         NSForegroundColorAttributeName:[UIColor whiteColor],
                         NSFontAttributeName:[UIFont systemFontOfSize:14],
                         }];
 CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr); // 根據(jù)富文本創(chuàng)建排版類CTFramesetterRef
 UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 20)];
 CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 創(chuàng)建排版數(shù)據(jù)
 CTFrameDraw(frameRef, context);
 CGContextRestoreGState(context);
 
 
 NSLog(@"CGContext default CTM matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context)));
 UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
 testLabel.text = @"測試文本";
 testLabel.font = [UIFont systemFontOfSize:14];
 testLabel.textColor = [UIColor whiteColor];
 [testLabel.layer renderInContext:context];
}

渲染結果如下,控制臺輸出的兩個matrix都是[2, 0, 0, -2, 0, 200];

遇到的問題

1、UILabel.layer在renderInContext的時候frame失效

初始化UILabel時設定了frame,但是沒有生效。

UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 100, 28)];

這是因為frame是在上一層view中坐標的偏移,在renderInContext中坐標起點與frame無關,所以需要修改的是bounds屬性:

testLabel.layer.bounds = CGRectMake(50, 50, 100, 28);

2、renderInContext和drawInContext的選擇

在把UILabel.layer渲染到context的時候,應該采用drawInContext還是renderInContext?


雖然這兩個方法都可以生效,但是根據(jù)畫線部分的內容來判斷,還是采用了renderInContext,并且問題1就是由這里的一句Renders in the coordinate space of the layer,定位到問題所在。

3、如何理解CoreGraphics坐標系不一致后,會出現(xiàn)繪制結果異常?

我的理解方法是,我們可以先不考慮坐標系變換的情況。

如下圖,上半部分是普通的渲染結果,可以很容易的想象;

接下來是增加坐標變換后,坐標系變成原點在左上角的頂點,相當于按照下圖的虛線進行了一次垂直的翻轉。


也可以按照坐標系變換的方式去理解,將左下角原點的坐標系相對y軸做一次垂直翻轉,然后向上平移height的高度,這樣得到左上角原點的坐標系。

附錄

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關文章

  • iOS用兩行代碼完美解決數(shù)據(jù)持久化

    iOS用兩行代碼完美解決數(shù)據(jù)持久化

    所謂的持久化,就是將數(shù)據(jù)保存到硬盤中,使得在應用程序或機器重啟后可以繼續(xù)訪問之前保存的數(shù)據(jù)。在iOS開發(fā)中,有很多數(shù)據(jù)持久化的方案,接下來我將嘗試著介紹一種巧妙的方法,用兩行代碼解決這個問題,一起來學習下。
    2016-08-08
  • 詳談iOS 位置權限彈出框閃現(xiàn)的問題

    詳談iOS 位置權限彈出框閃現(xiàn)的問題

    下面小編就為大家?guī)硪黄斦刬OS 位置權限彈出框閃現(xiàn)的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • iOS Xcode8更新后輸出log日志關閉的方法

    iOS Xcode8更新后輸出log日志關閉的方法

    今天剛把xcode更新到了xcode8,運行發(fā)現(xiàn)好多l(xiāng)og輸出,怎么關閉呢,不是很清楚,通過查閱相關資料順利關掉這些log日志,下面小編把方法共享下,需要的朋友參考下
    2016-09-09
  • iOS程序開發(fā)之使用PlaceholderImageView實現(xiàn)優(yōu)雅的圖片加載效果

    iOS程序開發(fā)之使用PlaceholderImageView實現(xiàn)優(yōu)雅的圖片加載效果

    這篇文章主要介紹了ioS程序開發(fā)之使用PlaceholderImageView實現(xiàn)優(yōu)雅的圖片加載效果的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • iOS開發(fā)之適配iOS10以及Xcode8

    iOS開發(fā)之適配iOS10以及Xcode8

    這篇文章主要為大家詳細介紹了iOS開發(fā)之適配iOS10以及Xcode8的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • IOS多線程實現(xiàn)多圖片下載(一)

    IOS多線程實現(xiàn)多圖片下載(一)

    這篇文章主要介紹了IOS多線程實現(xiàn)多圖片下載(一)的相關資料,需要的朋友可以參考下
    2016-03-03
  • iOS實現(xiàn)抽屜效果

    iOS實現(xiàn)抽屜效果

    這篇文章主要為大家詳細介紹了iOS實現(xiàn)抽屜效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • iOS自定義UIScrollView的滾動條實例代碼

    iOS自定義UIScrollView的滾動條實例代碼

    本篇文章主要介紹了iOS自定義UIScrollView的滾動條實例代碼,詳細的介紹了自定義滾動條的示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • IOS客戶端接入微信支付

    IOS客戶端接入微信支付

    對于一個ios的app,如果有一些虛擬的商品或者服務需要通過在線支付來收費的話,一般有幾種主流的選擇。如果是通過APP調用支付平臺APP的思路的話,一個是調起支付寶客戶端,一個則是調起微信支付。本文給大家分享ios客戶端接入微信支付,需要的朋友可以參考下
    2015-09-09
  • IOS關閉鍵盤的方法

    IOS關閉鍵盤的方法

    在iOS應用開發(fā)中,有三類視圖對象會打開虛擬鍵盤,進行輸入操作,但如何關閉虛擬鍵盤,卻沒有提供自動化的方法。這個需要我們自己去實現(xiàn)。
    2015-05-05

最新評論