跟著官方文檔學習3D Touch

前言

關于3D touch蘋果官方文檔是這么開始介紹的:


3D Touch官網截圖.png

大意如下:
iOS9開始,所有新的手機都增加了一個三維的用戶接口界面。

  • 在app外,用戶按壓主屏幕圖標就可以立即訪問應用程序所提供的功能。
  • 在app內,用戶按壓視圖就可以預覽額外的新內容,體驗快速訪問的特性。
    備注:app外,是指app未啟動或者處于后臺狀態;app內,是指app已經啟動且處于前臺激活狀態。

如果上面的簡介沒有看懂,可以用官方文檔中下面這段話來進一步解釋。

3D Touch官方截圖.png

大意如下:
3DTouch為iOS9用戶提供了一個額外維度的人機交互界面。在支持3DTouch的設備上,在app外,人們可以在主屏幕上按壓app圖標來快速選擇app可執行的某個具體的操作。在app內,人們可以使用不同的壓力來得到不同的內容查看效果:1.預覽視圖 2.打開一個單獨的視圖控制器界面查看視圖,進而進行其他交互。
蘋果的3D Touch分為兩類,一類是app外,在主屏幕上按壓app的圖標,可以在app圖標旁邊彈出一個帶有快捷操作項的菜單。另一類是在app內,稍用力按壓某個視圖,可以預覽除去該視圖額外的內容,再稍加用力按壓屏幕,可以彈出另一個控制器界面,這個控制器界面就是點擊這個被按壓的視圖將會跳轉的控制器。
下面我就以app內app外兩個維度來跟著官方文檔解釋3D Touch。


(一)app外3D Touch—Home Screen Quick Actions

所謂app外3D Touch也就是蘋果官方所說的Home Screen Quick Actions.

Home Screen Quick Actions簡介.png

大意如下:
過去,擁有iPhone的用戶,總是可以通過輕輕的點擊(tap)app圖標的方式來啟動一個app。或者通過按住屏幕上某個app圖標不松手的方式使app處于抖動可編輯狀態,進而來對主屏幕進行編輯,此處的編輯是指:刪除app或者移動app等操作。
現在,擁有iPhone6s或者iPhone6s Plus的用戶,除了可以進行以上的兩個操作外,用戶還可以通過按壓iPhone6s或者iPhone6s Plus應用圖標的方式,獲得由一組快速操作按鈕組成的菜單,也就是上圖中的菜單。菜單中的每個可以觸發操作的按鈕,蘋果稱之為quick actions,我在此稱之為快捷操作項。當用戶選擇了一個快捷操作項時,app將啟動或被激活,然后app delegate對象將會接收到與當前觸發的這個快速啟動項相關的消息。


如上圖所示,這是我在官方文檔中截取的圖片,上圖中矩形菜單中的四個item就是我們所說的“quick action”譯為:快速操作項。而這4個quick action共同組成了“a set of quick actions”,也就是上圖屏幕截圖中的"小菜單"。用戶能夠通過這組快速操作項來對app進行快速訪問,看起來有點快捷鍵的意思(這只是筆者本人的觀點,也是我譯為快捷操作項的原因)。

The best quick actions anticipate and accelerate a user’s interaction with your app.譯:這是最好的快捷操作方式,這種方式可以讓用戶提前預見app并且加速了用戶和程序間的交互。 蘋果官方是這么定義快捷操作項的。

以上是根據蘋果官方文檔進行的翻譯,通篇下來,能看出蘋果是一直在夸這個quick actions。功能雖好,但再好的功能也是需要我們程序員來一步一步集成的。接下來主要介紹集成這個quick actions功能。

Home Screen Quick Actions集成

Home Screen Quick Actions分為兩種:static quick actions和dynamic quick actions。iOS9 SDK提供了一組相關的API,能夠讓我們快速集成static(靜態的)或者dynamic(動態的)quick actions。
那么什么是static quick actions,什么又是dynamic quick actions?

