第八章——動畫控制【譯】

“動畫”一詞源自拉丁語,意思是“生命的行為”。在您的應用程序中,動畫可以將界面元素平滑地帶入屏幕或焦點,可以吸引用戶的注意力,而它們可以清楚地表明您的應用程序是如何響應用戶操作的。 在本章中,您將返回到您的 Quiz 應用程序,并使用各種動畫技術來實現。

在更新 Quiz 之前,我們可以通過文檔來查看什么能夠動畫化。 要打開文檔,請打開 XcodeHelp 菜單,然后選擇 Documentation and API Reference。 這將在新窗口中打開文檔。

打開文檔后,使用頂部的搜索欄搜索 “UIView”。在搜索結果的 API Reference 下,單擊 UIView 打開類引用,然后向下滾動到標題為 Animations 的部分。 該文檔提供了一些動畫建議(我們將在本書中介紹),并列出了可以動畫化的 UIView 上的屬性(圖8.1)。

圖8.1 UIView Animations 文檔

基本動畫

該文檔始終是了解任何 iOS 技術的良好起點。 有了這個小小的信息,讓我們繼續添加一些動畫到 ‘Quiz’。 您將要使用的第一種類型的動畫是 基本動畫(basic animation)。 基本動畫在起始值和結束值之間產生動畫(圖8.2)。

圖8.2 基本動畫

您要添加的第一個動畫將動畫化與 ViewController 相關聯的問題標簽的 alpha 值(透明度)。 當用戶進入下一個問題時,您將使用動畫來淡出標簽。 UIView 上有類方法可以讓你完成這個。 最簡單的 UIView 動畫方法是:

class func animate(withDuration duration: TimeInterval, animations: () -> Void)

此類方法有兩個參數:類型為 TimeInterval(它是 Double 的別名)的 duration 和作為閉包的 animations 變量。

閉包

閉包(closure) 是一組離散的功能,可以圍繞您的代碼傳遞。 閉包很像函數和方法。 事實上,函數和方法是閉包的特殊情況。

閉包具有輕量級的語法,允許它們作為參數容易地傳遞給函數和方法。 閉包甚至可以是函數或方法的返回類型。 在本節中,您將使用閉包來指定要發生的動畫。

一個閉包的語法是逗號分隔的括號中的參數列表,后跟一個返回箭頭和返回類型:

(arguments) -> return type

請注意,此語法與函數的語法類似:

func functionName(arguments) -> return type

現在再看一下 animations 參數期望的閉包用法:

class func animate(withDuration duration: TimeInterval, animations: () -> Void)

這個閉包不會引用任何參數,也不會返回任何內容。 (你有時會看到這個返回類型表示為(),這與 Void 相同)

閉包用法非常簡單和熟悉,但是如何在代碼中聲明關閉? 閉包語法采用以下形式:

{ (arguments) -> return type in
??// code
}

閉包表達式寫在 大括號({}) 中。 閉包的參數在開頭大括號之后的括號內列出。 閉包的返回類型來自參數,并使用常規語法。 關鍵字 in 用于將閉包的參數和返回類型與其正文中的語句分開。

打開 Quiz.xcodeproj。 在 ViewController.swift 中,添加一個新方法來處理動畫,并聲明一個不帶參數的閉包常量,不返回任何東西。

func animateLabelTransitions() {

??let animationClosure = { () -> Void in

??}
}

你使用一個常數,引用了一大堆功能。 然而,目前,這個閉包實際上并沒有做任何事情。 向閉包添加功能,將 questionLabelalpha 設置為 1.然后,將此閉包作為參數傳遞給 animate(withDuration:animations :)

func animateLabelTransitions() {

??let animationClosure = { () -> Void in
????self.questionLabel.alpha = 1
??}

??// Animate the alpha
??UIView.animate(withDuration: 0.5, animations: animationClosure)
}

在屏幕上,questionLabel 已經具有1alpha 值,所以如果您構建并運行,您將看不到任何動畫。 為了解決這個問題,重寫 viewWillAppear(_ :),以便每次 ViewController 的視圖進入屏幕時,將 questionLabelalpha 重置為 0

override func viewWillAppear(_ animated: Bool) {
??super.viewWillAppear(animated)

??// Set the label's initial alpha
??questionLabel.alpha = 0
}

以上代碼非常好,但您可以使其更簡潔。 更新代碼。

