如何使用自訂 Segue 實現視圖轉場動畫

如何使用自訂 Segue 實現視圖轉場動畫
如何使用自訂 Segue 實現視圖轉場動畫
In:

當iOS 第五版(iOS 5)釋出後,它針對使用者介面的設計導入了一個全新且革命性的方式,也就是 storyboard的採用,這也表示了現行的設計邏輯要從頭改變。在iOS 5以前,每一個控制器(controller)幾乎都跟著一個介面建構器(Interface Builder)檔,也就是所謂的nibxib檔,其原理很簡單:每一個視圖控制器假設都會設計一個相對應的 nib 檔,而所有的 nib 檔的組合就是應用程式的全部介面。在某種情況,這種開發方式很方便,因為開發者在設計該功能時只要將重點放在該介面即可,但是另一面卻也會因此產生許多的檔案,甚至開發者無法檢視整個App介面的開發全貌。

Custom Segue in Storyboards

有了 Storyboard 之後,所有的一切都成為歷史,因為這個方法已經幾乎被所有的開發社群所使用。相對於舊的技術,Storyboard提供至少了三種顯著的好處:

  1. 整個介面只有用一個檔案即可。專案內的全部檔案數量急遽減少,尤其對於大型專案來說更是可觀。也可以依照需求另外使用nib檔,單純作為輔助用的視圖。
  2. 開發者可以隨時檢視App的介面與流程。
  3. 在視圖控制器(以介面設計的專有名詞來說,稱作場景)間的轉換(或者稱作過場,transition),以及它們之間所進行的所有事情,現在都能清楚的定義以及清楚地呈現給開發者。

從以上的說明,場景(scene)間的轉換構成了Storyboard特別的一章,也就是所謂的 segue.

Segue 跟應用程式導覽(navigation)的處理很密切,因為實際上它是定義了從一個視圖控制器至另一個的轉換細節。這些細節定義了動畫是否有被使用。或者該使用哪種動畫,當然還有,實際轉換時的預備與成效。但是不只如此,segue 也可以用來傳遞資料至準備要顯示的視圖控制器上,而這幾乎是所有開發者常會碰到的狀況。

從程式觀點來看,一個 segue 是 UIStoryboardSegue類別的物件,在iOS 5第一次導入時就已經有說明。不像其他類別的物件,這樣的物件不能直接建立使用。不過Segue 的屬性可以被指定,因此在應用程式運行中,Segue準備要執行時,它們會被運用到。UIKit框架提供了一些預設的 segue 加上預設的動畫轉換,對於簡單的案例已經很適合了。其中包括了push segue (比如App中導覽控制器的呈現)、 modal segue 加上動畫功能,以及 popover segue。對於一些更進階的需求,iOS SDK 的預設 segue 可能就不夠用了,所以開發者必須要自訂segue(custom segue)

建立一個自訂segue不是困難的事,因為它包含了 iOS 的標準程式技術。實際上所需要的是,作為 UIStoryboardSegue 類別的子類別(subclass),並且只要覆寫(override)一個稱作 perform的方法。動畫邏輯必須要在這個方法中實作。觸發視圖控制器間的轉換,必須由開發者以程式來執行,但這些都是標準程序。

這篇教學的目標是要讓你知道如何實作自訂segue,透過簡單但是能夠切中要點的範例App 來全方面展現這個觀念。了解如何自訂 segue 可以驅動你開發更強大的應用程式。除此之外,這也可以讓你開發出使用者體驗最佳化,還有漂亮吸晴的應用程式。

所以,如果你有興趣學習我剛所敘述的部分,一起來探索接下來有關自訂 segue 的細節與內容。

Demo App 概述

不像之前我之前幾篇為教學內容所提供的起始專案模板,在這裡我們要從頭來建立一個應用程式。我會這樣做的意義是因為我們內容的一些重要部分與介面建構器(Interface Builder)有關,所以我認為逐步詳細從頭來說明會更好。

如同我先前所述,我們準備開發一個十分簡單的程式,我們建立兩個自訂的segue。從頭說明好了,我們的範例 App 會有三種視圖控制器,也就是在介面建構器有三個介面,以及三個相對的類別。預設第一個是由 Xcode 所建立,所以我們只要加上另外兩個。我們要建立的自訂segue,是用來由第一個視圖控制器導覽至第二個(以及返回),並且從第一視圖控制器至第三個(然後再次返回)。我們將不會在第二及第三個視圖控制器間加入任何連結。

如同剛說過的,我們會建立兩個自訂的segue。對每一個,我們會建立兩個相對應的類別(因此,總共是四個):針對第一個,我們會實作所有執行 segue,從第一個視圖控制器至下一個所需的自訂邏輯,。第二個,我們會實作返回第一個視圖控制器的邏輯,或者換句話說,我們會實作 unwind segue。我們待會會見到更多有關 unwind segue 的部分,現在只要記得這是用來讓我們返回到前一個視圖控制器的 segue。

這些視圖控制器本身沒做什麼。上面只有一個標示這個視圖控制器名稱的標籤,另外每一個都有不同的背景顏色,如此一來我們可以容易地觀察轉換(transition,或者稱過場)過程(是的,這是一個色彩繽紛的App)。第一個與第二個視圖控制器會有多一個標籤,用來顯示從其他視圖控制器傳遞過來的自訂訊息。

最後,當以下動作發生之後,Segue 將會執行:

  • 針對第一個視圖控制器轉換至第二個視圖控制器,我們會使用上滑(Swipe up)手勢,返回方向則使用下滑手勢(Swipe down)。我不準備描述要實作的動畫,我會讓你親眼見到。
  • 對於第一個視圖控制器轉換到第三個視圖控制器,我們會使用一個按鈕(UIButton)。如果要返回,我們會使用上滑手勢。同樣的,我不說明任何有關動畫的事情。

當然,我們準備在視圖控制器間傳遞資料。

往下繼續之前,以下是我們準備要示範的範例,它也呈現了我們自訂 segue 準備要運作的內容:

Custom Segue Demo