集成靜態和動態的快捷操作項.png
  • 定義靜態快捷操作需要在app的Info.plist文件中配置UIApplicationShortcutItems這個Key,UIApplicationShortcutItems這個Key對應的是一個數組。到此為止,我猜測,這個數組內應該放置的是一個字典, 每個字典又對應著一個quick action。這個猜測后面進行驗證。
  • 定義一個動態快捷操作需要用到“UIApplicationShortcutItem”類和相關的API創建UIApplicationShortcutItem對象(所謂API也就是蘋果官方SDK提供的一些可供我們調用或重寫的方法)。然后需要用UIApplication的sharedApplication方法獲取UIApplication對象,然后用UIApplication對象的“shortcutItems”屬性,當然shortcutItems是UIApplication的一個新屬性。

注意:兩種定義快捷操作項的方式都能顯示兩行文本和一個可選的圖標。
注意:quick actions最多顯示4項。也就是說,無論是靜態還是動態,這兩種定義快捷操作項的方式最多顯示四個快捷操作項。

static quick actions集成

無論是集成動態的還是靜態的Home Screen Quick Actions都需要先來了解下UIApplicationShortcutItems。


UIApplicationShortcutItems.png

Info.plist中的UIApplicationShortcutItems指app的“static quick actions”,也就是指那些靜態快捷操作項。UIApplicationShortcutItems這個key對應著一個字典數組,每一個字典代表一個快捷操作項。這個字典描述了這個快捷操作項的詳細信息。這個字典內可以包括快捷操作項的title(標題)、type(類型)、icon(圖標)、userInfo(用戶信息)等。

我們可以用這個數組字典為我們的app指定靜態快捷操作項。當用戶在支持3D Touch的設備上按壓主屏幕上的app圖標時候,顯示在主屏幕上的快捷操作項的個數是由系統決定的。

我們在Info.plist文件中定義的靜態快捷操作項的順序(數組中字典的先后順序)也就是靜態快捷操作項在屏幕上顯示的順序(默認第一個顯示在最下面,也就是這些快捷操作項在屏幕上自下向上排列)。
系統會優先加載Info.plist中配置的快捷操作項,只有在Info.plist中定義的靜態快捷操作項不夠4個的時候,才會去加載動態快捷操作項。

上面已經說過“UIApplicationShortcutItems這個key對應著一個字典數組,每一個字典代表一個快捷操作項。”但是,這個字典數組最多只能容納4個字典,也就是說,當我們按壓主屏幕上的app圖標時,最多會看到4個快捷操作項。確切的說,是系統默認最多顯示4個,而不是字典的容量為4,你可以給這個字典數組賦值5個字典,但是最后只會顯示前4個字典配置的快捷操作項。


如下屏幕截圖顯示了如何在Info.plist文件中定義兩個靜態快捷操作項:


官方文檔提供的截屏.png

如下代碼是上面Info.plist中兩個靜態快捷操作項的源碼:

<key>UIApplicationShortcutItems</key>
    <array>
        <dict>
            <key>UIApplicationShortcutItemIconFile</key>
            <string>open-favorites</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>Favorites</string>
            <key>UIApplicationShortcutItemType</key>
            <string>com.mycompany.myapp.openfavorites</string>
            <key>UIApplicationShortcutItemUserInfo</key>
            <dict>
                <key>key1</key>
                <string>value1</string>
            </dict>
        </dict>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeCompose</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>New Message</string>
            <key>UIApplicationShortcutItemType</key>
            <string>com.mycompany.myapp.newmessage</string>
            <key>UIApplicationShortcutItemUserInfo</key>
            <dict>
                <key>key2</key>
                <string>value2</string>
            </dict>
        </dict>
    </array>
