前言
關(guān)于這篇文章
由于iPhone 6S發(fā)布不到一年的時(shí)間,很多新特性、新技術(shù)還未普遍,不管是3D Touch
的使用還是開發(fā),對其有相關(guān)了解的人并不多。前幾天偶然接觸了3D Touch
的某個(gè)API接口,為了滿足好奇心,于是我就系統(tǒng)地去了解了這個(gè)蘋果的新技術(shù)。查閱了相關(guān)的官方文檔,敲了些Demo,并編寫了這篇文章,作為總結(jié)。
從Force Touch到3D Touch
使用過新版Mac Book或Apple Watch的朋友應(yīng)該對Force Touch
這個(gè)詞匯并不陌生,這是蘋果針對設(shè)備觸控操作的一項(xiàng)新的技術(shù),將傳統(tǒng)的用戶觸控點(diǎn)擊操作擴(kuò)展化,加上了按壓操作,設(shè)備可根據(jù)用戶手指在屏幕上的按壓力度來進(jìn)行相應(yīng)的響應(yīng)。在新版Mac Book以及Apple Watch中,我們可以通過使用不同的力度按壓觸控板或觸控屏來調(diào)出更多的控制選項(xiàng),人機(jī)交互性非常高。
3D Touch
是Force Touch
延伸出的新一代技術(shù),它現(xiàn)在應(yīng)用于裝配了iOS9
以上操作系統(tǒng)的iPhone 6S
上,致力于向iPhone用戶提供更加高質(zhì)量的交互體驗(yàn),將操作方式擴(kuò)展至三維層面。
本篇文章分別針對3D Touch
的特性以及開發(fā)API進(jìn)行講解。
特性 — 3D Touch On iPhone
Home Screen Quick Actions - 主頁屏幕快速操作
在手機(jī)的主頁上,假如你用手指輕輕按壓某個(gè)應(yīng)用的圖標(biāo),圖標(biāo)的背后出現(xiàn)了一個(gè)半透明的矩形,這就說明了這款應(yīng)用支持Home Screen Quick Actions
(主頁屏幕快速操作)。這時(shí),我們保持手指按壓,并加大力度,你就會(huì)發(fā)現(xiàn)這個(gè)應(yīng)用圖標(biāo)的周圍都變模糊了,一個(gè)小巧的選項(xiàng)欄菜單在你眼前彈出。
這個(gè)選項(xiàng)欄菜單每個(gè)選項(xiàng)視圖最多允許有兩行文本(主文本、次文本)以及一個(gè)圖標(biāo)(可要可不要)。另外,圖標(biāo)的位置是不定的,它會(huì)在文本的左邊或者右邊進(jìn)行布局,具體放置于哪一邊,則根據(jù)應(yīng)用圖標(biāo)的水平位置而定,不過這些布局系統(tǒng)都已經(jīng)幫我們處理好了。
當(dāng)我們點(diǎn)擊某一個(gè)選項(xiàng),應(yīng)用程序就會(huì)運(yùn)行起來,并且執(zhí)行相應(yīng)的操作,相比于以往用戶要先進(jìn)入應(yīng)用程序才能再進(jìn)行操作,使用
Home Screen Quick Actions
則更加的便利。
Peek and Pop - 預(yù)覽和查看詳情
Peek - 預(yù)覽
在傳統(tǒng)的手機(jī)用戶操作中,當(dāng)我們在應(yīng)用里看到某張縮略圖、某個(gè)網(wǎng)址鏈接或者某個(gè)列表Item時(shí),若我們想查看詳細(xì)的信息,比如想看縮略圖對應(yīng)的大圖、網(wǎng)址鏈接對應(yīng)的網(wǎng)頁、Item對應(yīng)的詳情頁面,一般會(huì)用手指對屏幕進(jìn)行點(diǎn)擊操作,從而讓應(yīng)用的頁面進(jìn)行跳轉(zhuǎn)。新特性Peek
則大大提高了這類型操作的用戶體驗(yàn)。
上方效果圖所展示的是對一個(gè)縮略圖片進(jìn)行
Peek
操作,當(dāng)我們用手指輕壓圖片時(shí),圖片的周圍迅速模糊,這說明你所按壓的地方支持Peek
操作。按壓力度稍微增大,預(yù)覽圖就出來了,這就是Peek
(預(yù)覽)功能。當(dāng)我們將手指從屏幕上抬起時(shí),預(yù)覽圖就會(huì)消失,界面也就恢復(fù)回來。有了
Peek
的操作簡化,當(dāng)我們想預(yù)覽某些詳情時(shí),只需按壓屏幕,手機(jī)即可彈出詳情頁面;移開手指,界面恢復(fù)原狀,這樣就使得用戶不必進(jìn)入一個(gè)新的頁面瀏覽詳情,然后再點(diǎn)擊返回按鈕回到原來視圖了。
Peek quick actions - 預(yù)覽中的快速操作
上方已經(jīng)說到,當(dāng)我們的手指按壓需要預(yù)覽的屏幕的區(qū)域,預(yù)覽的視圖就會(huì)出現(xiàn)。現(xiàn)在,繼續(xù)保持手指的按壓狀態(tài),然后再向上方滑動(dòng),就會(huì)在預(yù)覽視圖的下方滑出一個(gè)選項(xiàng)欄菜單。
當(dāng)菜單完全顯示出來后,你可以松開按壓在屏幕上的手指,然后點(diǎn)擊相應(yīng)的選項(xiàng)來執(zhí)行操作,就像上方效果圖一樣,可以復(fù)制、分享、點(diǎn)贊、刪除等等。
Pop - 查看詳情
在能出現(xiàn)Peek
預(yù)覽視圖的手指按壓力度基礎(chǔ)上,用戶再使把勁加大點(diǎn)力度,就能進(jìn)入相關(guān)詳情頁面,這個(gè)就是Pop
。事實(shí)上,Pop
所進(jìn)入的詳情頁面跟用戶用手指輕點(diǎn)后所跳轉(zhuǎn)出來的頁面是一樣的,所以,用戶如果想直接進(jìn)入詳情頁面,不需要預(yù)覽,可以直接輕點(diǎn)屏幕指定區(qū)域即可;而在預(yù)覽中,用戶想了解得更多,可以再加大按壓力度進(jìn)入詳情頁面。
3D Touch 還能做些什么?
3D Touch
能完成的功能非常多,你可以利用它來制作一個(gè)繪畫板,根據(jù)手指在屏幕上的壓力大小來模擬畫筆的粗細(xì),你也可以做一款精美的手游,通過手指的按壓力度來反饋不同的游戲操作......
總之,3D Touch
非常的強(qiáng)大,潛力無限。??
開發(fā) — 3D Touch API
下面,我會(huì)通過蘋果提供的3D Touch
API就之前所提及到的各個(gè)3D Touch
特性進(jìn)行開發(fā)實(shí)現(xiàn)的分析。所有的交互跟視圖布局我都是使用純代碼去實(shí)現(xiàn)。
判斷設(shè)備是否支持3D Touch
我們在為應(yīng)用添加3D Touch
功能時(shí),有必要做設(shè)備是否支持或開啟3D Touch
的判斷,考慮到用戶使用的手機(jī)型號(hào)比iPhone 6s
低,或者用戶自己已經(jīng)手動(dòng)關(guān)閉了3D Touch
功能,所以在編寫代碼的時(shí)候,需要獲取或監(jiān)聽當(dāng)前設(shè)備針對于3D Touch
的可用性狀態(tài),以便在后面做出判斷。
我們獲取當(dāng)前設(shè)備針對于3D Touch
的可用性狀態(tài),可以使用協(xié)議UITraitEnvironment
。
UITraitEnvironment的結(jié)構(gòu)
UITraitEnvironment
中包含一個(gè)屬性以及一個(gè)方法:
- var traitCollection: UITraitCollection
- func traitCollectionDidChange(previousTraitCollection: UITraitCollection?)
不管是屬性還是方法,其目的都是讓我們能夠獲取到當(dāng)前的設(shè)備特征環(huán)境集合,只不過使用方法來獲取比較動(dòng)態(tài),可以時(shí)刻監(jiān)聽變化。
獲取到的環(huán)境集合為UITraitCollection
類型,這個(gè)類里面包含屬性forceTouchCapability
,是一個(gè)UIForceTouchCapability
枚舉類型,有三個(gè)case,分別是Unknown
(未知的)、Unavailable
(不可用的)、Available
(可用的),根據(jù)相應(yīng)的forceTouchCapability
值,我們就能知道當(dāng)前設(shè)備對3D Touch
的可用性狀態(tài)。
如何使用UITraitEnvironment
其實(shí),很多我們常用的類都已經(jīng)實(shí)現(xiàn)了UITraitEnvironment
協(xié)議,比如說UIView
、UIViewController
等等,我們可以直接從他們的內(nèi)部中獲得traitCollection
屬性然后進(jìn)行判斷:
if self.traitCollection.forceTouchCapability == .Available {
// TODO: 加入實(shí)現(xiàn)3D Touch的代碼
}
如果我們想做到實(shí)時(shí)監(jiān)聽狀態(tài)的變化,比如軟件在運(yùn)行的時(shí)候用戶突然關(guān)閉了3D Touch
,我們可以自己去實(shí)現(xiàn)UITraitEnvironment
協(xié)議,實(shí)現(xiàn)其中的監(jiān)聽方法,在這里我就不演示代碼了。
Home Screen Quick Actions
實(shí)現(xiàn)Home Screen Quick Actions選項(xiàng)欄菜單
實(shí)現(xiàn)Home Screen Quick Actions
有兩種方法,分別是static
(靜態(tài))實(shí)現(xiàn)以及dynamic
(動(dòng)態(tài))實(shí)現(xiàn),它們的實(shí)現(xiàn)主要都是依靠UIApplicationShortcutItem
這個(gè)類來進(jìn)行。
-
Static 靜態(tài)實(shí)現(xiàn) 可以硬性地規(guī)定好要呈現(xiàn)出來的選項(xiàng)信息,這些設(shè)定好的選項(xiàng)在后期是不能夠再次修改的。進(jìn)行靜態(tài)實(shí)現(xiàn),我們需要修改的是
Info.plist
文件。下面我就編輯Info.plist
文件來進(jìn)行靜態(tài)實(shí)現(xiàn):
如圖所示,我在
Info.plist
文件中添加了一個(gè)key為UIApplicationShortcutItems
的數(shù)組,里面有兩個(gè)字典類Item,每個(gè)字典類Item都代表了一個(gè)UIApplicationShortcutItem
,它們具有5個(gè)屬性:UIApplicationShortcutItemIconType (String) 選項(xiàng)的圖標(biāo)類型,可選擇性設(shè)置。使用
static
靜態(tài)實(shí)現(xiàn)是無法使用自己定義的圖標(biāo)的,不過系統(tǒng)也已經(jīng)提供好一些默認(rèn)的圖標(biāo),我們可以設(shè)置圖標(biāo)的類型來使用指定的系統(tǒng)默認(rèn)圖標(biāo)。那究竟有多少種圖標(biāo)類型呢?這個(gè)我放在后面講~UIApplicationShortcutItemTitle (String) 選項(xiàng)的主標(biāo)題,必要設(shè)置項(xiàng)。相對于次標(biāo)題,主標(biāo)題文字較大且顏色較深,位于次標(biāo)題的上方。
UIApplicationShortcutItemSubtitle (String) 選項(xiàng)的次標(biāo)題,可選擇性設(shè)置。主要起到選項(xiàng)的幫助提示作用。
UIApplicationShortcutItemType (String) 選項(xiàng)的類型,必要設(shè)置項(xiàng)。利用它,我們就可以在你點(diǎn)擊選項(xiàng)執(zhí)行handle語句時(shí)判別具體是哪一個(gè)選項(xiàng)被選中了,從而讓程序進(jìn)行相應(yīng)的操作。關(guān)于選項(xiàng)選擇后的反饋處理,我在后面也會(huì)詳細(xì)去講。
-
UIApplicationShortcutItemUserInfo (Dictionary) 附加信息,可選擇性設(shè)置。你可以在里面添加某些附加信息,在執(zhí)行handle語句的時(shí)候可提取出來,具體看你的操作邏輯是如何實(shí)現(xiàn)的了。
現(xiàn)在我把應(yīng)用跑起來后,回到手機(jī)的主頁面,并用力按壓此應(yīng)用的圖標(biāo),
Home Screen Quick Actions
的選項(xiàng)欄菜單就呈現(xiàn)在我們的眼前:
-
Dynamic 動(dòng)態(tài)實(shí)現(xiàn) 較為靈活,我們通過代碼去構(gòu)建每個(gè)選項(xiàng),所以,在程序運(yùn)行的過程中,選項(xiàng)的信息可以動(dòng)態(tài)地去改變,不過,動(dòng)態(tài)實(shí)現(xiàn)較靜態(tài)實(shí)現(xiàn)來說相對復(fù)雜了點(diǎn)。
enum ShortcutItemType: String { case Home case Share } func setupShortcutItemsWithApplication(application: UIApplication) { let playItem = UIApplicationShortcutItem(type: ShortcutItemType.Home.rawValue, localizedTitle: "主頁", localizedSubtitle: "點(diǎn)擊進(jìn)入應(yīng)用主頁", icon: UIApplicationShortcutIcon(templateImageName: "icon_home"), userInfo: nil) let shareItem = UIApplicationShortcutItem(type: ShortcutItemType.Share.rawValue, localizedTitle: "分享", localizedSubtitle: "點(diǎn)擊向朋友分享", icon: UIApplicationShortcutIcon(type: .Share), userInfo: nil) application.shortcutItems = [playItem, shareItem] }
如代碼所示,應(yīng)用的單例application對象中擁有
shortcutItems
屬性,這是一個(gè)數(shù)組對象,我們可以在里面添加UIApplicationShortcutItem
實(shí)例或可變UIMutableApplicationShortcutItem
實(shí)例,從而實(shí)現(xiàn)菜單中的每個(gè)選項(xiàng)。
這里值得注意的是,要在shortcutItem
上配置icon并不是用到簡單的UIImage
類,而是使用專門的UIApplicationShortcutIcon
,它有兩種構(gòu)造方式,一種是選擇一個(gè)類型,然后使用與類型相對應(yīng)的系統(tǒng)所提供的圖標(biāo),究竟有多少種類型呢?我下面來列舉一下:UIApplicationShortcutIconType 枚舉: iOS 9.0 及以上系統(tǒng)所支持: case Compose case Play case Pause case Add case Location case Search case Share iOS 9.1 及以上系統(tǒng)所支持: case Prohibit case Contact case Home case MarkLocation case Favorite case Love case Cloud case Invitation case Confirmation case Mail case Message case Date case Time case CapturePhoto case CaptureVideo case Task case TaskCompleted case Alarm case Bookmark case Shuffle case Audio case Update
構(gòu)造的另一種方式是填入一串字符串,其實(shí)就是圖片的名字,對于這張圖片,蘋果做出了以下的要求:
- 這張圖片必須位于你的應(yīng)用程序包(app bundle)中。
- 圖片要為
正方形
,顏色使用單色
,大小為35×35
points(點(diǎn))。系統(tǒng)為了保持UI的統(tǒng)一性,會(huì)將你提供的這張圖片進(jìn)行顏色渲染,最終圖片的整體顏色會(huì)變?yōu)榛液谏赃@里建議圖片使用單色,即只有一種顏色。
現(xiàn)在我們在
AppDelegate
中的application(_:, didFinishLaunchingWithOptions:)
方法里面執(zhí)行setupShortcutItemsWithApplication(_:)
,把a(bǔ)pplication作為參數(shù)傳入其中。
這里提供一部分的參考代碼:if let shortcutItems = application.shortcutItems where shortcutItems.isEmpty { setupShortcutItemsWithApplication(application) }
可能有人在這里會(huì)感到奇怪,為什么我在執(zhí)行
setupShortcutItemsWithApplication(_:)
方法前要先判斷application
中的shortcutItems
數(shù)組屬性是否為空容器(里面沒有對象),原因是當(dāng)我們第一次開啟這個(gè)應(yīng)用的時(shí)候,系統(tǒng)會(huì)將我們應(yīng)用所配置的所有shortcutItem
進(jìn)行記錄,在我們下一次打開應(yīng)用時(shí),我們不必再去配置一遍,也就是說,application
中的shortcutItems
屬性在應(yīng)用第一次打開的時(shí)候是空的,但是當(dāng)我們進(jìn)行相應(yīng)的配置后,在下一次的應(yīng)用開啟時(shí),shortcutItems
就會(huì)默認(rèn)被系統(tǒng)所賦值,我們也沒必要每次打開應(yīng)用都去配置它。現(xiàn)在,運(yùn)行程序,我們來測試一下:
在程序運(yùn)行的過程中,我們可以任意改變
shortcutItems
:UIApplication.sharedApplication().shortcutItems = dynamicShortcuts
static與dynamic混用共同實(shí)現(xiàn)Home Screen Quick Actions 時(shí),選項(xiàng)欄菜單中各個(gè)選項(xiàng)的順序是按照
先靜態(tài)后動(dòng)態(tài)
來排列的。此外,我們還要注意避免選項(xiàng)類型的沖突。
監(jiān)聽Home Screen Quick Actions選項(xiàng)的選擇并進(jìn)行處理
監(jiān)聽Home Screen Quick Actions
選項(xiàng)的點(diǎn)擊選擇,我們要在AppDelegate
中實(shí)現(xiàn)方法application(_:, performActionForShortcutItem:, completionHandler:)
,判斷用戶選擇的是哪一個(gè)選項(xiàng),然后進(jìn)行相應(yīng)的操作:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
switch shortcutItem.type {
case ShortcutItemType.Home.rawValue:
print("選擇了主頁選項(xiàng)")
case ShortcutItemType.Share.rawValue:
print("選擇了分享選項(xiàng)")
default:
print("選擇了其他選項(xiàng)")
}
completionHandler(true)
}
在方法的最后記得調(diào)用completionHandler
閉包,把是否處理完成的布爾值傳進(jìn)去,如成功處理完,傳true
,失敗,則傳false
。
注意:關(guān)于方法application(_:, performActionForShortcutItem:, completionHandler:)
,蘋果官方文檔給出了使用的相關(guān)注意事項(xiàng):
這個(gè)方法會(huì)在你選擇了某個(gè)Home Screen Quick Actions
選項(xiàng)時(shí)調(diào)用,但是前提條件是AppDelegate中的application(_:,willFinishLaunchingWithOptions:)
方法以及application(_:didFinishLaunchingWithOptions)
方法都要返回true
真值。蘋果建議這個(gè)方法是在應(yīng)用已經(jīng)啟動(dòng)了、在后臺(tái)工作的時(shí)候才去監(jiān)聽Home Screen Quick Actions
選項(xiàng)的選擇,若我們在應(yīng)用程序還未啟動(dòng)的時(shí)候使用Home Screen Quick Actions
選擇某個(gè)選項(xiàng),我們就不應(yīng)該使用這個(gè)方法來進(jìn)行監(jiān)聽,應(yīng)當(dāng)在application(_:,willFinishLaunchingWithOptions:)
方法或者application(_:didFinishLaunchingWithOptions)
方法里進(jìn)行操作,最后返回false
,使得AppDelegate不會(huì)再去調(diào)用application(_:, performActionForShortcutItem:, completionHandler:)
這個(gè)方法。
那么,當(dāng)Home Screen Quick Actions
選項(xiàng)在應(yīng)用還未啟動(dòng)時(shí)被選擇了,我們應(yīng)該如何在AppDelegate中的application(_:,willFinishLaunchingWithOptions:)
方法或application(_:didFinishLaunchingWithOptions)
方法里監(jiān)聽Home Screen Quick Actions
選項(xiàng)的選擇以及做出相應(yīng)的操作呢?這里我們可以從這兩個(gè)方法的launchOptions
參數(shù)中獲取到對應(yīng)的shortcutItem
:
let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem
下面我就重寫application(_:didFinishLaunchingWithOptions)
來演示一下:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// TODO: 應(yīng)用的初始化代碼,包括UIWindow以及UIViewController等等的配置...
var performActionForShortcutItemWhenAppLaunch = false
// 判斷應(yīng)用的啟動(dòng)是否是因?yàn)橛脩暨x擇了Home Screen Quick Actions選項(xiàng)
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
performActionForShortcutItemWhenAppLaunch = true
print("選擇了\(shortcutItem.type)類的選項(xiàng)")
// TODO: 相應(yīng)的Handle操作
}
return !performActionForShortcutItemWhenAppLaunch
}
如上方代碼所示,我在前面就定義了以布爾型變量performActionForShortcutItemWhenAppLaunch
,默認(rèn)值為false
,然后再從launchOptions
中取出shortcutItem
,如果shortcutItem
為空,則說明應(yīng)用的啟用是由于用戶點(diǎn)擊了應(yīng)用的圖標(biāo),而不是通過Home Screen Quick Actions
;如果shortcutItem
不為空,則說明用戶是用過點(diǎn)擊shortcutItem
對應(yīng)的選項(xiàng)來啟動(dòng)應(yīng)用的,這時(shí)候我將true
值賦值給了performActionForShortcutItemWhenAppLaunch
。在方法的最后,我通過返回performActionForShortcutItemWhenAppLaunch
的布爾相反值,來讓應(yīng)用避免調(diào)用application(_:, performActionForShortcutItem:, completionHandler:)
方法。
Peek And Pop
Peek And Pop
較Home Screen Quick Actions
來說更為復(fù)雜,下面我就Peek
、Peek quick actions
、Pop
的實(shí)現(xiàn)進(jìn)行分析。
Peek & Pop
實(shí)現(xiàn)Peek
和Pop
首先我們要關(guān)注協(xié)議UIViewControllerPreviewingDelegate
,它有兩個(gè)需要我們?nèi)?shí)現(xiàn)的方法:
-
previewingContext(_:, viewControllerForLocation:) -> UIViewController
Peek
就是用戶用力按壓屏幕某個(gè)地方時(shí),周圍變得模糊,然后隨著按壓的力度加強(qiáng),最后會(huì)有一個(gè)預(yù)覽視圖彈出。舉個(gè)例子,屏幕上有一個(gè)TableView
,當(dāng)我們手指按壓其中某個(gè)Cell
時(shí),Cell
的周圍就會(huì)變得模糊,然后關(guān)于這個(gè)Cell
的預(yù)覽視圖就出現(xiàn)了。這個(gè)方法就是用于配置此過程中的相關(guān)邏輯,并返回最終的預(yù)覽視圖所屬的視圖控制器。在這個(gè)方法中,我們需要配置一些東西:-
previewingContext
參數(shù)中的sourceRect
。這是一個(gè)CGSize
類型,我們要將手指所按壓的視圖控件的frame
賦值給它,從而讓系統(tǒng)精確將視圖控件的周圍模糊掉;如何獲取被按壓的視圖控件呢?方法的viewControllerForLocation
參數(shù)就是當(dāng)用戶用力按壓時(shí)手指的按壓點(diǎn),我們可以利用這個(gè)按壓點(diǎn)來找到包含此點(diǎn)的視圖控件。 - 詳情視圖控制器的創(chuàng)建、預(yù)覽尺寸設(shè)置以及返回。在這個(gè)方法里,我們得實(shí)例化我們要預(yù)覽時(shí)展現(xiàn)出來的詳情視圖控制器,并且利用
preferredContentSize
設(shè)置它的預(yù)覽大小,這是一個(gè)CGSize
類型,當(dāng)我們把長寬都設(shè)置為0.0
的時(shí)候,預(yù)覽視圖大小就會(huì)采用系統(tǒng)默認(rèn)的尺寸值。
-
-
previewingContext(_:, commitViewController:)
Pop
操作就是在這個(gè)方法里執(zhí)行,在前面已經(jīng)說到,Pop
其實(shí)就是我們傳統(tǒng)情況下點(diǎn)擊控件然后應(yīng)用跳出詳情視圖控制器這一過程,所以,在這里我們需要做的就是把詳情視圖控制器展現(xiàn)出來。方法中的commitViewController
參數(shù)就是我們在前面方法中返回的詳情視圖控制器,我們在這個(gè)方法中將其"Show"出來即可。
下面就是參考的代碼:
// MARK: - UIViewControllerPreviewingDelegate
extension ViewController: UIViewControllerPreviewingDelegate {
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = self.tableView.indexPathForRowAtPoint(location) else { return nil }
let selectedCellFrame = tableView.cellForRowAtIndexPath(indexPath)!.frame
let detailViewController = DetailViewController()
detailViewController.mainTitle = self.tableViewData[indexPath.row]
detailViewController.preferredContentSize = CGSize(width: 0.0, height: 350)
previewingContext.sourceRect = selectedCellFrame
return detailViewController
}
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
self.showViewController(viewControllerToCommit, sender: self)
}
}
當(dāng)我們實(shí)現(xiàn)了UIViewControllerPreviewingDelegate
協(xié)議后,我們就可以為視圖控制器注冊Peek
預(yù)覽了,不過,在注冊的時(shí)候一定要先判斷好設(shè)備的3D Touch
是否可用:
// MARK: - Setup 3D Touch
if self.traitCollection.forceTouchCapability == .Available {
self.registerForPreviewingWithDelegate(self, sourceView: self.tableView)
} else {
print("3D Touch 不可用!")
}
這里我們使用UIViewController
中的方法registerForPreviewingWithDelegate(_:, sourceView:)
來進(jìn)行Peek
注冊,方法第一個(gè)傳入的參數(shù)就是實(shí)現(xiàn)了UIViewControllerPreviewingDelegate
的實(shí)例,第二個(gè)參數(shù)就是手指按壓的監(jiān)聽視圖。
Peek quick actions
Peek
的快速操作是在詳情視圖控制器中實(shí)現(xiàn)的,我們只需重寫這個(gè)視圖控制器的previewActionItems() -> [UIPreviewActionItem]
方法,返回一個(gè)數(shù)組即可。
UIPreviewActionItem
為一個(gè)協(xié)議,一般我們需要?jiǎng)?chuàng)建的是UIPreviewAction
或者UIPreviewActionGroup
實(shí)例。
-
UIPreviewAction就是代表一個(gè)選項(xiàng),它的構(gòu)造方法為
init(title: String, style: UIPreviewActionStyle, handler: (UIPreviewAction, UIViewController) -> Void)
,設(shè)置的是選項(xiàng)的標(biāo)題、類型以及選項(xiàng)選擇后所需要處理的操作。這里的類型UIPreviewActionStyle
有三種,分別是默認(rèn)
類型、已選擇
類型(旁邊有一個(gè)勾)、取消
類型(標(biāo)題顏色為紅色)。 -
UIPreviewActionGroup是一個(gè)
UIPreviewAction
的分組,它的構(gòu)造方法為init(title: String, style: UIPreviewActionStyle, actions: [UIPreviewAction])
,傳入的參數(shù)為標(biāo)題以及組成這個(gè)小組的UIPreviewAction
成員數(shù)組。它的作用是當(dāng)我們點(diǎn)擊這個(gè)小組選項(xiàng)時(shí),它就會(huì)將自己的成員選項(xiàng)們展開并呈現(xiàn)出來。
這里提供參考代碼:
private lazy var previewActions: [UIPreviewActionItem] = {
let action1 = UIPreviewAction(title: "分享", style: .Default, handler: { action, viewController in
print("Peek quick actions- 分享")
})
let action2 = UIPreviewAction(title: "搜索", style: .Default, handler: { action, viewController in
print("Peek quick actions- 搜索")
})
let action3 = UIPreviewActionGroup(title: "更多", style: .Default, actions: [action1, action2])
return [action1, action2, action3]
}()
// MARK: - Setup PreviewActionItems
override func previewActionItems() -> [UIPreviewActionItem] {
return self.previewActions
}
應(yīng)用運(yùn)行的效果圖如下:
3D Touch的其他API
蘋果為3D Touch
提供了Force Properties
(按壓強(qiáng)度屬性),我們可以在UITouch
類中找到屬性force
和maximumPossibleForce
,分別代表瞬時(shí)按壓力度以及設(shè)備最大可能達(dá)到的按壓力度,我們可以利用這兩個(gè)屬性完成很多有趣的交互,這就要看大家的想象力了,在這里我就舉一個(gè)簡單的小例子:
我編寫了一個(gè)UIView
的子類,它的大小由我們手指按壓它的力度去決定,這是它的內(nèi)部代碼:
class ResizeableView: UIView {
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
var multiple: CGFloat = 0.0
if let force = touches.first?.force, let maximumPossibleForce = touches.first?.maximumPossibleForce {
multiple = force / maximumPossibleForce
}
self.transform = CGAffineTransformMakeScale(1 + multiple, 1 + multiple)
}
}
現(xiàn)在我在一個(gè)視圖控制器中創(chuàng)建它的一個(gè)實(shí)例,并添加到控制器的視圖中,然后隨便設(shè)置一個(gè)frame
給它:
class TouchViewController: UIViewController {
private let mViewSizeValue: CGFloat = 70.0
private lazy var mView: ResizeableView = {
let view = ResizeableView()
view.backgroundColor = UIColor.redColor()
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.mView)
self.mView.frame = CGRectMake(100, 200, mViewSizeValue, mViewSizeValue)
}
}
于是乎,一個(gè)利用簡單的利用force
和maximumPossibleForce
屬性的小例子就做出來了。我們運(yùn)行一下看看效果:
總結(jié)
這篇文章向大家介紹了3D Touch
的特性,并詳細(xì)講解了有關(guān)于3D Touch
開發(fā)API的使用。相關(guān)代碼我已經(jīng)發(fā)布到個(gè)人的GitHub上:Tan3DTouch。
感謝大家的閱讀,在這里也祝大家夏日愉快!
參考資料
蘋果官方文檔:
Adopting 3D Touch on iPhone
iOS Human Interface Guidelines: 3D Touch
蘋果官方參考項(xiàng)目:
ApplicationShortcuts: Using UIApplicationShortcutItem
ViewControllerPreviews: Using the UIViewController previewing APIs