func animateLabelTransitions() {

??let animationClosure = { () -> Void in
????self.questionLabel.alpha = 1
??}

??// Animate the alpha
??UIView.animate(withDuration: 0.5, animations: animationClosure)

??UIView.animate(withDuration: 0.5, animations: {
????self.questionLabel.alpha = 1
??})
}

你有兩個改變:首先,你匿名傳遞閉包(即將它直接傳遞給方法,而不是將其分配給變量或常量)。 其次,您已經刪除了類型信息,因為閉包可以從上下文推斷出這一點。

當用戶點擊 Next Question 按鈕時,調用 animateLabelTransitions() 方法。

@IBAction func showNextQuestion(_ sender: UIButton) {
??currentQuestionIndex += 1
??if currentQuestionIndex == questions.count {
????currentQuestionIndex = 0
??}

??let question: String = questions[currentQuestionIndex]
??questionLabel.text = question
??answerLabel.text = "???"

??animateLabelTransitions()

}

構建并運行應用程序。 當您點擊 Next Question 按鈕時,標簽將淡入視圖。 動畫提供了令人震驚的用戶體驗,而不只是簡單地讓視圖直接出現。

下一個標簽

第一次按下 Next Question 按鈕時,動畫效果很好,但是隨后的按鈕按下時,沒有可見的動畫,因為標簽的 alpha 值為 1.在本節中,您將要添加另一個標簽。 當按下 “下一個問題” 按鈕時,現有標簽將淡出,而新標簽(下一個問題的文本)將淡入。

ViewController.swift 的頂部,用兩個標簽替換單個標簽的聲明。

@IBOutlet var questionLabel: UILabel!
@IBOutlet var currentQuestionLabel: UILabel!
@IBOutlet var nextQuestionLabel: UILabel!
@IBOutlet var answerLabel: UILabel!

Xcode 標記四個地方,您需要用其中一個新標簽替換 questionLabel。 更新 viewDidLoad() 以使用 currentQuestionLabel。 更新 viewWillAppear(_ :)showNextQuestion(_ :) 以使用 nextQuestionLabel

func viewDidLoad() {
??super.viewDidLoad()
??questionLabel.text = questions[currentQuestionIndex]
??currentQuestionLabel.text = questions[currentQuestionIndex]
}

override func viewWillAppear(_ animated: Bool) {
??super.viewWillAppear(animated)

??// Set the label's initial alpha
??questionLabel.alpha = 0

nextQuestionLabel.alpha = 0
}

@IBAction func showNextQuestion(_ sender: UIButton) { currentQuestionIndex += 1
??if currentQuestionIndex == questions.count {
????currentQuestionIndex = 0
??}

??let question: String = questions[currentQuestionIndex]
??questionLabel.text = question
??nextQuestionLabel.text = question
??answerLabel.text = "???"

??animateLabelTransitions()
}

現在更新 animateLabelTransitions() 以動畫化兩個標簽的透明度。 您將淡出 currentQuestionLabel 并同時淡入 nextQuestionLabel

func animateLabelTransitions() {

??// Animate the alpha
??UIView.animate(withDuration: 0.5, animations: {
????self.questionLabel.alpha = 1
????self.currentQuestionLabel.alpha = 0
????self.nextQuestionLabel.alpha = 1
??})
}

打開 Main.storyboard。 現在,這兩個標簽的代碼已被更新,您需要進行連接。 右鍵單擊 View Controller 以查看連接列表。 請注意,現有的 questionLabel 在其旁邊仍然存在黃色警告標志(圖8.3)。 單擊 x 刪除此連接。

圖8.3 缺少連接

通過從currentQuestionLabel 標簽旁邊的圓圈拖到畫布上的標簽,將 currentQuestionLabel outlet 連接到問題標簽。

現在將一個新的 Label 拖到界面上,并將其放在 currentQuestionLabel 標簽旁。 將 nextQuestionLabel 連接到此新標簽。

您希望此標簽與現有問題標簽處于相同的位置。 正如你可能猜到的那樣,實現這一目標的最好方法是通過約束。 右擊從 nextQuestionLabel 拖動到 currentQuestionLabel 并選擇 Top。 然后右擊從
nextQuestionLabel 向上拖動到其父視圖,并選擇 Center Horizontally in Container

現在,nextQuestionLabel 放錯了。 選擇它,打開 Resolve Auto Layout Issues 菜單,然后選擇 Update Frames。 標簽現在將出現在另一個的上面。