key Description
UIApplicationShortcutItemType (required) A required string delivered to your app when the user invokes the corresponding quick action. Your app can use this string to classify quick actions into types, and then to disambiguate among action types it receives. You don’t need to register your quick action types.
UIApplicationShortcutItemTitle (required) A string displayed to the user on the Home screen as the name of the quick action.If the title fits on one line, the system displays it as a single line quick action item. If the title is too long for one line and you have not specified a UIApplicationShortcutItemSubtitle string, the system displays the title on two lines.You can, optionally, internationalize a quick action title by employing your app’s InfoPlist.strings file. For guidance on how to do this, read Localizing Property List Values.
UIApplicationShortcutItemSubtitle An optional string that is displayed to the user on the Home screen, immediately below the corresponding title string.If you specify a subtitle for a quick action, the system displays the quick action title on a single line (perhaps just a portion of the title, followed by ellipsis character), no matter how long the title is.You can, optionally, internationalize a quick action subtitle by employing your app’s InfoPlist.strings file. For guidance on how to do this, read Localizing Property List Values.
UIApplicationShortcutItemIconType An optional string specifying the type of an icon from the system-provided library; see the UIApplicationShortcutIconType enumeration in UIApplicationShortcutIcon Class Reference. The icon is displayed in the set of quick actions for your app, along with the quick action's title, on the Home screen.
UIApplicationShortcutItemIconFile An optional string specifying an icon image to use from the app’s bundle, or the name of an image in an asset catalog. The icon is displayed before quick action title on the Home screen.Icons should be square, single color, and 35x35 points, as shown in these template files and as described in iOS Human Interface Guidelines.If you specify this key, the system ignores the UIApplicationShortcutItemIconType key.
UIApplicationShortcutItemUserInfo An optional, app-defined dictionary. One use for this dictionary is to provide app version information, as described in the “App Launch and App Update Considerations for Quick Actions” section of the overview in UIApplicationShortcutItem Class Reference.
key 描述
UIApplicationShortcutItemType (required) 這是一個必須設置的參數,而不是可選的,且這個參數是一個字符串(NSString)類型。當用戶觸發相應的快捷操作項時,這個參數會被傳遞給應用程序,應用程序可以根據這個字符串(type)來對快捷操作項進行分類,然后在接收的類型中進行區分確定被處觸發的快捷操作項的類型。而我們不需要注冊快捷操作項的類型。簡單的說,這個type字符串標記了快捷操作項的類型,當觸發快捷操作項時,系統可以根據其類型來進行判斷用戶當前觸發了哪一個快捷操作項,進而進行相應界面的跳轉。
UIApplicationShortcutItemTitle (required) 這是一個必須設置的參數,而不是可選的,且這個參數是一個字符串(NSString)類型會被顯示在屏幕上。這是快捷操作項的標題。如果標題能夠自適應一行,那么系統就以一行的方式顯示這個快速啟動項。如果這個標題太長導致一行根本容不下且我們沒有指定UIApplicationShortcutItemSubtitle(快速啟動項的子標題),那么系統就以兩行的方式展示這個快速啟動項的標題。當然,我們也可以根據實際情況使用app InfoPlist.string文件國際化快速啟動項的標題。如果想知道怎么操作,請參考閱讀 Localizing Property List Values。
UIApplicationShortcutItemSubtitle 這是一個可選設置的參數,而不是必選的,且這個參數是一個字符串(NSString)類型。這是快捷操作項的子標題,一旦設置會被顯示在屏幕上(前提是要按壓app圖標),它會展示在對應的標題下方。如果我們為一個快捷操作項指定了子標題,那么系統就會以一行的形式顯示子標題(也許僅僅是標題的一部分,后面跟著省略號)無論標題有多長。當然,我們也可以根據實際情況使用app InfoPlist.string文件國際化快速啟動項的標題。如果想知道怎么操作,請參考閱讀 Localizing Property List Values。
UIApplicationShortcutItemIconType 這是一個可選設置的參數,而不是必選的,且這個參數是一個字符串(NSString)類型。他提供了一些默認的圖標類型供我們使用。這是一個枚舉值,可以在UIApplicationShortcutIcon類中查看 UIApplicationShortcutIconType的枚舉值。 設置的圖標會被現實在標題后面。
UIApplicationShortcutItemIconFile 這是一個可選設置的參數,而不是必選的,且這個參數是一個字符串(NSString)類型。他代表app’s bundle中某個圖標的路徑或者代表image asset catalog中某個圖片的名字。這個圖標會在標題顯示之前預先顯示出來。圖標應該是正方形,35 * 35點的(注意:iOS開發中講究的是點,而不是像素,在非retina屏幕上,1點 == 1像素,但是在retina屏幕上就不一定)。如果設置了這個key,那么系統就會忽略UIApplicationShortcutItemIconType。
UIApplicationShortcutItemUserInfo 這是一個可選設置的參數,而不是必選的,且這個參數是一個字典(NSDictionary)類型。這個字典可以包含應用程序的版本信息、程序啟動或者更新注意事項。具體的可以參考UIApplicationShortcutItem Class Reference

