Stanford iOS10 公開課知識點(3)

Crazy巴旦木發表於2018-04-08

1.View 初始化:init(frame:CGRect),init(coder:NSCoder) 所有那些interface Builder裡構建的,其實都會被編碼,這就是coder這個轉碼器作為引數的原因,UI以XML檔案的格式儲存,當你執行程式時,那麼XML檔案作為指令載入。指導如何建立那些物件,使用的就是init(coder:)這個構造器來建立檢視,如果你有需要放到構造器裡的程式碼,比如說初始化UIView裡的一個變數,你需要兩個構造器都要重寫,init(coder:)是必要的,都要呼叫setup()來保證能夠正確的完成構造,如果你有需要初始化的變數,你可能沒法兒使用這種方法,因為你不能在完成構造之前呼叫自己的函式,所以你可能要把初始化變數的部分在兩個構造器中重複寫一遍。 還有另外一種初始化方法,適用於所有通過interface Builder構造的,awakeFromNib每個物件都會呼叫,只要是通過Interface Builder構造的都是,包括Storyboard編輯的檢視,雖然這不是構造器但是可以進行初始化工作,因此像“你必須完全初始化才能呼叫函式”這些約束是不能滿足的,但是還是可以放部分初始化程式碼在這裡。

UIView CGFloat 繪圖用浮點數,CGPoint,CGSize,CGRect 在繪圖時我們用的單位是點,不是畫素,一個畫素是指最小的能發光的單位,螢幕通過讓畫素髮光來顯示內容,在有些裝置上,許多個畫素一起。這是很好的,因為如果你繪製一條平滑的曲線或者顯示文字,就不會產生鋸齒,要保證平滑就需要很多的點,但螢幕上並沒有那麼多點,所以需要靠畫素。因此,在一個解析度很高的螢幕上,圖片會顯得很小,在解析度偏低的螢幕上會顯得很大,因為每個畫素都很大,所以不用畫素繪製,而是以點為單位,字型和點是一個概念。如果你想要知道每個點裡有多少個畫素,也就是解析度有多高,可以通過UIView裡面contentScaleFactor這個屬性,這是個CGFloat,比如iPhone 7Plus 就是3,每個點在每個方向上有3個畫素點,比如iPhone4 或者4s就是1個。 bounds。size,代表可繪製的大小,座標系。frame,center對繪圖毫無關係,是告訴你在父檢視中的位置。center是指你的中心在父檢視座標系的位置,frame是一個矩形,在父檢視座標系裡完全包含你的邊框。那麼bounds和frame的size是一樣大麼?不然,旋轉檢視,bounds是View的邊界,雖然被旋轉了,但是它都不知道被旋轉了,你觀察它的center和包含的frame,大小是不一樣的,frame是不一樣的。因為對於父檢視來說View是個菱形,所以要大些才能包含。“切忌在繪製中使用frame和center”,frame和center只是用來定位你在父檢視中的位置,如果你設定center,frame會跟著動,設定frame,center會相應調整。 為什麼需要自定義的檢視呢?要麼是需要自己繪圖,要麼是需要自定義多點觸控輸入的處理,這是最主要的原因。 繪製檢視。以前是drawRect現在叫draw引數是Rect。系統會呼叫你的這個方法,傳送給UIView的子類。你重寫這個方法,系統呼叫這個方法會是在當系統想要你繪製的時候,這是你在檢視中繪製的唯一方法。引數rect是你bounds裡的一部分,系統希望你繪製的部分,這樣能夠優化效能,所以能少畫就少畫。如果自己呼叫的話,那就是錯誤的,這個方法是由系統呼叫的,只應該讓系統來呼叫,如果你想重畫時,使用setNeedsDisplay,沒有引數,或者帶個引數rect,系統會在合適的時候讓你重畫。 實現draw(CGRect),建立一些路徑,然後描邊,填色,以此繪製圖形等。如何畫路徑?有兩種方法,一種是獲得context,用來繪製的上下文,然後發訊息給context,移動到這個點,畫一條線到這裡,這裡加一個圓弧等操作,另一種方法是建立一個路徑物件,使用UIBezierPath類似的操作,但是要先建立一個物件,然後告訴這個物件,我先要路徑移到這裡,然後這裡有條線,有個圓弧等等,然後發訊息給路徑,給路徑描邊。後一種方法比較簡單。 Core Graphics(CG),獲取繪製的context方法UIGraphicsGetCurrentContext(),會返回給你一個支援建立路徑和移動等等的上下文,然後通過線條和圓弧等構成路徑,然後在上下文中調整繪製屬性,比如想要的顏色,字型,線寬度,等等,設定好之後就store和fill就完成了,描邊和填充是僅有的兩條命令,真正能讓東西顯示在螢幕上。其實文字就是被填充的複雜線條,或者還描了邊。所以可以用填充和描邊做很多事。UIBezierPath把CG函式封裝進物件。 給顏色加透明的方法withAlphaComponent,alpha取0到1之間的值。如果你想要在你的UIView中用透明度繪製的話,你就可以用它來繪製,但是需要設定var opaque= false,把檢視的opaque設定false,因為檢視假定它們是不透明的,這跟效能提升有關,通過透明度繪製的話,會用到CPU效能來分辨哪些畫素要展示等等,預設情況下假定是不透明繪製,如果你想要通過透明度繪製的話,別忘了opaque = false。 繪製文字。一種方式是通過UILabel,只要建立一個UILabel然後把它作為你的檢視的子檢視,就可以在你的檢視中得到了文字。存在的問題是,你繪製的程式碼是在draw(rect:)中,你畫在那裡,如果你是在文字周圍畫,就要保證你繪製的與你的子檢視能對齊,你想在draw(rect:)中繪製文字,你想通過它繪製文字,就需要NSAttributedString,不是String,是一字串,有一些屬性顏色,字型之類的。你建立一個NSAttributedString然後讓它draw(at:某個點),它就以那個點為左上角畫那個字串,然後你就能通過size屬性得到它繪製所需要的空間大小。但是它是一個老式API,var和let都不能讓它可變或不可變。如果你想要一個可變的NSAttributedString來改變屬性和改變文字的話,就需要NSMutableAttributedString。也就是說設定屬性在NSAttributedString不起作用,在NSMutableAttributedString才會起作用。想要獲得起個不可變字串,使用NSMutableAttributedString(string:"xxxx"),使用mutableString獲得可變字串。通過傳遞進一個字典來設定屬性,在這裡實參名字叫做attributes,你既可以設定屬性又可以新增到屬性那裡,NSRange是富文字字串的整數索引範圍,在Swfit中用富文字還是很麻煩的。建議在String中用叫做utf16的變數,utf16會返回一個叫String.UTF16View的東西,它是一個字元的集合,但它不像是characters,它是代表這個字串的每16位Unicode表示組成的集合,和NSString一樣,唯一的問題是它得到的和返回的仍舊是由String.Index來索引,所以必須說起始索引是什麼,偏移多少之類的來定位,另外不可以傳遞一個String.Index的Range到這個setAttribute裡,所以你必須從String.Index把你想要的範圍轉化到整形構成的NSRange。 可以設定的屬性有哪些?NSForegroundColorAttributeName,設定文字的顏色,NSBackgroundColorAttributeName,設定字型本身和底色,NSStrokeWidth能幫你把字母繪製成粗或細,NSFontAttributeName設定字型,等等。 如果你想要設定你即將繪製的東西的字型的話,你想要好看的話,需要一個重要的靜態方法,屬於UIFont類,就是preferredFont(forTextStyle:),你把它傳給UIFont。你可以指定文字樣式,就像是你講要使用這個文字的環境,它是什麼樣子的文字。大概有8個樣式(比如UIFontTextStyle.headline等),非常鼓勵在設定文字時找一個適合的文字樣式。並且可以根據系統設定自動變化,如果你有正確的自動佈局時,每一樣東西都會正確的伸縮。還有一些系統字型,別把系統字型和首選字型混淆,系統提供的是systemFont,首選字型是為使用者的內容提供的,這就是區別。UIFontDescriptor用來描述特殊字型,有一些程式是做版面設計的,能夠處理一些特殊的字型。