構建并運行應用程序。 點擊 Next Question 按鈕,您將看到兩個標簽優雅地淡入淡出。

如果再次點擊它,則不會再出現淡入淡出,因為 nextQuestionLabel 已經具有 1 的透明度。要解決此問題,您將交換對兩個標簽的引用。 當動畫完成時,需要將 currentQuestionLabel 設置為屏幕標簽,并將 nextQuestionLabel 設置為屏幕外標簽。 您將使用 動畫的完成者(completion handler) 來完成此操作。

動畫完成

方法 animate(withDuration:animations :) 很快就會結束并返回。 也就是說,它開始動畫,但是并不等待動畫完成。 如果你想知道什么時候動畫完成怎么辦? 例如,您可能希望將動畫鏈接在一起,或者在動畫完成時更新另一個對象。 要知道動畫的完成時間,可以使用閉包中的 completion 參數。 您將利用此機會交換兩個標簽引用。

ViewController.swift 中,更新 animateLabelTransitions() 以使用具有最多參數的 UIView 動畫方法,其中包括一個 completion 閉包的動畫方法。

func animateLabelTransitions() {

??// Animate the alpha
??UIView.animate(withDuration: 0.5, animations: {
??self.currentQuestionLabel.alpha = 0
??self.nextQuestionLabel.alpha = 1
??})
??UIView.animate(withDuration: 0.5,
????delay: 0,
????options: [],
????animations: {
????self.currentQuestionLabel.alpha = 0
??????self.nextQuestionLabel.alpha = 1
????},
????completion: { _ in
????swap(&self.currentQuestionLabel,
????&self.nextQuestionLabel)
??})
}

delay 指示系統在觸發動畫之前等待多長時間。 我們將在本章后面討論這些選項。 現在,你將傳遞一個空的數組。

在 completion 閉包中,您需要告訴系統,以前是 currentQuestionLabel 的現在是 nextQuestionLabel,而以前的 nextQuestionLabel 現在是 currentQuestionLabel。 要實現這一點,您可以使用 swap( :) 函數,它接受兩個引用并交換它們。

構建并運行應用程序。 現在你可以在所有的問題之間轉換。

動畫化約束

在本節中,您將擴展您的動畫,使 nextQuestionLabel 從屏幕左側飛入,當用戶按下 Next Question 按鈕時, currentQuestionLabel 會從右側飛出。 在這樣做的時候,你需要學習如何動畫化約束。

首先,您需要參考需要修改的約束。 到目前為止,所有 @IBOutlet 都是視圖對象。 但是,outlet 不僅限于視圖——實際上,您的界面文件中的任何對象都可以有 outlet,包括約束。

ViewController.swift 的頂部,為兩個標簽的中心約束聲明兩個 outlet。

@IBOutlet var currentQuestionLabel: UILabel!
@IBOutlet var currentQuestionLabelCenterXConstraint: NSLayoutConstraint!
@IBOutlet var nextQuestionLabel: UILabel!
@IBOutlet var nextQuestionLabelCenterXConstraint: NSLayoutConstraint!
@IBOutlet var answerLabel: UILabel!

現在打開 Main.storyboard。 您想將這兩個 outlet 連接到各自的約束。 最簡單的方法是使用文檔大綱。 單擊文檔大綱 Constraints 旁邊的三角形,并找到 Current Question Label CenterX Constraint。 右擊 View Controller 拖動到該約束(圖8.4)并選擇正確的 outlet。 對 Next Question Label CenterX Constraint 進行同樣的操作。

圖8.4連接約束outlet

目前,Next Question 按鈕和答案子視圖的中心 X 約束在 currentQuestionLabel 的中心 X。 當您實現此標簽的動畫以在屏幕外滑動時,其他子視圖將隨之而來。 這不是你想要的。

選中 Next Question 按鈕的 X 值居中到 currentQuestionLabel 的約束并將其刪除它。 然后從右擊 Next Question 按鈕向上拖動到其父視圖,然后選擇 Center Horizontally in Container

接下來,您希望兩個問題標簽在一個屏幕寬度中分開。 nextQuestionLabel 的中心將是距視圖左側的屏幕寬度的一半。 currentQuestionLabel 的中心將處于當前位置,居中在當前屏幕。

當動畫被觸發時,兩個標簽將向右移動全屏寬度,將 nextQuestionLabel 放置在屏幕的中心,將 currentQuestionLabel 放在屏幕右側的屏幕寬度的一半(圖8.5)。

