iOSiOS开发iOS开发周刊

iOS-3D Touch 特性 & API 详解

2016-12-10  本文已影响1232人  Tangentw

前言

关于这篇文章

由于iPhone 6S发布不到一年的时间,很多新特性、新技术还未普遍,不管是3D Touch的使用还是开发,对其有相关了解的人并不多。前几天偶然接触了3D Touch的某个API接口,为了满足好奇心,于是我就系统地去了解了这个苹果的新技术。查阅了相关的官方文档,敲了些Demo,并编写了这篇文章,作为总结。

从Force Touch到3D Touch

使用过新版Mac Book或Apple Watch的朋友应该对Force Touch这个词汇并不陌生,这是苹果针对设备触控操作的一项新的技术,将传统的用户触控点击操作扩展化,加上了按压操作,设备可根据用户手指在屏幕上的按压力度来进行相应的响应。在新版Mac Book以及Apple Watch中,我们可以通过使用不同的力度按压触控板或触控屏来调出更多的控制选项,人机交互性非常高。
3D TouchForce Touch延伸出的新一代技术,它现在应用于装配了iOS9以上操作系统的iPhone 6S上,致力于向iPhone用户提供更加高质量的交互体验,将操作方式扩展至三维层面。

本篇文章分别针对3D Touch的特性以及开发API进行讲解。

特性 — 3D Touch On iPhone

Home Screen Quick Actions - 主页屏幕快速操作

在手机的主页上,假如你用手指轻轻按压某个应用的图标,图标的背后出现了一个半透明的矩形,这就说明了这款应用支持Home Screen Quick Actions(主页屏幕快速操作)。这时,我们保持手指按压,并加大力度,你就会发现这个应用图标的周围都变模糊了,一个小巧的选项栏菜单在你眼前弹出。


这个选项栏菜单每个选项视图最多允许有两行文本(主文本、次文本)以及一个图标(可要可不要)。另外,图标的位置是不定的,它会在文本的左边或者右边进行布局,具体放置于哪一边,则根据应用图标的水平位置而定,不过这些布局系统都已经帮我们处理好了。
当我们点击某一个选项,应用程序就会运行起来,并且执行相应的操作,相比于以往用户要先进入应用程序才能再进行操作,使用Home Screen Quick Actions则更加的便利。

Peek and Pop - 预览和查看详情

Peek - 预览

在传统的手机用户操作中,当我们在应用里看到某张缩略图、某个网址链接或者某个列表Item时,若我们想查看详细的信息,比如想看缩略图对应的大图、网址链接对应的网页、Item对应的详情页面,一般会用手指对屏幕进行点击操作,从而让应用的页面进行跳转。新特性Peek则大大提高了这类型操作的用户体验。

Peek演示
上方效果图所展示的是对一个缩略图片进行Peek操作,当我们用手指轻压图片时,图片的周围迅速模糊,这说明你所按压的地方支持Peek操作。按压力度稍微增大,预览图就出来了,这就是Peek(预览)功能。当我们将手指从屏幕上抬起时,预览图就会消失,界面也就恢复回来。
有了Peek的操作简化,当我们想预览某些详情时,只需按压屏幕,手机即可弹出详情页面;移开手指,界面恢复原状,这样就使得用户不必进入一个新的页面浏览详情,然后再点击返回按钮回到原来视图了。

Peek quick actions - 预览中的快速操作

上方已经说到,当我们的手指按压需要预览的屏幕的区域,预览的视图就会出现。现在,继续保持手指的按压状态,然后再向上方滑动,就会在预览视图的下方滑出一个选项栏菜单。



当菜单完全显示出来后,你可以松开按压在屏幕上的手指,然后点击相应的选项来执行操作,就像上方效果图一样,可以复制、分享、点赞、删除等等。


Pop - 查看详情

在能出现Peek预览视图的手指按压力度基础上,用户再使把劲加大点力度,就能进入相关详情页面,这个就是Pop。事实上,Pop所进入的详情页面跟用户用手指轻点后所跳转出来的页面是一样的,所以,用户如果想直接进入详情页面,不需要预览,可以直接轻点屏幕指定区域即可;而在预览中,用户想了解得更多,可以再加大按压力度进入详情页面。

