新手指南:使用 Social Framework 以及 UIActivityViewController
來自: http://swift.gg/2016/02/04/social-framework-introduction/
在你即將完成一個大型應用時,或者老板、用戶找到你時,你才意識到這個應用還缺少一個重要功能:將內容發布到 非死book 或 推ter。面臨死線,深呼一口氣,你花費了無數的小時坐在電腦前去集成 非死book 或 推ter 的 SDK,但是最后發現要實現這個功能實在是太難了,或者幾乎是不可能實現。這時,你該怎么辦呢?可能是時候考慮找一些借口告訴老板或者客戶并不準備遞交這個 App 了。著急的滿頭大汗或者不知所措?或者,是否有一種能夠簡單快捷的方法能讓你的應用在短時間內集成分享功能呢?
親愛的讀者,上面我說的這種糟糕的情況希望你們沒有遇到過。不過確實存在一種完美快捷的方法來解決這個問題。事實上,這種解決方法被稱為 Social framework,它被內置于 iOS 的 SDK 中。也許你們好多人已經使用過這個框架;但是,我猜現在依然有一大部分的開發人員還沒有注意到這個框架,更不知道使用這個框架能夠在幾分鐘內就可以把分享功能集成到你的 App 中。
正如我們所了解的,Apple 很久之前就提供了這個內置的分享到 非死book 和 推ter 的方法。很顯然,集成基本分享功能非常簡單,但是想要集成一些高級功能則需要使用其他合適的 SDK。在本教程中,我們關注的是使用最基本的方法,更確切的說,我們關注的是如何在我們的應用中使用默認的內置發送功能去進行分享。下面的 demo 里,我們僅僅使用了 Social framework 默認的發送控制器,剩下的 iOS 系統會幫我們處理。我們也不會去處理類似于登錄、獲取權限、自定義視圖等這些細節問題。簡單來說,我們將使用系統提供的「黑盒」,并且在它上面添加一些代碼。
接下來,我們將討論一個特殊的視圖控制器: UIActivityViewController 。你也許沒聽說過,但是一定看到過:
使用這個控制器,不僅可以使用內置的分享選項,也可以使用一些其他的功能,如發郵件、短信、打印等。事實上,所彈出的選項會基于你所要分享的內容,但是這個控制器卻又非常容易掌握。相信我,毫不夸張的說,把它集成到你的 App 中不會超過兩行代碼,一會你就會見證這個過程。
如果你很想看看上述工作是如何實現的,并且想在你的下一個 App 中使用,那么,繼續往下讀,跟著我們去發現這是一個簡單而實用的功能。在這里我并不是想突出 iOS 8 中功能「新」的特點,而是為了告訴所有開發人員值得關注這些新功能所帶來的巨大的靈活性。那么,讓我們開始吧。
小覷 Demo
教程中創建的示例程序非常簡單。僅包含一個視圖控制器(我們保留了 Xcode 默認自動創建的那個),并且這個控制器包含了兩部分:包含了一個 bar button item 的 toolBar,以及其正下方的 textView。我們假想這是一個記筆記的應用的其中一部分:提供了一個 textView 去編寫文本,然后發布到 非死book 和 推ter。整個視圖控制器如下圖所示:
點擊 toolBar 上的 bar button item 后會彈出一個 action sheet ,并給我們顯示三個選項:
- Share on 非死book
- Share on 推ter
- More
對于上面的前兩項我們將使用 Social framework 實現:彈出 iOS 內置的 Compose view controller 去寫一些文字,然后發送出去。第三個選項,我們會顯示一個 UIActivityViewController ,上邊的兩項也能在這里找到。
下一個截圖展示了分享文字到 推ter 時,iOS 內置默認的 Compose view controller 的樣子:
還有一點我必須要提到的就是:在運行程序之前,必須在你的設備的設置選項中登錄 非死book 或者 推ter。接下來,我會展示如何操作,但是現在你得有個印象。當然,在我們的代碼中我會考慮到用戶沒有登錄的情況,并且顯示恰當的信息。過一會兒所有的一切都會實現。
你也意識到了這不是一個復雜的程序,所以這次我也不會提供一個初始項目。我們將在五分鐘內使用 Social framework 做好準備工作并且添加分享選項。對于那些想直接閱讀如何實現并且測試示例程序的讀者,你可以選擇跳到教程的最后直接去下載示例代碼。
創建 App 和 UI
讓我們運行 Xcode 來創建一個工程。創建的時候,確保設置或者選中了以下選項:
- 創建一個 Single-View Application
- 命名為 EasyShare (也可以選擇其他的名稱)
- 選擇 Swift 作為項目開發語言
- 選擇 iPhone 作為 App 的目標設備
一旦在 Xcode 中準備好了項目啟動的部分,直接打開 Interface Builder 來設置我們僅有的一個場景。首先在頂部、緊貼著狀態欄的正下方的地方(Y = 20)添加一個 toolBar 。你也可以為 toolBar 設置一個顏色。在 demo 中使用的顏色是 #F39C12 。然后,在 toolBar 右側一個 bar button item ,在 Attributes Inspector 中可以把 button item 的樣式設置為 Action ,并且設置其顏色為白色。
下一步,在對象庫(控件選擇欄)中選擇一個 textview 添加到視圖中。設置如下的 frame:
- X = 10
- Y = 74
- Width = 580
- Height = 300
下面我們會添加為 textview 添加邊框和圓角效果,不過現在做到這已經可以了。不要忘記刪除了 textview 中默認提供的「Lorem ipsum」文字。
現在我們來添加 約束(constraints) 。由于我們添加的子視圖足夠的簡單,因此添加約束也是相當容易。直接添加就可以了,不過有時可能會遇到約束沖突的情況,下面的截圖展示了我添加的約束。對于 toolbar 的約束:
對于 textview 的約束:
最終看上去應該像這樣:
現在是時候創建并且連接 IBOutlet 屬性到 textview 上了,同時在 bar button item 上綁定 IBAction 方法。在 ViewController.swift 這個文件中的最上面添加如下代碼:
@IBOutlet weak var noteTextview: UITextView!
然后將其連接到 textview 上。同樣地將 IBAction 方法連接到 bar button item 上:
@IBAction func showShareOptions(sender: AnyObject) { }
之后我們的很多的工作都會在這個方法中完成,但是目前我們先做到這。
我們已經完成了一些最基本的代碼,現在我們在 ViewController 中添加一個自定義方法為 textview 添加邊框和圓角效果:
func configureNoteTextView() { noteTextview.layer.cornerRadius = 8.0 noteTextview.layer.borderColor = UIColor(white: 0.75, alpha: 0.5).CGColor noteTextview.layer.borderWidth = 1.2 }
第一行代碼是產生圓角效果,下面兩行分別是設置邊框的顏色和邊框的寬度。
在 viewDidLoad() 方法中進行調用:
override func viewDidLoad() { super.viewDidLoad() configureNoteTextView() }
最后,我們在工程中添加即將要使用到的框架(Social framework)。在工程導航欄中單擊工程,然后選擇 General 選項,滾動到 Linked Frameworks and Libraries ,點擊加號按鈕(+),在彈出的窗口中輸入「social」進行搜索,選中添加:
添加成功后,Linked Frameworks and Libraries 中會顯示 Social.framework ,結果如下圖:
最后一步,在 ViewController.swift 的最上面添加:
import Social
現在我們的準備工作已經完成了,等下會添加具體的分享功能。你可以先運行一下程序,運行的結果應該如下所示:
顯示分享選項
在示例程序中,當點擊 bar button item 后,我們所提供的所有分享的選項會在一個 action sheet 中顯示。所以我們先來實現 showShareOptions(_:) 的 IBAction 方法。下面的截圖展示了完成后的效果:
讓我們開始吧。首先,我們需要考慮到鍵盤。當 textview 處于編輯狀態的時候,如果點擊 bar button item,在彈出 action sheet 之前需要隱藏鍵盤:
@IBAction func showShareOptions(sender: AnyObject) { // Dismiss the keyboard if it's visible. if noteTextview.isFirstResponder() { noteTextview.resignFirstResponder() } }
現在我們需要初始化一個新的 alert controller 并且將它設置為 action sheet:
@IBAction func showShareOptions(sender: AnyObject) { ... let actionSheet = UIAlertController(title: "", message: "Share your Note", preferredStyle: UIAlertControllerStyle.ActionSheet) } ``` 正如你所看到的截屏一樣,在我們的 action sheet 中我們需要顯示三個樣式相同的選項。然而,不要忘記為用戶提供一個可以取消 action sheet 的選項。所以,我們一共需要四個選項,代碼如下: ```swift @IBAction func showShareOptions(sender: AnyObject) { ... // Configure a new action for sharing the note in 推ter. let tweetAction = UIAlertAction(title: "Share on 推ter", style: UIAlertActionStyle.Default) { (action) -> Void in } // Configure a new action to share on 非死book. let 非死bookPostAction = UIAlertAction(title: "Share on 非死book", style: UIAlertActionStyle.Default) { (action) -> Void in } // Configure a new action to show the UIActivityViewController let moreAction = UIAlertAction(title: "More", style: UIAlertActionStyle.Default) { (action) -> Void in } let dismissAction = UIAlertAction(title: "Close", style: UIAlertActionStyle.Cancel) { (action) -> Void in } actionSheet.addAction(tweetAction) actionSheet.addAction(非死bookPostAction) actionSheet.addAction(moreAction) actionSheet.addAction(dismissAction) presentViewController(actionSheet, animated: true, completion: nil) }
這就是全部的代碼內容了。最后我們已經成功的顯示了 action sheet。現在你可以運行程序然后點擊 bar button item 去顯示 action sheet。但是現在沒有實現業務邏輯,所以無法正常工作。在下一部分,我們會逐漸完成缺失的代碼讓我們的程序可以分享到 推ter,然后是 非死book。
Tweet,Tweet
上面我已經提到了,如果用戶想要發送推文到社交網絡上的話,需要先登錄對應的賬號。對于代碼的邏輯來說,這就意味著在彈出發送視圖之前,我們首先要檢查用戶是否已經登錄了對應的賬號。如果登錄了就去正常顯示;反之就要展示一個自定義的警告視圖去提醒用戶。
我們首先實現發送到 推ter 這個功能。我們需要檢查用戶是否已經登錄的 推ter 的賬號。下面的是實現 tweetAction 彈出警告視圖的代碼:
let tweetAction = UIAlertAction(title: "Share on 推ter", style: UIAlertActionStyle.Default) { (action) -> Void in // Check if sharing to 推ter is possible. if SLComposeViewController.isAvailableForServiceType(SLServiceType推ter) { } else { self.showAlertMessage("You are not logged in to your 推ter account.") } }
正如你在上面代碼片段中看到的那樣, SLComposeViewController 中的 isAvailableForServiceType(_:) 這個類方法可以檢查用戶是否登錄的對應的賬號,相對應返回的結果為 true 或 false 。這個方法需要傳入一個和我們需要關心的社交平臺所相關的 string 值。Apple 已經為不同的分享方式提供了指定的 string 值,你可以輸入 SLServiceTyp 就會有自動提示:
在條件判斷的 else 部分會注意到我們調用了一個叫做 showAlertMessage(_:) 的函數去提醒用戶他還未登錄。但是這個方法現在沒有寫完,在這一部分的最后我們會實現這個方法。我必須要指出的是盡管這個 alert controller 使用了我們所傳遞的信息作為其顯示的內容,但是為了我們代碼的邏輯更加得清晰,我們最好避免編寫完全一樣的代碼。換言之,檢查用戶是否登錄了 非死book 也會復用這段邏輯。
讓我們繼續,現在我們需要初始化一個 SLComposeViewController 的實例。這個默認的控制器能讓我們編輯需要分享的內容以及提供我們想要的「Post」按鈕。
let tweetAction = UIAlertAction(title: "Share on 推ter", style: UIAlertActionStyle.Default) { (action) -> Void in // Check if sharing to 推ter is possible. if SLComposeViewController.isAvailableForServiceType(SLServiceType推ter) { // Initialize the default view controller for sharing the post. let 推terComposeVC = SLComposeViewController(forServiceType: SLServiceType推ter) } else { self.showAlertMessage("You are not logged in to your 推ter account.") } }
再一次,我們必須要傳入所對應的分享的類型去初始化 SLComposeViewController 。
上面的類中有個叫做 setInitialText(_:) 的方法。通過調用這個方法,我們可以直接把 textview 中的內容設置到發送視圖中。當你明白如何使用這個方法后,你會變得更加得心應手,但是請不要忽略一個問題:文字的最大的長度是 140 個字符。這是 推ter 在發推時的一個限制。
所以下一步我們要做什么?非常簡單:首先我們要檢查 textview 中的字符長度。如果小于或等于 140 個字符,我們就直接調用之前提到的初始化方法;反之,我們要「壓縮」這一段文字,只將前 140 個字符作為我們要發送的文本。
把我所說的翻譯成代碼吧:
let tweetAction = UIAlertAction(title: "Share on 推ter", style: UIAlertActionStyle.Default) { (action) -> Void in // Check if sharing to 推ter is possible. if SLComposeViewController.isAvailableForServiceType(SLServiceType推ter) { // Initialize the default view controller for sharing the post. let 推terComposeVC = SLComposeViewController(forServiceType: SLServiceType推ter) // Set the note text as the default post message. if count(self.noteTextview.text) <= 140 { // 譯者注:Swift 2.0 后使用 self.noteTextview.text.characters.count 推terComposeVC.setInitialText("\(self.noteTextview.text)") } else { let index = advance(self.noteTextview.text.startIndex, 140) // 譯者注:Swift 2.0 后使用 self.noteTextview.text.startIndex.advancedBy(140) let subText = self.noteTextview.text.substringToIndex(index) 推terComposeVC.setInitialText("\(subText)") } } else { self.showAlertMessage("You are not logged in to your 推ter account.") } }
不過上面缺少了彈出視圖的方法:
let tweetAction = UIAlertAction(title: "Share on 推ter", style: UIAlertActionStyle.Default) { (action) -> Void in // Check if sharing to 推ter is possible. if SLComposeViewController.isAvailableForServiceType(SLServiceType推ter) { ... // Display the compose view controller. self.presentViewController(推terComposeVC, animated: true, completion: nil) } else { self.showAlertMessage("You are not logged in to your 推ter account.") } }
上面我們已經完成了發送到 推ter 的工作。
嗯,就快完成了,不過我們還要添加缺少的 showAlertMessage(_:) 方法:
func showAlertMessage(message: String!) { let alertController = UIAlertController(title: "EasyShare", message: message, preferredStyle: UIAlertControllerStyle.Alert) alertController.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default, handler: nil)) presentViewController(alertController, animated: true, completion: nil) }
最后再說一句,用很少的代碼去實現了發送文本到 推ter(非死book 要做的步驟類似)其實并不是一件難事,并且這個過程是如此得容易和迅速!接下來讓我們的程序也能夠分享到 非死book,在這之前可以運行一下我們的程序。
分享到 非死book
這部分我們需要把 textview 中的內容分享到 非死book,實現步驟類似于分享到 推ter。實際上所做的會更加簡單一點,因為 非死book 沒有對發送的文字長度做限制,我們可以跳過檢查長度這個步驟。
現在我們需要關注的是 非死bookPostAction 。接下來我直接給出了它的實現,因為并沒有什么新的內容或者需要特別注意的地方:
let 非死bookPostAction = UIAlertAction(title: "Share on 非死book", style: UIAlertActionStyle.Default) { (action) -> Void in if SLComposeViewController.isAvailableForServiceType(SLServiceType非死book) { let 非死bookComposeVC = SLComposeViewController(forServiceType: SLServiceType非死book) 非死bookComposeVC.setInitialText("\(self.noteTextview.text)") self.presentViewController(非死bookComposeVC, animated: true, completion: nil) } else { self.showAlertMessage("You are not connected to your 非死book account.") } }
再一次,我們需要通過特定的 string 來檢查用戶是否連接了 非死book 賬號。這和 SLComposeViewController 實例初始化所需要的值是相同的。把 textview 中的文字作為發送視圖的初始值,然后發送!這就結束了!最后,記得當用戶沒有登錄 非死book 時,顯示一條提示信息。
測試前的準備
我們的示例程序既可以在模擬器又可以在真機上進行測試。然而,在測試之前,你需要成功的登錄對應的賬號,否則,你看到的會如下圖所示:
為了連接 推ter 或者 非死book(或者兩者),你需要打開設備的設置(Settings)界面進行賬號登錄。在設置中你能發現它們的上層入口:
如果想登錄 推ter,可以選擇 推ter 這個選項,然后輸入賬號和密碼。最后點擊登錄按鈕:
一旦登錄后,界面中會增加一行用于顯示用戶名:
可以點進修改密碼,或者將賬號移出系統。
你也可以使用類似的方法登錄 非死book。將郵箱地址作為用戶名并且提供密碼就可以了登錄了。你也可以通過顯示賬號的那一行來取消關聯 非死book。
確保你已經登錄這兩個賬號。登錄成功后,就可以盡情地測試你的程序了。
使用 UIActivityViewController
如果想要同時添加多個分享選項,或者集成一些額外的分享功能, UIActivityViewController 為我們提供了一個很好的解決方案。每次所顯示的選項會基于所顯示的內容,也可能是依賴于運行的應用,又或者是根據是否已經連接到特定的社交賬號來展示不同的選項。下面是一些常用的選項:
- Send an email
- Send a SMS
- Share on 推ter and 非死book
- Add to Reading List
- Copy
- Send using AirDrop
下面你要看到的,是僅僅通過兩行代碼去初始化和使用 UIActivityViewController 。初始化需要傳入兩個參數,其中第二個是可選的(optional),你可以將其設置為 nil 。
第一個參數是一個數組,里面包含了我們想要發送的內容。在我們的示例程序中,我們在數組中添加了 textview 中的文字。當然了,我們也可以把圖片添加到數組中。數組中的內容決定了系統顯示的樣式。具體來說,如果我們只有一張圖片,那么就不會顯示「Add to reading list」。
第二個參數也是一個數組。通過這個數組你可以明確地告訴系統你想顯示的 activity 的類型 。如果設置為 nil ,那么系統會顯示所有的可以使用的 activity。
在這兒你也可以選擇哪些 activity 不顯示在分享視圖中,不過我們一會兒再來探討。現在我們要關注的是第三個選項: moreAction 。下面的代碼我們會初始化 UIActivityViewController 的一個實例并且彈出:
let moreAction = UIAlertAction(title: "More", style: UIAlertActionStyle.Default) { (action) -> Void in let activityViewController = UIActivityViewController(activityItems: [self.noteTextview.text], applicationActivities: nil) self.presentViewController(activityViewController, animated: true, completion: nil) }
如果運行程序,現在 More 選項已經可以使用了。不過模擬器上的選項可能會沒有真機上多。例如,模擬器上就沒有發送到 SMS 的選項:
上面我已經提到,有的開發者需要移除一些并不想顯示在分享視圖中選項。你所需要做的是聲明 excludedActivityTypes 這個屬性并且將特定的類型傳入數組。作為例子,我已經更新了代碼,你可以看到,現在代碼片段中多了一行,這行代碼可以移除發送郵件這個選項:
let moreAction = UIAlertAction(title: "More", style: UIAlertActionStyle.Default) { (action) -> Void in let activityViewController = UIActivityViewController(activityItems: [self.noteTextview.text], applicationActivities: nil) activityViewController.excludedActivityTypes = [UIActivityTypeMail] self.presentViewController(activityViewController, animated: true, completion: nil) }
再次運行程序,注意到發送郵件選項已經不見了。
小結
作為一個開發者,我必須承認的是,當我第一次使用示例程序中的分享功能的時候,我被其集成的簡潔性所震驚了。毫無疑問,我們今天解決的只是一些簡單的編程任務,不過我還是希望通過例子能夠將實現這些功能的步驟闡釋得足夠明白。顯然,無論是 iOS 內置的 Social framework,還是 UIActivityViewController 并不是適用于所有的情況。因為這些系統控件可能和我們的自定義的 UI 不匹配。但是如果不存在界面上的情況,那么就可以考慮下使用系統提供方案。如果你已經準備使用了,那么現在你已經知道如何在幾分鐘內集成這些功能了。無論你是否決定使用今天我所講的內容,我都希望今天的內容可以為你以后的開發中帶來方便。
作為參考,你可以 在這里下載整個 Xcode project 文件 。
本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問http://swift.gg。