注意當你建立一個自訂 segue,你可以實作任何你可以想像得到或者需要的動畫特效,你可以嘗試一下待會我們接下來的內容。

建立 App

我們打開 Xcode 來開始。選擇建立一個新專案,在接下來的導引中,選取 Single View Application 作為專案模板。點擊 Next 按鈕。在 Product Name: 欄,設定 CustomSegues 作為專案的名稱。另外要確認選取 Swift 作為程式語言,而 Device 設為 iPhone。

t26_3_template2

再次點擊 Next。在這個步驟,選擇儲存這個專案的空間,並點擊 Create 按鈕。

現在專案已經準備好了,在專案導覽器(Project Navigator)選擇相對的群組。預設會打開 General 標籤,而這正是我們所需要的。在 Device Orientation 區域的Deployment Info區塊,取消 Landscape LeftLandscape Right 選項的勾選。只留下Portrait 選項的選取。另外,倘若你想要在之前的作業系統測試你的App,你也可以任意將部署目標(deployment target)從 8.1 變更至之前的 iOS 版本(但是最好不要回到iOS8之前的版本)。

t26_4_general_tab

現在,我們準備繼續接下來的實作,開始進行第一個介面的設定。

初始介面設定

第一個步驟是加入我們準備在介面中用到的視圖控制器。所以,在專案導覽器點擊 Main.storyboard 檔,介面建構器便會出現。我們準備在這邊將我們的工作區分成三個部分,針對每一個建立一個視圖控制器。但是首先,要確認有將目前的尺寸類別(size class)改為 Compact WidthRegular Height,這樣便會符合iPhone 的介面:

t26_5_size_classes

設置第一個視圖控制器

當你打開介面建構器,你會見到Xcode 加入的預設場景。我們從這邊開始進行。一開始,加入兩個 UILabel 物件至場景中。第一個,設定以下的屬性:

  • Frame: X=16, Y=77, W=368, H=21
  • Font: System Bold
  • Font size: 24
  • Text: View Controller #1
  • Text alignment: Center

另外,設定 TopLeadingTrailingHeight 約束條件(constraint):

t26_6_set_constraints1

第二個標籤,設定屬性如下

  • Frame: X=16, Y=390, W=368, H=21
  • Text: None
  • Text alignment: Center

接下來,設定 Horizontal Center in ContainerVertical Center in ContainerWidthHeight 約束條件如下:

t26_7_set_constraints2_1t26_8_set_constraints2_2
當你完成屬性的設置與兩個標籤的約束條件,加入一個 UIButton 物件到場景中。並做以下的指定:

  • Frame: X=169, Y=712, W=62, H=30
  • Title: Tap Me!
  • Text Color: Black

對於它的約束條件,只要按下介面建構器右下方的Pin按鈕,並在選取Selected Views 區塊下的Add Missing Constraints

完成後,選取視圖並在屬性檢閱器(Attributes Inspector)中打開相對應的下拉選單,並選取 Other… 選項來變更其背景顏色。在顏色挑選器,點擊 Color Palettes* 按鈕,然後選取 Crayons** 色盤。挑選 Cantaloupe 顏色,然後關閉顏色挑選器。

t26_9_color_picker

這是第一個場景的截圖:

t26_10_ib_scene1

設置第二個視圖控制器

是時候來設定第二個視圖控制器了。從元件庫中拖曳一個視圖控制器(view controller)元件,直接拉進畫面中。然後在這裏加入兩個 UILabel 物件,並指定跟上面一樣的屬性。也不要忘記設定約束條件。這裏唯一的差異是第一個標籤的文字,應該改為 "View Controller #2" (不需要引號)。

接下來,再次選取視圖,並打開顏色挑選器。這一次挑選Honeydew 顏色(在列表的第二個),並關閉挑選器。

Xcode 會顯示出一個警告,告訴你兩個視圖控制器間沒有segue。先不要管它,我們待會會修復。

下一張圖列出專案的第二個場景:

t26_11_ib_scene2

設置第三個視圖控制器

最後,我們再從元件庫取出一個視圖控制器並加入畫面中。這裏,只要加上一個 UILabel 元件,而不是兩個。設定你在前兩個場景的第一標籤的屬性,但是有兩個不一樣的地方是:首先,設定標籤的Y原點為 389第二個,設定 "View Controller #3" 值作為標籤的文字。沒有其他子視圖會加進去。所以你只要幫這個視圖挑選背景顏色即可。依照你已經做過兩次的步驟,最後一次打開顏色挑選器,並選取Sky 顏色。

依照其約束條件,設定 Horizontal Center in ContainerVertical Center in ContainerWidthHeight 約束條件,如同你在其他兩個視圖控制器的第二個標籤一樣。Y原點以及這些約束條件的變更會讓你將標籤在視圖中完全置中。

以下是介面的第三個場景的樣貌:

t26_12_ib_scene3

加入新視圖控制器類別

對於我們前面在介面加入的場景,我們必須設定一個不同的類別,所以我們可以實作每一個細節。預設每一個場景是設為 ViewController ,這可以跟我們第一個視圖控制器(自動由Xcode產生的)完美搭配。不過對於其他兩個,我們必須要建立兩個新類別,所以我們繼續往下。

第一個步驟是建立一個新類別檔,所以至 File > New > File… 選單來開始這個程序。接下還會出現建立新檔案的導引,在第一步,確認你有在iOS區塊的Source 分類選取Cocoa Touch Class

點擊 Next 選單。在導引的第二個步驟,在Subclass of: 欄位選取或輸入UIViewController 值。然後,指定SecondViewController 作為你所建立檔案的名稱。當然,你不能變更程式語言,所以確認最後一個欄位,Swift 有被選取。

再一次點擊 Next,然後是點擊Create來建立新檔案。完成之後,你會在專案導覽器見到一個稱作SecondViewController.swift 的新檔案。

再次重複以上的步驟,並幫專案的第三個視圖控制器加入類別檔。並將這個類別檔案設為ThirdViewController,其他的步驟保持一樣。