3D Touch 还能做些什么?

3D Touch能完成的功能非常多,你可以利用它来制作一个绘画板,根据手指在屏幕上的压力大小来模拟画笔的粗细,你也可以做一款精美的手游,通过手指的按压力度来反馈不同的游戏操作......
总之,3D Touch非常的强大,潜力无限。😁

开发 — 3D Touch API

下面,我会通过苹果提供的3D TouchAPI就之前所提及到的各个3D Touch特性进行开发实现的分析。所有的交互跟视图布局我都是使用纯代码去实现。

判断设备是否支持3D Touch

我们在为应用添加3D Touch功能时,有必要做设备是否支持或开启3D Touch的判断,考虑到用户使用的手机型号比iPhone 6s低,或者用户自己已经手动关闭了3D Touch功能,所以在编写代码的时候,需要获取或监听当前设备针对于3D Touch的可用性状态,以便在后面做出判断。

我们获取当前设备针对于3D Touch的可用性状态,可以使用协议UITraitEnvironment

UITraitEnvironment的结构

UITraitEnvironment中包含一个属性以及一个方法:

不管是属性还是方法,其目的都是让我们能够获取到当前的设备特征环境集合,只不过使用方法来获取比较动态,可以时刻监听变化。

获取到的环境集合为UITraitCollection类型,这个类里面包含属性forceTouchCapability,是一个UIForceTouchCapability枚举类型,有三个case,分别是Unknown(未知的)、Unavailable(不可用的)、Available(可用的),根据相应的forceTouchCapability值,我们就能知道当前设备对3D Touch的可用性状态。


如何使用UITraitEnvironment

其实,很多我们常用的类都已经实现了UITraitEnvironment协议,比如说UIViewUIViewController等等,我们可以直接从他们的内部中获得traitCollection属性然后进行判断:

if self.traitCollection.forceTouchCapability == .Available {
    //  TODO:  加入实现3D Touch的代码
}

如果我们想做到实时监听状态的变化,比如软件在运行的时候用户突然关闭了3D Touch,我们可以自己去实现UITraitEnvironment协议,实现其中的监听方法,在这里我就不演示代码了。

Home Screen Quick Actions

实现Home Screen Quick Actions选项栏菜单

实现Home Screen Quick Actions有两种方法,分别是static(静态)实现以及dynamic(动态)实现,它们的实现主要都是依靠UIApplicationShortcutItem这个类来进行。


监听Home Screen Quick Actions选项的选择并进行处理

监听Home Screen Quick Actions选项的点击选择,我们要在AppDelegate中实现方法application(_:, performActionForShortcutItem:, completionHandler:),判断用户选择的是哪一个选项,然后进行相应的操作:

    func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
        switch shortcutItem.type {
        case ShortcutItemType.Home.rawValue:
            print("选择了主页选项")
        case ShortcutItemType.Share.rawValue:
            print("选择了分享选项")
        default:
            print("选择了其他选项")
        }
        completionHandler(true)
    }

在方法的最后记得调用completionHandler闭包,把是否处理完成的布尔值传进去,如成功处理完,传true,失败,则传false


注意:关于方法application(_:, performActionForShortcutItem:, completionHandler:),苹果官方文档给出了使用的相关注意事项:

这个方法会在你选择了某个Home Screen Quick Actions选项时调用,但是前提条件是AppDelegate中的application(_:,willFinishLaunchingWithOptions:)方法以及application(_:didFinishLaunchingWithOptions)方法都要返回true真值。苹果建议这个方法是在应用已经启动了、在后台工作的时候才去监听Home Screen Quick Actions选项的选择,若我们在应用程序还未启动的时候使用Home Screen Quick Actions选择某个选项,我们就不应该使用这个方法来进行监听,应当在application(_:,willFinishLaunchingWithOptions:)方法或者application(_:didFinishLaunchingWithOptions)方法里进行操作,最后返回false,使得AppDelegate不会再去调用application(_:, performActionForShortcutItem:, completionHandler:)这个方法。


那么,当Home Screen Quick Actions选项在应用还未启动时被选择了,我们应该如何在AppDelegate中的application(_:,willFinishLaunchingWithOptions:)方法或application(_:didFinishLaunchingWithOptions)方法里监听Home Screen Quick Actions选项的选择以及做出相应的操作呢?这里我们可以从这两个方法的launchOptions参数中获取到对应的shortcutItem