注意:這些key只支持iOS9系統以及更高版本系統。

下面是我自己利用Info.plist文件集成的static quick actions。
如下圖,是Info.plist中的配置:

Info.plist中添加名為UIApplicationShortCutItems的Key.png
// delegate.m文件中實現以下方法
// 作用:點擊3Dtouch菜單上的某個item跳轉到指定界面
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
    if ([shortcutItem.type isEqualToString:@"0"]) {
        NSLog(@"點擊了哈哈");
    } else if ([shortcutItem.type isEqualToString:@"1"]) {
        NSLog(@"點擊了呵呵");
    } else if ([shortcutItem.type isEqualToString:@"2"]) {
        NSLog(@"點擊了呦呦");
    } else {
        NSLog(@"點擊了么么");
        
    }
}

如下是運行效果圖:
注意:3D Touch的調試只能在真機上,不能夠在模擬器上。

Home screen quick action截圖.png

dynamic quick actions的集成

與集成靜態的快捷操作項不同的是,集成動態的快捷操作項不需要配置info.plist文件。取而代之的是代碼配置,接下來,我就要集成下圖所示的動態快捷操作項。


動態快捷操作項屏幕截圖.png

步驟:

  • 在程序啟動完成回調的didFinishLaunchingWithOptions:方法中實現以下代碼
  • 在performActionForShortcutItem:方法中針對于點擊不同的快捷操作項而進行不同的操作。
  • 完畢
// 以下是全部代碼,且都是在delegate.m文件中實現的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
    //使用系統提供的ShortcutIcon類型
    UIApplicationShortcutIcon *addOpportunityIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeAdd];
    UIApplicationShortcutItem *addOpportunityItem = [[UIApplicationShortcutItem alloc] initWithType:@"addOpportunity" localizedTitle:@"添加機會" localizedSubtitle:nil icon:addOpportunityIcon userInfo:nil];
    
    UIApplicationShortcutIcon *bookMarkIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeCompose];
    UIApplicationShortcutItem *bookMarkItem = [[UIApplicationShortcutItem alloc] initWithType:@"bookMark" localizedTitle:@"添加小記" localizedSubtitle:nil icon:bookMarkIcon userInfo:nil];
    
    UIApplicationShortcutIcon *searchGuestIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
    UIApplicationShortcutItem *searchGuestItem = [[UIApplicationShortcutItem alloc] initWithType:@"searchGuest" localizedTitle:@"搜索客戶" localizedSubtitle:@"通過關鍵字搜索感興趣客戶" icon:searchGuestIcon userInfo:nil];
    
    //自定義ShortcutIcon
    // 如果設置了自定義的icon,那么系統自帶的就不生效
    UIApplicationShortcutIcon *myGuestIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"myGuestImage"];
    
    UIApplicationShortcutItem *myGuestItem = [[UIApplicationShortcutItem alloc] initWithType:@"myGuest" localizedTitle:@"我的客戶" localizedSubtitle:nil icon:myGuestIcon userInfo:nil];
    
    [UIApplication sharedApplication].shortcutItems = @[addOpportunityItem,bookMarkItem,searchGuestItem,myGuestItem];
    
    return YES;
}

// 作用:點擊快速啟動項菜單上的某個快速啟動項跳轉到指定界面
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
// 方式一:type
    if ([shortcutItem.type isEqualToString:@"addOpportunity"]) {
        NSLog(@"點擊了添加機會item");
    } else if ([shortcutItem.type isEqualToString:@"bookMark"]) {
        NSLog(@"點擊了添加小記item");
    } else if ([shortcutItem.type isEqualToString:@"myGuest"]) {
        NSLog(@"點擊了我的客戶item");
    } else {
        NSLog(@"點擊了搜索客戶item");

    }

    // 方式二:title或者subtitle
    if ([shortcutItem.localizedTitle isEqualToString:@"添加機會"]) {
        NSLog(@"點擊了添加機會item");
    } else if ([shortcutItem.localizedTitle isEqualToString:@"添加小記"]) {
        NSLog(@"點擊了添加小記item");
    } else if ([shortcutItem.localizedTitle isEqualToString:@"我的客戶"]) {
        NSLog(@"點擊了我的客戶item");
    } else {
        NSLog(@"點擊了搜索客戶item");
    }
}