在你加入第二個檔案之後,你會在專案導覽器見到它們,如下所示:

t26_15_project_navigator_new_classes

現在,在我們回到介面建構器並設定相對應的類別給場景之前,我們來宣告一對待會會用到的IBoutlet屬性。至 ViewController.swift 檔,加入以下的屬性:

@IBOutlet weak var lblMessage: UILabel!

現在至SecondViewController.swift 檔,並加入如上面同一行程式。這兩個屬性會與前兩個控制器的第二個標籤做連結,這樣我們之後可以在上面顯示訊息。

再次打開Main.storyboard檔,並選取第二個場景(綠色背景)。在上面,點擊視圖控制器物件,然後至工具面板的識別檢閱器(Identity Inspector)處,在Custom Class 區塊的 Class 欄,設定為我這部分加入的第一個類別至專案中,也就是 SecondViewController:

t26_16_second_scene_class

針對第三個場景(有淡藍色背景)重做這個步驟。選取之後,點擊場景上方的視圖控制器,然後設定為ThirdViewController 作為其類別:

t26_17_third_scene_class

很棒,現在每一個場景可以符合不同的類別。在我們完成這部分之前,我們先連結兩個我們宣告的IBoutlet屬性,我們從第一個視圖控制器開始(橘色這個)。點擊畫面上方的視圖控制器物件,然後按下Ctrl鍵不放,拖曳至場景中心的標籤:

t26_18_connect_iboutlet

在跳出的小視窗,選取lblMessage屬性,連結便會成功完成。

t26_19_select_iboutlet

接著在Second View Controller 場景設定相同的步驟,並連結視圖控制器的 lblMessage 屬性至場景中間的標籤。

加入自訂 Segue 類別

自訂segue的建立包含了UIStoryboardSegue 的子類別,以及一個強制實作的方法,稱作perform。在這個方法中,在兩個視圖控制器間執行轉換的自訂邏輯會實際應用到,所以在我們撰寫任何segue相關的程式前,讓我們加入所有缺少的類別。

在專案加入一個新類別的方式,已經在前面的部分有做了細項說明,所以用它作為你要在這邊加入所需檔案的參考。在我給你每一個檔案的細節前,我先說明我們總共會建立四個檔案。那是因為每一個自訂 segue 都需要兩個類別,我們準備建立這兩個。重點是,第一個類別是用來實作實際執行segue時其所有的邏輯以及轉換。第二個類別是用來實作當第二個視圖轉換回第一個視圖控制器的相反動作。這也是所謂的返回(unwind)segue,我們待會會見到更多的細節。

有了這些概念後,開始加入新類別檔案至專案中。加入每一個檔案時需確保兩件事:要選取Cocoa Touch Class 模板,在導引過程第二步驟並設定UIStoryboardSegue 做為Subclass of: 欄位的值。

所有四個檔案的名稱如下:

  1. FirstCustomSegue
  2. FirstCustomSegueUnwind
  3. SecondCustomSegue
  4. SecondCustomSegueUnwind

加入專案後,以上這些都在專案導覽器內可以找到。

建立第一個自訂 Segue

我們開始進行第一個客製化segue,在撰寫任何一行程式之前,為了要應用自訂轉換,需要在介面建構器的兩個場景間加上segue。在這裏,我們準備在View Controller 場景(橘色背景)與 Second View Controller 場景( 綠色背景)間建立segue。

同樣地,打開Main.storyboard 檔。為了方便建立segue,如果文件大綱(Document Outline)隱藏起來,展開它,並在View Controller Scene區塊選取View Controller 物件:

t26_21_document_outline

然後,按住Ctrl鍵,拖曳至Second View Controller Scene區塊下的Second View Controller

t26_22_create_segue_connect

一個黑色視窗跳出,列著各種有關新 segue 的選項。在這裡面有一個稱作 custom的名稱:

t26_23_create_segue_select_custom

點擊它之後,segue 便會馬上建立。你可以檢查畫面中的兩個場景是否有連上一條線,確認 segue 是否有建立。

t26_24_segue_line

在我們開始寫程式之前,還需要確定幾個設定。首先,在畫面中點擊 segue 線,並在 Utilities 面板打開屬性檢閱器。其中一件最重要的任務是設定 segue 的識別碼(identifier),我們會在程式中參照它。重要的是對於不同的segue不要指定相同的識別碼。在這裏,設定idFirstSegue 作為 我們 segue 的識別碼。另外在 Segue Class 欄位,我們必須指定用來作為自動轉換的自訂類別名稱。在這裏,設定FirstCustomSegue 作為自訂 segue 的類別。執行期間,這個 App 將會執行這個類別的程式, 讓 segue 可以順利運作。

t26_25_set_segue_identifier

自訂 segue 的實作

現在新自訂的 segue 已經出現,且做了指定,我們來實作當第一個控制器導覽至第二個時的轉換。簡單地說,通常跟自訂 segue 有關的程式是去「玩」兩個視圖控制器的視圖。首先,指定目標視圖控制器視圖(destination view controller)的初始狀態,而它是手動加入App的視窗。然後當來源視圖控制器((source view controller))的視圖以離開畫面後,以動畫將目標視圖放置於所想位置。

註:一個segue物件參照準備要呈現的視圖控制器,就是 目標視圖控制器,而目前的視圖控制器就是所謂的來源控制器

最後,當動畫轉換結束後,新視圖控制器便直接呈現給使用者看,沒有進一步的動畫。

在這篇教學中整個App所要達成的特效是,不管segue是如何執行,我們想要第一個視圖控制器的視圖向上滑,而第二個視圖控制器的視圖跟著移動。兩者視圖間沒有空間,第二個將會「貼」至第一個視圖。

我們來開始寫程式,逐步來了解,最後的地方,我會提供整個方法的實作給你參考。

打開FirstCustomSegue.swift 檔,然後定義以下的函數:

override func perform() {

}

因為這個函數已經定義在UIStoryboardSegue 父類別(superclass),在這裏我只是覆寫它,所以我們可以加入我們想要的自訂邏輯,因此,第一步是指定來源與目標視圖控制器的視圖給兩個區域變數,以利後面的工作。