let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem

下面我就重写application(_:didFinishLaunchingWithOptions)来演示一下:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    
        //  TODO: 应用的初始化代码,包括UIWindow以及UIViewController等等的配置...
        
        var performActionForShortcutItemWhenAppLaunch = false
        //  判断应用的启动是否是因为用户选择了Home Screen Quick Actions选项
        if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
            performActionForShortcutItemWhenAppLaunch = true
            print("选择了\(shortcutItem.type)类的选项")
            //  TODO: 相应的Handle操作
        }
        
        return !performActionForShortcutItemWhenAppLaunch
    }

如上方代码所示,我在前面就定义了以布尔型变量performActionForShortcutItemWhenAppLaunch,默认值为false,然后再从launchOptions中取出shortcutItem,如果shortcutItem为空,则说明应用的启用是由于用户点击了应用的图标,而不是通过Home Screen Quick Actions;如果shortcutItem不为空,则说明用户是用过点击shortcutItem对应的选项来启动应用的,这时候我将true值赋值给了performActionForShortcutItemWhenAppLaunch。在方法的最后,我通过返回performActionForShortcutItemWhenAppLaunch的布尔相反值,来让应用避免调用application(_:, performActionForShortcutItem:, completionHandler:)方法。

Peek And Pop

Peek And PopHome Screen Quick Actions来说更为复杂,下面我就PeekPeek quick actionsPop的实现进行分析。

Peek & Pop

实现PeekPop首先我们要关注协议UIViewControllerPreviewingDelegate,它有两个需要我们去实现的方法:

下面就是参考的代码:

//  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)
    }
}

当我们实现了UIViewControllerPreviewingDelegate协议后,我们就可以为视图控制器注册Peek预览了,不过,在注册的时候一定要先判断好设备的3D Touch是否可用:

//  MARK: - Setup 3D Touch
        if self.traitCollection.forceTouchCapability == .Available {
            self.registerForPreviewingWithDelegate(self, sourceView: self.tableView)
        } else {
            print("3D Touch 不可用!")
        }

这里我们使用UIViewController中的方法registerForPreviewingWithDelegate(_:, sourceView:)来进行Peek注册,方法第一个传入的参数就是实现了UIViewControllerPreviewingDelegate的实例,第二个参数就是手指按压的监听视图。

Peek quick actions

Peek的快速操作是在详情视图控制器中实现的,我们只需重写这个视图控制器的previewActionItems() -> [UIPreviewActionItem]方法,返回一个数组即可。
UIPreviewActionItem为一个协议,一般我们需要创建的是UIPreviewAction或者UIPreviewActionGroup实例。

这里提供参考代码:

    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
    }

应用运行的效果图如下:


3D Touch的其他API

苹果为3D Touch提供了Force Properties(按压强度属性),我们可以在UITouch类中找到属性forcemaximumPossibleForce,分别代表瞬时按压力度以及设备最大可能达到的按压力度,我们可以利用这两个属性完成很多有趣的交互,这就要看大家的想象力了,在这里我就举一个简单的小例子:

我编写了一个UIView的子类,它的大小由我们手指按压它的力度去决定,这是它的内部代码:

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)
    }
}

现在我在一个视图控制器中创建它的一个实例,并添加到控制器的视图中,然后随便设置一个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)
    }

}

于是乎,一个利用简单的利用forcemaximumPossibleForce属性的小例子就做出来了。我们运行一下看看效果:

总结

这篇文章向大家介绍了3D Touch的特性,并详细讲解了有关于3D Touch开发API的使用。相关代码我已经发布到个人的GitHub上:Tan3DTouch
感谢大家的阅读,在这里也祝大家夏日愉快!

参考资料

苹果官方文档:
Adopting 3D Touch on iPhone
iOS Human Interface Guidelines: 3D Touch

苹果官方参考项目:
ApplicationShortcuts: Using UIApplicationShortcutItem
ViewControllerPreviews: Using the UIViewController previewing APIs

上一篇 下一篇

猜你喜欢

热点阅读