注意:需要強調的是,快捷操作項最多定義4個。就像蘋果官方的比喻一樣:這里一共就有4個插槽,也就是最多顯示4個快捷操作項。程序優先加載Info.plist文件中定義的靜態快捷操作項,如果Info.plist中的靜態快捷操作項不足4個才會去加載代碼定義的動態快捷操作項(前提是代碼中定義了動態快捷操作項)來補充剩余的插槽。

(二)app內的3D Touch - Peek and Pop

Snip20160418_9.png

啟動并進入應用程序,在應用程序內可以響應用戶不同的按壓力度,隨著用戶按壓力度的增大,程序會一次進入三個交互階段:

  • 1.表明內容可以被預覽
  • 2.展示預覽視圖-也就是我們所熟知的peek-并且帶有可以配置的peek快捷操作項
  • 3.在預覽視圖上出現可選的導航視圖-也即是我們所熟知的pop

app內的3D Touch主要分為peek可用性檢測、Peek 、pop。peek 可用性主要是證明3D Touch的可用性。peek階段只提供部分內容供用戶預覽,pop階段顯示整屏內容供用戶瀏覽。從時間角度上看,peek階段在前,pop階段在后;從按壓力度角度上看,peek所需的按壓力度較小,pop階段所需的按壓力度較大。
拿新浪微博app上的圖片微博為例,在iPhone6s以及iPhone6s Plus上,如果開啟了3D Touch,當我們按壓某條微博的某張圖片時,當前被按壓這張圖片之外的所有內容都被系統自動模糊處理,以突出顯示當前按壓選中的內容,這代表peek是可用的,也就是說,輕輕按壓圖片以突出顯示的這個過程是驗證peek是否可用的過程。在此基礎上不要松手,稍稍用力按壓圖片,就會在屏幕上彈出來一個圓角矩形的視圖顯示這張圖片,這個階段叫做peek階段,也就是預覽階段。在此基礎上不要松手,繼續用力按壓圖片,就會在屏幕上彈出一個鋪滿整屏的視圖顯示這張圖片,這個階段叫做pop階段。下面分別用四張圖來說明不同階段(不同按壓力度)所呈現的界面效果。

階段一:peek可用性

表明peek可用性.png

上圖的效果蘋果官方文檔給出了如下解釋:

peek可用性檢測.png

表明peek的可用性
輕按時,周圍內容出現模糊效果,告訴用戶可以預覽額外的內容—也就是peek是可用的。


階段二:peek-預覽部分內容

peek階段.png
peek.png

稍微用力按壓視圖,然后視圖會切換到一個peekView,這個peekView通常需要配置展示額外的內容。
如果用戶手指抬起停止按壓視圖,peekView會自動消失并且app會恢復到交互開始之前的狀態。


pop-全屏瀏覽內容

pop階段.png

如果在peek階段,用戶手指沒有抬起,而是繼續用力按壓視圖以觸發導航功能,用系統提供的pop轉場到另一個視圖:這個視圖就是peek階段peekView預覽的視圖——popView。popView會占滿整個屏幕,只在導航條的左邊顯示一個返回按鈕。


peek quick action

peek階段上移peekView進行快捷操作.png

peek階段,如果用戶手指不離開屏幕,而是在屏幕上向上滑動,系統就會給用戶顯示事先已經關聯好的快捷操作項。每一個peek的快捷操作項都是app內的一個深度鏈接。當快捷操作項被顯示出來的時候,用戶可以抬起手指停止觸摸屏幕,并且此時peek View仍然會顯示在屏幕上不會消失。這樣就允許用戶點擊快捷操作項,進而調用相關的深度鏈接。

peek和pop功能的實現

