上一節中,我們用簡簡單單的幾行代碼實現了一個iOS動畫,本節將對iOS動畫代碼做詳細解釋,並開始用純代碼方式來進行UI開發。
什麼是UI開發:UI即User Interface(用戶界面)的簡稱,泛指用戶的操作界面。通俗點說,就是一個APP上用戶能看到的、能觸摸、拖拉的東西,比如背景、文字、按鈕、圖片等都是構成UI的元素,選用什麼顏色、什麼形狀、擺放到什麼位置都是UI設計的工作。UI設計可以讓軟件變得美觀有個性有品味,還可讓軟件的操作變得舒適、簡單、自由,充分體現軟件的定位和特點。因此UI設計是很重要的,但是UI設計主要是設計師和美工的工作。程序員的主要工作是在App上實現這些UI設計,我們稱為UI開發。UI設計與開發直接影響用戶的體驗,在移動App開發中佔有很重的比例,每一個iOS程序員都必須熟練掌握。本節分享一種UI開發方法:用純代碼來寫UI。
一、創建工程
打開Xcode創建一個iOS的"Single View App"工程,工程名為:MyCode。不懂創建的人請參看
二、工程目錄簡介
上圖界面中左側有四個大文件夾,下面大致瞭解一下這些文件夾的作用:
1. Products: 主要用於mac電腦軟件開發,iOS開發用不到。
2. MyCodeTests: 用於單元測試。
3. MyCodeUITests: 用於UI測試。
4. MyCode: iOS開發的內容主要都是存放在這個文件夾中。
MyCode這個文件夾又包含:
4.1 AppDelegate.swift:代表應用程序,App初始化需要的內容都在這裡做,App是從這裡開始啟動的,這個文件暫時不做深入。
4.2 ViewController.swift: 這是iOS存放視圖控制器類的文件(如果不懂得什麼是“類”,應該先看看“面向對象”編程思想相關的文章),裡面定義一個頁面 (ViewController),我們編寫的UI代碼都要寫在這個頁面裡,比如文字、按鈕、圖片等元素都要擺放在這個頁面裡,才能展示給用戶看。本節將重點關注這個文件以及裡面定義的類。
4.3 Main.storyboard: 這個文件裡面定義一個主storyboard(即故事板),storyboard裡面可以放置多個頁面(ViewController),頁面之間的跳轉關係也可以在storyboard裡面定義。故事板可以幫助我們用比較直觀的方式來快速的開發UI,通過storyboard我們可以看到我們設計的頁面長什麼樣子。比如,我們要在頁面上添加一張圖片,我們只要將一個圖片控件直接拉到storyboard上,就可以看到這個圖片在頁面上到底是大是小,位置在哪裡等等。這是iOS推薦的UI開發模式。有人要問了,那我們還要用代碼寫UI,不是很麻煩嗎?其實這兩種方式寫UI各有優缺點,我們可以取長補短,這在後面講到storyboard的時候再討論。Main.storyboard顧名思義就是主頁面所在的storyboard,也就是點開App後,默認的用戶看到的第一個頁面。我們可以在一個項目中定義多個storyboard,一個storyboard裡面又可以定義多個頁面(ViewController)。所以storyboard可以認為是一群相關頁面的集合。
5. Assets.xcassets: 這個文件夾主要用於存放資源文件,比如圖片
6. LauchScreen.storyboard: 顧名思義就是啟動頁面所在的storyboard文件。在打開一個App的時候,一般不會直接跳到主頁面,經常會先來個倒計時,先顯示某某公司Logo或者廣告圖片視頻什麼的,承擔這個功能的頁面就叫啟動頁。
7. info.plist: 這個文件是項目的配置文件,比如主頁面是哪個頁面,Main.storyborad只是創建項目時系統默認的主頁面所在的storyborad,在info.plist裡可以隨時更換到別的storyborad。
三、認識視圖控制器
下面我們重點關注ViewController.swift這個文件,單擊這個文件,得到如下界面(紅框和箭頭是我做的標記),下面逐一解釋代碼的作用
import UIKit:UIKit是iOS的SDK提供給我們專門用於編寫UI的庫或者包,import是導入這個庫的意思,只有import之後,在這個文件中才能使用這個庫提供的相關的類來編寫UI。以後還會用到更多SDK中的庫,也要這麼導入。上面說的這些庫都是iOS的SDK內部提供的庫,使用時可以直接這樣導入。站在巨人的肩膀上,明顯可以省很多事,因此有時我們要經常在項目中用別人造過的輪子或者說工具,這些叫第三方庫。使用第三方庫時略微麻煩一點,怎麼操作請關注後續我的文章。
UIViewController:這是UIKit庫中一個重要的類,顧名思義“視圖控制器”。項目創建時,系統自動為我們創建了一個ViewController類來繼承UIViewController類。一個ViewController類的實例化對象對應App中的一個頁面。所以很明顯,我們的UI代碼應該寫在ViewController類裡面。我們暫時不用關注這個類是怎麼被實例化的,只要知道當我們編譯運行時,系統會自動為我們實例化這個類。
viewDidLoad(): 這是UIViewController中的一個“方法”(也叫“函數”),代表頁面容器已經初始化完畢,這時頁面什麼都沒有還是空白的,我們可以在這時往頁面中添加我們設計的UI元素,比如圖片、文字。每個頁面都有一個完整的生命週期:從它開始被創建一直到它被銷燬回收。在這整個生命週期的不同階段,UIViewController都會提供對應的函數(viewDidLoad()就是其中一個),讓我們有機會進行修改界面、回收資源等操作。我們要添加的UI代碼都是寫在圖中紅色箭頭所指的地方。其他生命週期函數還有很多,有興趣可以自己查找學習,在這裡不做介紹。
四、UIView
UIView是UIKit庫中視圖的基類,可以理解為頁面中的一個色塊,如下圖大紅框框中的部分就是一個頁面(ViewController),而其中的紅色的塊就是一個UIView。
1. 屬性和佈局
我們對視圖最關心的有兩點:
a) 它長什麼樣:這稱為視圖的屬性,比如是什麼顏色、是否有邊框、邊框的顏色、邊框的大小等
b) 它應該放在頁面的哪個位置:這就是佈局,佈局有兩種方法。一種使用frame,另一種是用AutoLayout。
(1)屬性
對於以後要用到的其他更高級的視圖控件,比如UILabel(專門顯示文字)、UIImageView(專門顯示圖片)、UIButton(按鈕)等都是類似的。只是他們有更多的屬性而已。我們學習這些視圖,無非就是熟悉他們的屬性和佈局,因此可以舉一反三。
下面是一段最簡單的例子:
let purpleView = UIView()
purpleView.backgroundColor=UIColor.purple
purpleView.frame=CGRect(x: 0, y: 100, width: 150, height: 150)
view.addSubview(purpleView)
可以將上面這段黑色加粗的代碼拷貝到如下圖所示的位置:
編譯運行效果如下圖:
下面對代碼逐行做說明:
let purpleView = UIView() //這句是創建一個視圖,注意要定義在函數外面
purpleView.backgroundColor = UIColor.purple //這句是將視圖的背景色設置為紫色
purpleView.frame = CGRect(x: 0, y: 100, width: 150, height: 150) /*這句是設置視圖的大小和位置:x:0表示視圖與頁面的左邊距離為0,y:100表示視圖與頁面的上邊距離為100,width:150代表視圖寬度為150,height:150代表視圖的高度為150。*/
view.addSubview(purpleView) //這句是表示將purpleView這個視圖對象添加到頁面裡
注意:iOS用“//”來註釋單行代碼,可以用這個方法將臨時不用的代碼註釋起來,也可以用來對代碼進行說明。被“//”註釋後的代碼在程序運行時,將不會執行。
(2)佈局
還可以用自動佈局AutoLayout的約束方式來限制視圖的位置:
//創建一個視圖,與上面一樣要放置的函數外面
let redView = UIView()
//禁止將AutoresizingMask轉化為Constraints
redView.translatesAutoresizingMaskIntoConstraints = false
// 背景色為紅色
redView.backgroundColor = UIColor.red
// 將視圖添加到頁面中
view.addSubview(redView)
//創建約束,注意:要在視圖添加到其父容器後,才能進行約束設置,否則App會奔潰
//寬度約束
let widthConstraint = NSLayoutConstraint(item: redView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 150)
//高度約束
let heightConstraint = NSLayoutConstraint(item: redView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 150)
//頂部約束
let topConstraint = NSLayoutConstraint(item: redView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 100)
//左側約束
let leftConstraint = NSLayoutConstraint(item: redView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 150)
//在頁面中添加多個約束
view.addConstraints([widthConstraint,heightConstraint,leftConstraint,topConstraint])
上面這段黑色加粗的代碼可以直接拷貝到項目中:
編譯運行結果如下:
用自動佈局AutoLayout的約束來設置視圖的位置是比較靈活的,但是iOS系統提供的這種寫法,明顯太繁瑣。所以一般使用第三方提供的SnapKit庫來簡化代碼的寫法,有興趣的人可以查閱相關資料。
2. 動畫
上面談到的屬性和佈局都是設置靜止不動的內容,有時我們想讓這些圖片或文字能夠動起來,這樣看起來比較生動有趣,這就要用到動畫。
在 那節,我們分享了一個簡單的iOS動畫,沒有對代碼做任何解釋,下面將對動畫代碼做詳細說明。主要代碼如下:
animView.frame=CGRect(x:0,y:0,width:100,height:100) //設置動畫視圖的尺寸
animView.center=view.center //動畫視圖放在父視圖的正中央
animView.backgroundColor=UIColor.green//動畫視圖的背景色設置為綠色
view.addSubview(animView)//將動畫視圖添加到父視圖(即頁面)
UIView.animate(withDuration: 2,delay:1,usingSpringWithDamping:0.2,initialSpringVelocity:0,options:[.repeat,.autoreverse], animations:{
self.animView.transform=CGAffineTransform(scaleX: 0.5, y: 0.5)//將動畫視圖大小縮小為原來的一半
},completion:nil)
前面四行,是設置屬性和佈局可以參看上面的,不做說明。我們重點關注最後一個方法:
UIView.animate()這個方法是UIView類提供的一個靜態方法,專門用於播放和控制視圖動畫。裡面的參數有:
withDuration: 2表示動畫總的時長為2秒
delay:1表示動畫延時1秒才開始播放,就是這段動畫代碼被執行後不馬上播放,而要等1秒鐘後才開始播放。
usingSpringWithDamping:0.2彈簧動畫的阻尼值,也就是相當於摩擦力的大小,該屬性的值從0.0到1.0之間,越靠近0,阻尼越小,彈動的幅度越大,反之阻尼越大,彈動的幅度越小,如果大道一定程度,會出現彈不動的情況。
initialSpringVelocity:0彈簧動畫的速率,或者說是動力。值越小彈簧的動力越小,彈簧拉伸的幅度越小,反之動力越大,彈簧拉伸的幅度越大。這裡需要注意的是,如果設置為0,表示忽略該屬性,由動畫持續時間和阻尼計算動畫的效果。
options後面可以設置很多可選項,.repeat這個選項表示動畫是重複的,.autoreverse這個選項表示動畫播放完畢後會自動倒播,注意這些選項前面要加一個點。
animations:{}這個大括號裡面我們要指定視圖最終屬性值。比如我們要讓一個原來透明度為1的視圖慢慢變為透明度為0.5,這個過程我們不需要關心,我們只要在這個大括號中將視圖最終0.5這個值設置好就行了。系統會根據視圖最初的透明度以及我們設置的這個0.5自動生成中間值並用這些值來控制完成動畫過程。也就是說,我們代碼只要告訴系統,視圖最終的屬性值是多少系統就會為我們自動生成這些動畫過程。
scanX:0.5,y:0.5表示這是一個縮放動畫,並且視圖在X方向縮小為原來的一半,Y方向也縮小為原來的一半,總體看就是方塊整體慢慢縮小一半。
2.1 動畫類型
按照動作,動畫可分為以下幾個類型:
(1)平移:
self.animView.transform=CGAffineTransform(translationX: -200, y: 20)
translationX: -200, y: 0表示視圖向左移動200距離,同時向下移動20距離。x正值表示向右移動,負值表示向左移動;y正值表示向下移動,負值表示向上移動。
(2)縮放:
self.animView.transform=CGAffineTransform(scaleX: 0.5, y: 0.5)
scanX:0.5,y:0.5表示視圖在x方向縮小為原來的一半,y方向也縮小為原來的一半,總體看就是方塊整體慢慢縮小一半。x和y的值一般要大於等於0
(3)旋轉:
self.animView.transform=CGAffineTransform(rotationAngle:CGFloat.pi/4)
rotaionAngle:CGFloat.pi/4表示順時針旋轉45度,CGFloat.pi/4代表旋轉的角度。
2.1 組合動畫
有的人說了:我想同時變可以嗎,比如平移的同時縮放?當然可以啦。可以這樣寫:
let scale=CGAffineTransform(scaleX:0.6,y:0.6)//縮小到原來的0.6倍
let rotation=CGAffineTransform(rotationAngle:CGFloat.pi/4)//順時針旋轉45度
self.animView.transform=rotation.concatenating(scale)
用concatenating()這個方法,你想套幾個動畫都可以,比如還有一個平移動畫可以這樣寫:
let transY=CGAffineTransform(translationX: 0, y: 150)//向下移動150的距離
let scale=CGAffineTransform(scaleX:0.6,y:0.6)//縮小到原來的0.6倍
let rotation=CGAffineTransform(rotationAngle:CGFloat.pi/4)//順時針旋轉45度
self.animView.transform=transY.concatenating(rotation.concatenating(scale))
這些動畫就不在這裡演示了,因為這裡沒辦法加視頻。用相關代碼替換掉對應的行就可以實現。
本系列會不斷更新,有興趣的同學,請關注我。
閱讀更多 移動大電猿 的文章