圖8.5滑動標簽

為了實現這一點,當加載 ViewController 的視圖時,您需要將 nextQuestionLabel 移動到其屏幕外的位置。

ViewController.swift 中,添加一個新方法,并從 viewDidLoad() 中調用它。

func viewDidLoad() {
??super.viewDidLoad()
??currentQuestionLabel.text = questions[currentQuestionIndex]

??updateOffScreenLabel()
}

func updateOffScreenLabel() {
??let screenWidth = view.frame.width
??nextQuestionLabelCenterXConstraint.constant = -screenWidth
}

你想要動畫化標簽從左到右地顯示。 動畫化約束與動畫化其他屬性有所不同。 如果修改動畫塊中的約束的常量,則不會發生動畫。 為什么? 修改約束后,系統需要重新計算層中所有相關視圖的邊框以適應此更改。 任何約束更改都會自動觸發,這將是非常浪費的。 (想象一下,如果您更新了相當多的約束條件,你不會想讓它在每次更改后都重新計算一次這些邊框)。所以您必須要求系統在修改完成后重新計算邊框。 為此,您可以在視圖中調用 layoutIfNeeded() 方法。 這將迫使該視圖根據最新的約束來布局其子視圖。

ViewController.swift 中,更新 animateLabelTransitions() 以更改約束常量,然后強制更新視圖的布局。

func animateLabelTransitions() {

??// Animate the alpha
??// and the center X constraints
??let screenWidth = view.frame.width
??self.nextQuestionLabelCenterXConstraint.constant = 0
??self.currentQuestionLabelCenterXConstraint.constant += screenWidth

??UIView.animate(withDuration: 0.5,
????delay: 0,
????options: [],
????animations: {
??????self.currentQuestionLabel.alpha = 0
??????self.nextQuestionLabel.alpha = 1

??????self.view.layoutIfNeeded()
????},
????completion: { _ in
??????swap(&self.currentQuestionLabel,
????????&self.nextQuestionLabel)
??})
}

最后,在完成處理程序中,您需要交換兩個約束 outlet,并將 nextQuestionLabel 重置為屏幕左側。

func animateLabelTransitions() {

??// Animate the alpha
??// and the center X constraints
??let screenWidth = view.frame.width
??self.nextQuestionLabelCenterXConstraint.constant = 0
??self.currentQuestionLabelCenterXConstraint.constant += screenWidth

??UIView.animate(withDuration: 0.5,
????delay: 0,
????options: [],
????animations: {
??????self.currentQuestionLabel.alpha = 0
??????self.nextQuestionLabel.alpha = 1

??????self.view.layoutIfNeeded()
????},
????completion: { _ in
??????swap(&self.currentQuestionLabel,
????????&self.nextQuestionLabel)
??????swap(&self.currentQuestionLabelCenterXConstraint,
????????&self.nextQuestionLabelCenterXConstraint)

??????self.updateOffScreenLabel()
??})
}

構建并運行應用程序。 動畫幾乎完美。 標簽在屏幕上滑動和關閉,alpha 值也適當地動畫化。

有一個小問題要解決,但可能會有點難發現。 要發現它,請從模擬器(Command-T)的 Debug 菜單中打開 Slow Animations。 所有標簽的寬度都會被動畫化(要在 answerLabel 上看到這一點,您需要點擊 Show Answer 按鈕)。 這是因為內在內容大小會在文本更改時發生變化。 解決方法是強制視圖在動畫開始之前布局其子視圖。 這將在 alpha 和滑動動畫開始之前更新所有三個標簽的邊框,以適應下一個文本。

更新 animateLabelTransitions() 以強制視圖在動畫開始之前布局其子視圖。

func animateLabelTransitions() {

??// Force any outstanding layout changes to occur
??view.layoutIfNeeded()

??// Animate the alpha
??// and the center X constraints
??let screenWidth = view.frame.width
??self.nextQuestionLabelCenterXConstraint.constant = 0
??self.currentQuestionLabelCenterXConstraint.constant += screenWidth

??UIView.animate(withDuration: 0.5,
????delay: 0,
????options: [],
????animations: {
??????self.currentQuestionLabel.alpha = 0

self.nextQuestionLabel.alpha = 1

??????self.view.layoutIfNeeded()
????},
????completion: { _ in
??????swap(&self.currentQuestionLabel,
????????&self.nextQuestionLabel)
??????swap(&self.currentQuestionLabelCenterXConstraint,
????????&self.nextQuestionLabelCenterXConstraint)

??????self.updateOffScreenLabel()
??})
}