Drawing Images。一種是把UIImageView作為子檢視就可以。但有時候想要在draw(rect:)中繪製圖片,就用到了UIImage物件,當展示可失敗結構體時,已經展示瞭如何建立一個UIImage,let image:UIImage? = UIImage(named:"xxx")。只要把圖片放到xcassets檔案裡,找不到就返回空。另一種方法你可以從一個檔案裡取得圖片檔案,也可以通過URL獲取到圖片,還可以通過UIBezierPath來繪製,然後通過EndImageContext會抓取那個區域裡面的內容然後建立一個小影象給你。draw(atPoint:)會用point作為圖片的左上角用預設尺寸畫出圖片,draw(inRect:)會縮放圖片去適應你提供的矩形,drawAsPattern(inRect:)會把圖片鋪滿你提供的矩形(小圖片的話重複填滿)。 當UIView的尺寸改變時,不會被預設重繪。只是抓著你所繪製的畫素,然後直接拉伸它們去適應你的新區域,但是達不到想要的效果。想要的效果是如果bounds變了,就通知我在新的區域重繪,怎麼控制重繪呢?UIView中有個contentMode的變數去控制它,去分散畫素點,縮放檢視使用scaleToFill是預設的處理方式,拿現在的檢視,通過拉伸去填充新的邊界,如果想保持寬高比,但是一部分會被截斷,無法顯示。真正想要的是重繪,如果contentMode的值為.redraw,當你的邊界被改變時,就會呼叫draw(rect:),用rect引數重繪。實際上既可以在程式碼中設定這個變數重繪,也可以在Interface Builder中啟用重繪,在Inspector中有contentMode。 @IBDesignable @IBInspectable 靈活運用 @IBInspectable 的型別必須是明確的,不能讓Swift猜。

相關文章