如果要實現Peek和Pop需要進行下面兩步配置:
1>在運行時檢查3D Touch的可用性。
2>遵守UIViewControllerPreviewingDelegate協議,實現協議中的以下兩個代理方法。
此處我以按壓tableView內的cell為例講解peek和pop的集成。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *cellID = @"cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 
    if (!cell) { 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 
    } 
    cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row]; 
    // 檢測3D Touch可用性
    if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { 
    [self registerForPreviewingWithDelegate:self sourceView:cell]; 
    } 
    return cell;
}
/**
   *previewingContext:被預覽的視圖控制器的內容對象
   *location:源視圖的左邊系上的觸摸點的坐標位置
   *調用時間:進入peek預覽階段時會調用這個方法。
   *作用:返回一個配置好的以供預覽的視圖控制器。
   *需要的操作:把源視圖坐標系上的點轉換為當前控制器的視圖上的點。
   *返回一個控制器。
*/
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
              viewControllerForLocation:(CGPoint)location
{
     //將觸摸點的坐標轉化為tableView坐標系上的坐標點
    CGFloat locationBaseTableView = [self.tableView convertPoint:location fromView:[previewingContext sourceView]]; 
    //根據觸摸點獲取觸摸的cell的indexPath 
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
    // 根據indexPath對被peek的控制器進行配置 
    WSTableViewController *peekViewController =[[WSTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
    peekViewController.model = self.models[indexPath.row];
    return peekViewController;
}
/** 
   *previewingContext:被預覽的視圖控制器的內容對象 == 上面代理方法中的previewingContext,內存中是同一個對象
   *viewControllerToCommit:被present(pop)的視圖控制器 == 上面代理方法中返回的控制器,內存中是同一個對象
   *調用時間:pop階段調用這個方法
   *作用:配置并且present一個commit(pop)視圖控制器。
*/
- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
     commitViewController:(UIViewController *)viewControllerToCommit
{
    [self.navigationController pushViewController:viewControllerToCommit animated:YES];
}

另外,如果希望實現peek Quick Actions功能需要在被peek的控制器內實現以下方法,在此例中,也就是需要在WSTableViewController中實現這個方法。

- (NSArray<id<UIPreviewActionItem>> *)previewActionItems{

    UIPreviewAction *like = [UIPreviewAction actionWithTitle:@"喜歡" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    NSLog(@"點擊了喜歡");
}];

UIPreviewAction *comment = [UIPreviewAction actionWithTitle:@"評論" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    NSLog(@"點擊了評論");
}];

UIPreviewAction *complaint = [UIPreviewAction actionWithTitle:@"投訴" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    NSLog(@"點擊了投訴");
}];

    return @[like,comment,complaint];

名詞解釋

3D Touch可用性檢查

想要檢查某個設備運行時是否支持3F Touch,需要用到NSObject對象的trait environment的trait collection的forceTouchCapability屬性。用戶在app運行過程中可以關閉3D Touch,這需要借助于實現如下代理方法:traitCollectionDidChange:(UITraitCollection *)previousTraitCollection。可以看出,這個方法的參數就是UITraitCollection類型。那么這個方法什么時候調用呢?蘋果官方是這么說的:
Called when the iOS interface environment changes.也就是說,當界面環境發生變化的時候回調用這個方法。
那么什么是界面環境呢?又怎么理解界面環境的變化呢?

UITraitEnvironment協議
/*! Trait environments expose a trait collection that describes their environment. */
@protocol UITraitEnvironment <NSObject>
@property (nonatomic, readonly) UITraitCollection *traitCollection NS_AVAILABLE_IOS(8_0);

/*! To be overridden as needed to provide custom behavior when the environment's traits change. */
- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection NS_AVAILABLE_IOS(8_0);
@end

UITraitEnvironment詳解.png