override func perform() {
    // 指定來源與目標視圖給區域變數
    var firstVCView = self.sourceViewController.view as UIView!
    var secondVCView = self.destinationViewController.view as UIView!

}

另外,我們需要畫面的寬度與高度值,所以讓我們以兩個變數來存放:

override func perform() {
    ...

    // 取得畫面寬度與高度
    let screenWidth = UIScreen.mainScreen().bounds.size.width
    let screenHeight = UIScreen.mainScreen().bounds.size.height

}

好的,我們來指定視圖要出現的初始位置。想像我們想要將視圖從底部移到頂部,我們會將視圖移至畫面的可視範圍外,置於目前視圖的下方。這很容易辦到,只要設定視圖的框(frame)即可:

override func perform() {
    ...

    // 指定目標視圖的初始位置
    secondVCView.frame = CGRectMake(0.0, screenHeight, screenWidth, screenHeight)

}

這裏,第二個視圖控制器還不是App window 的子視圖。所以在我們實作實際的動畫前,很清楚地我們必須將其加入 window 中。這可以使用 App 的 window 物件, insertSubview(view:aboveSubview:) 方法來辦到。如同以下,首先我們存取 window 物件,然後我們加入目標視圖:

override func perform() {
    ...

    // 取得App的 key window 並插入目標視圖至目前視圖(來源視圖)上
    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(secondVCView, aboveSubview: firstVCView)

}

對於above 這個字不用感到困惑。有關視窗子視圖的順序,它們全都是以堆疊方式來置放,畢竟這樣才不會產生麻煩。

最後,我們來產生轉換動畫。首先,我們會將第一個視圖控制器的視圖從最上面移出畫面,同時我們會將第二個視圖移至第一個原來的位置。實際上我們說「移」,意思是指修改兩個視圖的框架(frame):

override func perform() {
    ...

    // 轉換動畫
    UIView.animateWithDuration(0.4, animations: { () -> Void in        
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)
        secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, -screenHeight)

        }) { (Finished) -> Void in

    }

}

以上這兩行會執行所需的效果。不過我們還沒完成,因為我們還沒有呈現新的視圖控制器(目標視圖控制器)。我們會在第二個閉包(closure)實作它,如下所示:

override func perform() {
    ...

    // 轉換動畫
    UIView.animateWithDuration(0.4, animations: { () -> Void in
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)
        secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, -screenHeight)

        }) { (Finished) -> Void in
            self.sourceViewController.presentViewController(self.destinationViewController as UIViewController,
                animated: false,
                completion: nil)
    }

}

就這樣,整個上面函數的全貌如下:

override func perform() {
    // 指定來源與目標視圖給區域變數
    var firstVCView = self.sourceViewController.view as UIView!
    var secondVCView = self.destinationViewController.view as UIView!

    // 取得畫面寬度及高度
    let screenWidth = UIScreen.mainScreen().bounds.size.width
    let screenHeight = UIScreen.mainScreen().bounds.size.height

    // 指定目標視圖的初始位置
    secondVCView.frame = CGRectMake(0.0, screenHeight, screenWidth, screenHeight)

    // 存取App的 key window 並插入目標視圖至目前視圖(來源視圖)上
    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(secondVCView, aboveSubview: firstVCView)

    // 轉換動畫
    UIView.animateWithDuration(0.4, animations: { () -> Void in
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)
        secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, -screenHeight)

        }) { (Finished) -> Void in
            self.sourceViewController.presentViewController(self.destinationViewController as UIViewController,
                animated: false,
                completion: nil)
    }

}

在你第一次測試 App 前,我們也必須讓自訂 segue 能夠執行。這會在 ViewController 視圖控制器的視圖上執行向上滑動手勢時發生。因此,繼續打開 ViewController.swift 檔。至viewDidLoad 方法來建立一個新手勢物件,並加入至視圖中:

override func viewDidLoad() {
    ...

    var swipeGestureRecognizer: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showSecondViewController")
    swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Up
    self.view.addGestureRecognizer(swipeGestureRecognizer)
}

當手勢執行時,showSecondViewController 函數,準備要被呼叫,但是還沒實作。因此讓我們它的定義,只有簡單一行:我們將執行自訂segue :

func showSecondViewController() {
    self.performSegueWithIdentifier("idFirstSegue", sender: self)
}

你可以注意到,我們使用它的識別碼值存取segue。以上方法的呼叫讓我們在視圖上向上滑動時,FirstCustomSegue 類別的實作可以被執行。

如果你想要的話,你可以馬上測試這個App。你可以在模擬器或真實的裝置執行,你會見到即使向上滑動時會發生轉換,但是你還是不能導覽回去第一個視圖控制器。這很正常,因為我們馬上要實作這個函數。

Unwind Segue

建立自訂 segue 時,必須要實作兩個方向的導覽:從來源至目標視圖控制器,以及返回。幾乎不可能只有建立單一方向的導覽。現在我們實作了segue,顯示了我們想要呈現第二個視圖控制器的動畫,我們必須要進行能夠反向的動作。執行返回導覽的segue,稱作unwind segue

為了簡化,一個 unwind segue 是… 就跟標準的一樣,但是設置上會有點複雜。需要執行幾個步驟,不過所有都是由標準程式技術所組成。注意兩件事:

  1. 一個unwind segue通常是一對標準的自訂segue。
  2. 要讓一個 unwind segue能運作,我們必須要再一次覆寫perform 法,並且應用任意我們想要或App所需的自訂邏輯與動畫。不過,當導覽返回來源視圖控制器,不需要跟標準segue的轉換特效相同。