構建并運行應用程序并循環瀏覽一些問題和答案。 動畫小問題現已解決。

定時功能

動畫的加速由其定時功能控制。 默認情況下,動畫使用 ease-in/ease-out 的定時功能。 用駕駛來類比,這意味著司機從休息時間平穩地加速到恒定的速度,然后在最后逐漸減速,休息。

其他定時功能包括 線性(linear)(從頭到尾的恒定速度),緩和(ease-in)(加速到恒定速度,然后突然終止)和 緩解(ease-out)(從全速開始,然后最終減慢)。

ViewController.swift 中,更新 AnimateLabelTransitions() 中的動畫以使用線性定時函數。

UIView.animate(withDuration: 0.5,
??delay: 0,
??options: [ .curveLinear ],
??animations: {
????self.currentQuestionLabel.alpha = 0
????self.nextQuestionLabel.alpha = 1

????self.view.layoutIfNeeded()
??},
??completion: { _ in
??swap(&self.currentQuestionLabel, &self.nextQuestionLabel)
??swap(&self.currentQuestionLabelCenterXConstraint, &self.nextQuestionLabelCenterXConstraint)

??self.updateOffScreenLabel()
})

現在,與使用默認的 easy-in/ease-out 動畫曲線相反,動畫將具有線性動畫曲線。 構建并運行應用程序。 差異是微妙的,但如果你注意它也是能發現的。

options 參數接受 UIViewAnimationOptions 參數。 為什么這個參數在方括號內? 除了定時功能之外,動畫還有很多選擇。 因此,您需要一種指定多個選項(數組)的方法。 UIViewAnimationOptions 符合 OptionSet 協議,允許您使用數組對多個值進行分組。

以下是可以傳入options 參數的一些可能的動畫選項。

動畫曲線選項控制動畫的加速。 可能的值是:

  • UIViewAnimationOptions.curveEaseInOut
  • UIViewAnimationOptions.curveEaseIn
  • UIViewAnimationOptions.curveEaseOut
  • UIViewAnimationOptions.curveLinear

UIViewAnimationOptions.allowUserInteraction
默認情況下,視圖無法與動畫相互影響。 指定此選項將覆蓋默認值。 這可以用于重復動畫,例如脈沖視圖。

UIViewAnimationOptions.repeat
無限期重復動畫; 經常與 UIViewAnimationOptions.autoreverse 選項配對。

UIViewAnimationOptions.autoreverse
向前運行動畫,然后向后,將視圖返回到其初始狀態。

請務必查看 UIView Class Reference 中的 Constants 部分,以查看所有可能的選項。

青銅挑戰:彈簧動畫

iOS內置了一個強大的物理引擎。利用這種動力的一個簡單的方法是使用彈簧動畫。

// UIView

class func animate(withDuration duration: TimeInterval,
??delay: TimeInterval,
??usingSpringWithDamping dampingRatio: CGFloat,
??initialSpringVelocity velocity: CGFloat,
??options: UIViewAnimationOptions,
??animations: () -> Void,
??completion: ((Bool) -> Void)?)

使用這種方法使兩個標簽以彈簧的形式在屏幕上彈入和彈出。 參考 UIView 文檔來了解每個參數。

白銀挑戰:Layout Guides

如果您旋轉為橫向,則 nextQuestionLabel 變得可見。 不要使用硬編碼間距約束的常量,使用 UILayoutGuide 的實例將兩個標簽分開。 該 layout guide 應該具有等于 ViewController 視圖的寬度約束,以確保在不動畫時 nextQuestionLabel 保持在屏幕外。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 在iOS實際開發中常用的動畫無非是以下四種:UIView動畫,核心動畫,幀動畫,自定義轉場動畫。 1.UIView...
    請叫我周小帥閱讀 3,142評論 1 23
  • 前言的前言 唐巧前輩在微信公眾號「iOSDevTips」以及其博客上推送了我的文章后,我的 Github 各項指標...
    VincentHK閱讀 5,404評論 3 44
  • 如果想讓事情變得順利,只有靠自己--夏爾·紀堯姆 上一章介紹了隱式動畫的概念。隱式動畫是在iOS平臺創建動態用戶界...
    夜空下最亮的亮點閱讀 1,976評論 0 1
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,141評論 5 13
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,551評論 6 30