大意如下:
UITraitEnvironment是聲明在UIKit框架中的一個協議,繼承自NSObject協議,起始于iOS8.0。界面環境包括水平和垂直方向的size class、呈現比例、以及用戶界面語言(見文章末尾)。可以通過UITraitEnvironment協議來使用UITraitEnvironment。也就是說,iPhone和iPad設備的橫屏和豎屏狀態、縮放比例等都是界面環境。采用了UITraitEnvironment協議的類有:UIScreen、UIWindow、UIViewController、UIPresentationController和UIView。
一個采用了UITraitEnvironment協議的對象通過使用traitCollection屬性來訪問環境特征。同時這個協議也提供了一個可以重寫的方法,當界面環境發生改變時以供系統調用。這個方法就是我們上面所說的 - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection。實現這個方法可以提高iOS app的自適應性。

訪問trait collection
Snip20160415_3.png

trait collection 是視圖控制器(UIViewController類或者其子類的實例)或者視圖(UIView類或者其子類的實例)的屬性,因為UIViewController和UIView默認遵守了UITraitEnvironment協議,trait collection正是UITraitEnvironment協議中聲明的一個屬性。

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusEnvironment>
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment>
響應界面環境的變化
響應界面環境的變化.png

大意如下:
調用時間:當該系統界面環境發生變化的時候會調用代理方法 - traitCollectionDidChange:
參數說明:其UITraitCollection類型的參數previousTraitCollection是界面環境發生變化之前的對象。
詳細說明:
當iOS界面環境發生變化時,系統會調用這個代理方法。根據app實際需要,可以在視圖控制器或者視圖中實現這個代理方法,以響應一些改變。比如:當iPhone從豎屏旋轉到橫屏時,你也許需要調整控制器的子視圖的布局,我們可以在這個方法中進行操作。默認這個方法的實現是空實現,這也就說明,我們需要重寫這個代理方法的實現。
重寫這個代理方法時,需要先調用父類的這個方法的默認實現,確保界面上的元素的視圖層次結構先得到調整。使用如下類似的代碼:

- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
    [super traitCollectionDidChange: previousTraitCollection];
    if ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)
        || self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass)) {
        // your custom implementation here
    }
}

注意:這個代理方法只有在iOS8.0及其之后的系統上調用。

實現協議方法

UIUserInterfaceIdiom - 用戶界面語言

userInterfaceIdiom是UITraitCollection的屬性。其屬于UIUserInterfaceIdiom類型,UIUserInterfaceIdiom非對象性,其值是5個枚舉值。分別如下:

typedef NS_ENUM(NSInteger, UIUserInterfaceIdiom) {
    UIUserInterfaceIdiomUnspecified = -1,
    UIUserInterfaceIdiomPhone NS_ENUM_AVAILABLE_IOS(3_2), // iPhone and iPod touch style UI
    UIUserInterfaceIdiomPad NS_ENUM_AVAILABLE_IOS(3_2), // iPad style UI
    UIUserInterfaceIdiomTV NS_ENUM_AVAILABLE_IOS(9_0), // Apple TV style UI
    UIUserInterfaceIdiomCarPlay NS_ENUM_AVAILABLE_IOS(9_0), // CarPlay style UI
};

文/VV木公子(簡書作者)
PS:如非特別說明,所有文章均為原創作品,著作權歸作者所有,轉載轉載請聯系作者獲得授權,并注明出處,所有打賞均歸本人所有!
如果您是iOS開發者或者,或者對本篇文章感興趣,請關注本人,后續會更新更多相關文章!敬請期待!

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

推薦閱讀更多精彩內容

  • 前言 關于這篇文章 由于iPhone 6S發布不到一年的時間,很多新特性、新技術還未普遍,不管是3D Touch的...
    Tangentw閱讀 4,517評論 8 18
  • iOS 9之后提供以下幾個3D Touch API: 1.Home screen quick action 主屏幕...
    DeadRoach閱讀 770評論 1 1
  • 專著:http://www.lxweimin.com/p/3443a3b27b2d 1.簡單的介紹一下3D Touc...
    violafa閱讀 1,021評論 1 0
  • 1.簡單的介紹一下3D Touch 3D Touch的觸控技術,被蘋果稱為新一代多點觸控技術。其實,就是此前在Ap...
    Camille_chen閱讀 12,069評論 19 33
  • 3D Touch 給iOS9的用戶一個維度的交互。在支持的設備上,人們能夠在主屏幕界面通過按壓應用程序圖標,快速的...
    Jack__yang閱讀 602評論 0 2