unwind segue的實作是我們步驟中的一部分,主要是:

  1. IBAction 方法的建立,可以在 unwind segue 執行時依照需求執行一些程式。這個方法的名稱可以任取,且它不強制去包含任何事。有需要再做定義,即使是空白,也能夠定義 unwind segue。
  2. unwind segue的建立以及其設置,跟我們先前在介面建構器所定義的並不完全相同,你待會就可以見到它是如何完成的。
  3. UIStoryboardSegue類別的一個子類別中,透過perform方法的覆寫來實作自訂邏輯。
  4. 透過在UIViewController 類別所提供特定方法中的定義,讓系統知道unwind segue準備要執行什麼。

此時你一定感到一頭霧水,但是慢慢地你會瞭解一切的意思。

我們來定義上面第一個步驟的 IBAction 方法。這個動作方法,一定都是在我們想要導覽的初始視圖控制器中實作。如我所說,實作內容是可以空白的,但是你必須要謹記在心,你至少要定義一個方法,否則便不能繼續。在App中是不需要有許多個這樣像 unwind segue 的動作方法。大部份只要定義一個像這樣的動作方法,並檢查哪一個 unwind segue 要呼叫,這樣就夠了。

在動作中,打開 ViewController.swift 檔。在其中加入以下的程式:

@IBAction func returnFromSegueActions(sender: UIStoryboardSegue){

}

我們待會會加入一些範例程式。注意這個方法的參數是 UIStoryboardSegue 物件,這個細節很重要,需要記住。

現在至介面建構器,打開 Main.storyboard 檔。是時候建立unwind segue了,我們會由檢查Second View Controller場景內的物件來開始,不管是在場景本身中檢查或者在文件大綱中檢查都可以。倘若你看得仔細點,你會發現有一個物件稱作Exit

t26_26_exit_1

t26_27_exit_2

如果你從未建立過自訂segue,你可能沒用過它,也許想知道這是什麼,好的,這就是你要建立的unwind segue。

點擊場景中的Second View Controller 物件,然後按住Ctrl拖曳至Exit物件:

t26_28_unwind_exit

接著會出現如下的跳出選單,上面有我們前面定義的IBAction方法:

t26_29_unwind_popup

確認有選取它。unwind segue 便會建立,你就可以在文件大綱中檢查,沒有建立新的線,但是Segue已經建好了:

t26_30_unwind_segue_listed

現在點擊剛建立的unwind segue,並打開屬性檢閱器。在上面設定 ** idFirstSegueUnwind** 作為segue的識別碼:

t26_31_set_unwind_segue_identifier

unwind segue 建立過程的的第二部分已經結束。這表示是時候寫些程式了,讓unwind segue的能正常執行。

一開始,打開FirstCustomSegueUnwind.swift 檔。再一次,我們準備覆寫perform 方法,並使用區域變數來作為視圖的參照。另外,我們也使用另一個變數來存放畫面的高度。

override func perform() {
    // 指定來源與目標視圖給區域變數
    var secondVCView = self.sourceViewController.view as UIView!
    var firstVCView = self.destinationViewController.view as UIView!

    let screenHeight = UIScreen.mainScreen().bounds.size.height
}

注意,此時的來源視圖控制器是 Second View Controller,而目標則是 View Controller.繼續往下,這裏我們不指定任何視圖的初始狀態,我們只會加入第一視圖控制器(要返回那個)的視圖至window的子視圖:

override func perform() {
    ...

    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(firstVCView, aboveSubview: secondVCView)

}

最後,我們必須要對兩個視圖的移動做動畫,這次它們同時朝底部移動:

override func perform() {
    ...

    // Animate the transition.
    UIView.animateWithDuration(0.4, animations: { () -> Void in        
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
        secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, screenHeight)

        }) { (Finished) -> Void in

            self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
    }


}

透過兩個視圖y點(在Y軸變更偏移量),我們在預設的動畫時間內將他們放置於新位置。當轉換結束之後,我們只是將第二個視圖控制器解除,沒有加上任何其他動畫,這樣就完成了。現在整個方法的實作全貌如下所示:

override func perform() {
    // Assign the source and destination views to local variables.
    var secondVCView = self.sourceViewController.view as UIView!
    var firstVCView = self.destinationViewController.view as UIView!

    let screenHeight = UIScreen.mainScreen().bounds.size.height

    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(firstVCView, aboveSubview: secondVCView)

    // 轉換動畫
    UIView.animateWithDuration(0.4, animations: { () -> Void in
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
        secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, screenHeight)

        }) { (Finished) -> Void in

            self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
    }
}

至目前為止一切都蠻順利的。我們建立了一開始所需的IBAction方法,以及unwind segue,並實作了動畫轉換的自訂邏輯。最後有一個方法也需要實作,在這個方法我們會使用FirstCustomSegueUnwind 類別。

打開 ViewController.swift 檔,定義以下的方法:

override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue {

}

這是UIViewController 類別所提供的方法。它在undwind Segue執行時會自動地被呼叫,而它的實作是強制性的。它有三個參數:

  1. fromViewController:這是目前所顯示且要解除的視圖控制器。
  2. toViewController:這是目標視圖控制器,換句話說視我們想要顯示的控制器。
  3. identifier:所要執行segue的識別碼。

注意這個方法回傳了 segue物件。我們所要做的非常簡單:我們會使用識別碼來判斷要執行的segue,然後我們會初始化一個unwind segue 自訂類別的物件並回傳。另外,我們也會呼叫 super 方法,如同以下程式所示,我們來覆寫它:

override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue {
    if let id = identifier{
        if id == "idFirstSegueUnwind" {
            let unwindSegue = FirstCustomSegueUnwind(identifier: id, source: fromViewController, destination: toViewController, performHandler: { () -> Void in

            })
            return unwindSegue
        }        
    }

    return super.segueForUnwindingToViewController(toViewController, fromViewController: fromViewController, identifier: identifier)
}

首先,我們確認識別碼參數確定有一個值,所以我們使用if let陳述。然後使用idFirstSegueUnwind 作為我們要採用segue的確認值,而且在if 陳述中我們初始化FirstCustomSegueUnwind 類別的物件。這個物件會在 if 陳述中回傳,在陳述外面我們在呼叫完super 類別後,我們回傳了相同方法的值。

unwind segue現在已經準備好了。剩下的是實作手勢觸發時的內容。打開SecondViewController.swift 檔,直接至viewDidLoad方法,在裡面加上下面幾行:

override func viewDidLoad() {
    ...

    var swipeGestureRecognizer: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showFirstViewController")
    swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Down
    self.view.addGestureRecognizer(swipeGestureRecognizer)

}

我們定義在上面的手勢是向下滑,跟我們之前的正好相反。現在,我們定義 showFirstViewController 動作方法:

func showFirstViewController() {
    self.performSegueWithIdentifier("idFirstSegueUnwind", sender: self)
}

現在一切都準備好了,繼續試著再次執行App。這次,你可以在兩個視圖控制器間導覽了。

動作執行

在前面部分,我們宣告一個稱作returnFromSegueActions(sender:)的空方法,而我們說過這個方法是必須實作才能建立unwind segue。另外我也告訴你我們準備加入一些程式,現在正是要加入的時候。

因此,在這個動作方法,我們準備執行一個很簡單的任務:我們會變更第一個視圖控制器的背景顏色,然後使用一個UIView 動畫來回復為正常。即使在一個真正的應用程式中這樣做是沒有什麼意義的,但這是對於我們要示範一個unwind segue執行時,動作是如何被執行的一個很棒的範例。

一開始,至ViewController.swift 檔,然後至IBAction方阿,在其中,加入以下的內容:

@IBAction func returnFromSegueActions(sender: UIStoryboardSegue){
    if sender.identifier == "idFirstSegueUnwind" {
        let originalColor = self.view.backgroundColor
        self.view.backgroundColor = UIColor.redColor()

        UIView.animateWithDuration(1.0, animations: { () -> Void in
            self.view.backgroundColor = originalColor
        })
    }
}

首先,我們先檢查這個unwind segue是不是我們所感興趣那個。然後我們儲存背景顏色,並且將顏色變更為紅色。最後,使用動畫 block,我們設定回原來的值。

以上清楚的顯示,在自訂segue被執行時,此時你要執行一個動作,會如何產生運作。稍後,我們會在以上方法多加入一些程式。

資料傳遞

實作自訂segue(以及unwind segue)時,你必須要注意兩件事:首先,對於要產生的轉換,能讓你的App提供很棒的使用者體驗。第二,對於兩個視圖控制器間要交換的資料,不是執行標準的Segue就是unwind segue。

在前面部分,我們已經介紹過第一個狀況,我們對第一個與第二個視圖控制器間的切換指定了很棒的轉換動畫。現在,我們來看如何傳遞資料。以下你會見到的這個程序,很可能你已經使用過,儘管它以UIKit 所提供的預設Segue。

在我們開發的範例應用程式,我們並不會從一個視圖控制器傳遞至另一個。現在我們只是互相傳送一個字串值,並顯示在我們之前在View ControllerSecond View Controller場景加入的第二個標籤中。

一開始,打開 SecondViewController.swift 檔,在IBOutlet屬性後加入以下的屬性宣告:

var message: NSString!

接下來,我們會使用這個屬性在Segue執行前從 ViewController 類別來傳遞一個字串值至這個控制器。

現在打開 ViewController.swift 檔。在類別主體,加入以下的函數實作內容:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "idFirstSegue" {
        let secondViewController = segue.destinationViewController as SecondViewController
        secondViewController.message = "Hello from the 1st View Controller"
    }
}

以上的函數是 UIViewController 類別。依照所要執行Segue的識別碼,我們存取目標視圖控制器,也就是 SecondViewController。然後我們設定以上的字串訊息為SecondViewController類別的message 屬性。

以上是你要使用segue傳遞任何資料至另一個視圖控制器所需要的工作。其作法非常容易理解。

再一次,打開SecondViewController.swift 檔。是時候去處理 message 屬性,並且顯示以上傳遞後的字串。在 viewDidLoad 方法,只要加入以下這行:

override func viewDidLoad() {
    ...

    lblMessage.text = message
}

最後,我們從第二個視圖控制器傳遞一個字串值至第一個。我們再次實作以上的方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "idFirstSegueUnwind" {
        let firstViewController = segue.destinationViewController as ViewController
        firstViewController.lblMessage.text = "You just came back from the 2nd VC"
    }
}

以上的實作,注意我們直接存取 ViewController 類別的訊息標籤。這是另一個在視圖控制器的子視圖顯示資料的方式。注意在這兩種情況,必須要透過segue物件來存取目標視圖控制器。

這是你要在兩個視圖控制器間傳遞資料的全部內容。在第二個等會我們要自訂的segue,你會見到最後一個方式,但是這邊呈現的是大部份都會用到的。

如果可以的話,你可以執行App。這次,在兩個視圖控制器的第二個標籤,所傳遞的訊息是一個字串值。注意我們在SecondViewController 類別的工作已經結束。現在我們要將重點放在專案中的第三個視圖控制器。

建立第二個自訂Segue

所有我們在前面所討論過的部分,是你建立與處理自訂segue所需的觀念。不過我想多建一個,讓你能夠消化所學的內容,也可以看見一些不同的狀況,我們開始吧。

第一個步驟,在介面建構器建立自訂segue,所以在專案導覽器點擊 Main.storyboard 來打開它。

介面在你的畫面上出現後,打開文件大綱面板(如果它有縮起來的話),然後在View Controller Scene 點擊View Controller 物件,並按住 Ctrl 鍵以及滑鼠按鈕,拖曳至Third View Controller SceneThird View Controller 物件。藍色線便會出現:

t26_32_second_segue_connect

在跳出的視窗,選取 custom segue。完成後,確認你的畫面上是否有建立了新segue:

t26_33_second_segue_line

點擊以上的線,並在工具面板(Utilities pane)打開屬性檢閱器。在這裏,需要指定兩件事:segue 識別碼,以及類別。對於識別碼,在Identifier 欄位設定為idSecondSegue 然後,在Segue Class 欄位,輸入SecondCustomSegue 值,所以App可以分辨在執行一個新的自訂segue時要使用哪個類別。

t26_34_second_segue_settings

第二個自訂segue已經建立,所以我們來實作我們需要的動畫轉換。

實作第二個自訂Segue

如同我所介紹,在成為UIStoryboardSegue類別子類別,並覆寫perform 方法,你可以任意實作你想要的各種轉換。不管是簡單、複雜的動畫轉換或者不加都可以(但是如果你建立一個自訂Segue,卻不加入任何動畫特效是哪招?)。

針對要轉換至第三個視圖控制器的segue,我們將指定另外一種動畫。這次,我們會實作一個縮小(zoom out)與放大(zoom in)的特效。明確地說,View Controller 視圖控制器的視圖會以畫框縮小來遠離,如此便可以造成縮小特效。另一方面,第三視圖控制器的的框架,一開始會縮小,直到動畫執行後,他會變回原來的狀態。這便會產生放大特效。在這之後的內容,你會見到App 的運作,便能瞭解其意。

我們開始撰寫程式。打開SecondCustomSegue.swift檔,如同我們已經建立過兩次一樣,我們透過區域變數的指定,將視圖控制器的視圖參照它。當然,我們的實作會在perform方法中進行:

override func perform() {
    var firstVCView = sourceViewController.view as UIView!
    var thirdVCView = destinationViewController.view as UIView!

}

注意你可以跳過這個步驟並直接使用來源與目標視圖控制器的屬性來存取視圖,不過雖然需要多兩行程式,我想這樣的方式更能清楚表示。

現在,我們加入第三個視圖控制器的視圖至window中。這是你必須做的,否則便不會有第二個視圖可轉換。

override func perform() {
    ...

    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(thirdVCView, belowSubview: firstVCView)

}

現在,我們可以指定第三視圖控制器其視圖的初始狀態。我們不要碰觸它的框架,我們將會修改它的transform屬性如下所示:

override func perform() {
    ...

    thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)

}

如你所見,我們將寬度與高度縮減,我們不把值設為零,因為這樣沒有效果。一個很小的值便符合我們的需求。

下一步是執行動畫。在這裏,我們將會應用兩個連續的動畫,一開始,我們會先縮小原始視圖,然後我們會放大目標視圖。

override func perform() {
    ...

    UIView.animateWithDuration(0.5, animations: { () -> Void in        
        firstVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)        

        }) { (Finished) -> Void in

            UIView.animateWithDuration(0.5, animations: { () -> Void in
                thirdVCView.transform = CGAffineTransformIdentity

                }, completion: { (Finished) -> Void in

                    firstVCView.transform = CGAffineTransformIdentity
                                        self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
            })
    }
}

第一個動畫開始後,第一個視圖會變得很小,這會看起來像是鏡頭拉遠特效。在完成處理器(completion handler),我們開始一個新動畫,將視圖回復目標視圖控制器至正常狀態。最後,一旦動畫也結束,我們將第一個視圖控制器至它的正常狀態,且在沒有進一步動畫情況下呈現第三視圖控制器。

全部的方法的內容如下所示:

override func perform() {
    var firstVCView = sourceViewController.view as UIView!
    var thirdVCView = destinationViewController.view as UIView!

    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(thirdVCView, belowSubview: firstVCView)

    thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)

    UIView.animateWithDuration(0.5, animations: { () -> Void in

        firstVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)

        }) { (Finished) -> Void in

            UIView.animateWithDuration(0.5, animations: { () -> Void in
                thirdVCView.transform = CGAffineTransformIdentity

                }, completion: { (Finished) -> Void in

                    firstVCView.transform = CGAffineTransformIdentity
                                        self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
            })
    }
}

自訂轉換準備好了,我們還沒結束,因為我們必須執行segue。倘若你記得,在View Controller的場景,有一個按鈕,標題是「Tap me」,是我們要用來起用segue的。

首先,打開 ViewController.swift 檔,並加入以下的IBAction 方法:

@IBAction func showThirdViewController(sender: AnyObject) {
    self.performSegueWithIdentifier("idSecondSegue", sender: self)
}

如同所期望的,我們沒有做什麼事情,只是執行segue 而已。注意segue的識別碼必須要匹配介面建構器的設定。

最後,我們必須將按鈕連結以上的動作方法。打開Main.storyboard.swift 檔,並至文件大綱的View Controller Scene。點擊按鈕物件,然後按住 Ctrl 鍵與滑鼠按鈕,並拖曳至View Controller物件:

t26_35_ibaction_method_connect

一個有許多選項的跳出選單會出現。其中有一個區塊稱作Sent Events,在下方也列出IBAction 方法名稱。選取它,連結便會成功完成。

t26_36_ibaction_method_select

倘若你想要繼續測試App,點擊初始視圖控制器的按鈕,並看一下第三視圖控制器是怎麼呈現。

The Second Unwind Segue

在我們範例程式中實作的第一個 unwind Segue,我說首先第一件事你必須要做的是定義一個當 segue 被執行的可以呼叫的 IBAction 方法。在那裏我們實作一個稱作returnFromSegueActions(sender:)的方法名稱,當它設置好後,我們也會在新的unwind segue使用它。

以上表示,我們可以進到下一步,在介面建構器建立 unwind segue。所以,打開Main.storyboard檔,並至Third View Controller Scene。選取Third View Controller 物件,並按住Ctrl鍵,拖曳至 Exit 物件。

t26_37_unwind_segue_connect2

在跳出的視窗中,點擊動作方法的名稱,如此unwind segue便會建立。

t26_38_unwind_segue_2_created

接下來,選取segue,並打開屬性檢閱器,在identifier 欄位,設定idSecondSegueUnwind值。

現在unwind segue已經準備好了,我們幫想要執行的轉換行為寫程式了。這次,我們並不只執行反向動畫至標準segue。我們準備建立稍微複雜一點的動畫,也就是第三視圖控制器會縮小,同時朝向畫面上方移動,而第一視圖控制器的的視圖將會放大,並從畫面底部往上移動。所以,它佔據了可用區域。這個視圖一開始會離開畫面的可視區域。

打開SecondCustomSegueUnwind.swift 檔,我們將會最後一次在perform 方法中實作。我們先從簡單點來:

override func perform() {
    var firstVCView = destinationViewController.view as UIView!
    var thirdVCView = sourceViewController.view as UIView!

    let screenHeight = UIScreen.mainScreen().bounds.size.height

}

同樣的,我們將兩個視圖控制器視圖存放至兩個區域變數,我們也進一步儲存畫面高度至一個變數。接下來,我們指定 firstVCView視圖的初始狀態。如同我所說的,它將會離開畫面,然後縮小:

override func perform() {
    ...

    firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
    firstVCView.transform = CGAffineTransformScale(firstVCView.transform, 0.001, 0.001)


    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(firstVCView, aboveSubview: thirdVCView)

}

除了初始狀態的設定外,我們也會加入該視圖至App的視窗。

最後,我們繼續執行動畫。記得離開的視圖會縮小,而出現的視圖會放大,兩個視圖都是朝上移動。

override func perform() {
    ...

    UIView.animateWithDuration(0.5, animations: { () -> Void in

        thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)
        thirdVCView.frame = CGRectOffset(thirdVCView.frame, 0.0, -screenHeight)

        firstVCView.transform = CGAffineTransformIdentity
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)

        }) { (Finished) -> Void in

            self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
    }
}

這是整個自訂unwind segue的全部,以下為其程式的全貌:

override func perform() {
    var firstVCView = destinationViewController.view as UIView!
    var thirdVCView = sourceViewController.view as UIView!

    let screenHeight = UIScreen.mainScreen().bounds.size.height

    firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
    firstVCView.transform = CGAffineTransformScale(firstVCView.transform, 0.001, 0.001)

    let window = UIApplication.sharedApplication().keyWindow
    window?.insertSubview(firstVCView, aboveSubview: thirdVCView)

    UIView.animateWithDuration(0.5, animations: { () -> Void in

        thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001)
        thirdVCView.frame = CGRectOffset(thirdVCView.frame, 0.0, -screenHeight)

        firstVCView.transform = CGAffineTransformIdentity
        firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)

        }) { (Finished) -> Void in

            self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
    }
}

再次回到ViewController.swift 檔,我們參訪一下 segueForUnwindingToViewController(toViewController:fromViewController:identifier:) 方法。我們已經討論過它,現在我們必須檢查我們的新unwind segue,所以我初始化並回傳一個我們子類別的segue 物件。依照我們之前所做的,我們準備再建立一個檢查segue的識別碼是否匹配第二個unwind segue的識別碼的判斷,然後才能繼續。加入以下的程式來修改方法:

override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue {
    if let id = identifier{
        ...
        else if id == "idSecondSegueUnwind" {
            let unwindSegue = SecondCustomSegueUnwind(identifier: id,
                source: fromViewController,
                destination: toViewController,
                performHandler: { () -> Void in

            })

            return unwindSegue
        }        
    }

    ...
}

這裏沒有什麼特別或困難的部分。現在新的 unwind segue 可以完美執行了。不過,我們必須加上個什麼東西來啟動,我們準備在第三視圖控制器加上一個上滑手勢來處理它。所以,至ThirdViewController.swift 檔,並在 viewDidLoad 方法加入以下幾行程式:

override func viewDidLoad() {
    ...

    var swipeGestureRecognizer: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showFirstViewController")
    swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Up
    self.view.addGestureRecognizer(swipeGestureRecognizer)
}

剩下的部分就是實作showFirstViewController 動作方法,跟其他類似且簡單:

func showFirstViewController() {
    self.performSegueWithIdentifier("idSecondSegueUnwind", sender: self)
}

現在,在我們執行App前,我們何不在第三視圖控制器解除時,在 ViewController 視圖控制器內顯示一個訊息?

這次我們不用之前所見將訊息作為資料傳遞,我們將會在returnFromSegueActions(sender:) 方法內進行。我們已經加入一些範例程式在裡面,我們說這個方法是用來執行各種動作以及一個 unwind segue。

打開 ViewController.swift 檔,於上述方內,加入以下的else 狀況:

@IBAction func returnFromSegueActions(sender: UIStoryboardSegue){
    ...

    else{
        self.lblMessage.text = "Welcome back!"
    }
}

加上以上的內容後,每次當第二segue執行時會在第一視圖控制器顯示。

執行App

此時,我們的範例程式已經結束。你可以進行測試,當然,你可以自行變更任何部分,這樣會讓你更熟悉自訂segue。以下是App執行時動畫執行的狀況。

Custom Segue Demo

總結

倘若你往回快速回顧一下我們這篇文章的內容,你絕對會發現自訂segue的處理不是太困難。應用自訂動畫轉換,在不同視圖控制器間作導覽,可以得到很棒的使用者體驗,讓你的App與眾不同。倘若自訂segue對你來說還很陌生,趕快使用它們,我強烈鼓勵你這麼做。這個我們所實作的範例,指引你正確執行自訂segue與unwind segue的方式。我們所建立的動畫與應用不是太複雜,但是對你了解這些所有的意義已經足夠。發揮你的想像力,並建立令人驚艷的特效。這讓你自行發揮吧,我希望你喜歡它並發現這篇文章對你有幫助,同樣的,我們期待你的看法。

為了讓你參考,你可以在這裡下載Xcode專案

譯者簡介:王豪勳 -渥合數位服務創辦人,畢業於台灣大學應用力學研究所,曾在半導體產業服務多年,近年來專注於協助客戶進行App軟體以及網站開發,平常致力於研究各式最軟硬體技術,擁有多本譯作。
原文
A Beginner’s Guide to Animated Custom Segues in iOS 8
作者
Gabriel Theodoropoulos
資深軟體開發員,從事相關工作超過二十年,專門在不同的平台和各種程式語言去解決軟體開發問題。自2010年中,Gabriel專注在iOS程式的開發,利用教程與世界上每個角落的人分享知識。可以在Google+或推特關注 Gabriel。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。