3D Touch

前言

3D Touch是一种立体触控技术,被苹果称为新一代多点触控技术,是在Apple Watch上采用的Force Touch,屏幕可感应不同的感压力度触控。3D Touch,苹果iPhone 6s的新功能,系统版本需要iOS 9.0+ ,有Peek Pop 两种新手种势。

2015年9月10日,苹果在新品发布会上宣布了3D-Touch功能。 2016年6月13日,苹果开发者大会WWDC在旧金山召开,会议宣布可以在待机画面用3D Touch操作通知。

现在,许多安卓手机也用上了3D Touch。

– 摘自百度百科


3D Touch四大功能模块

  • Home Screen Quick Actions (主屏幕快捷入口)
  • peek And pop (预览和弹出 展示框)
  • Force Properties (按压力度属性)
  • Web View peek and pop API (HTML链接预览功能)

设备支持判断

  • 所有遵守UITraitEnvironment(特征环境)协议的类,都会有UITraitCollection(特征集合类)的属性,都有forceTouchCapability(力的触觉能力)属性,
    而UIResponder就遵守了UITraitEnvironment协议,所以所有继承于UIResponder的类都会使用3DTouch.包括UIView也可以使用

  • forceTouchCapability是枚举型

  • 代码示例

    示例代码* OC

    1
    2
    3
    4
    5
    6
    // UIForceTouchCapability:  
    typedef NS_ENUM(NSInteger, UIForceTouchCapability) {
    UIForceTouchCapabilityUnknown =0, //3D Touch检测失败
    UIForceTouchCapabilityUnavailable =1, //3D Touch不可用
    UIForceTouchCapabilityAvailable =2 //3D Touch可用
    };
1
2
3
4
5
 // 判断是否支持:
if (self.traitCollection.forceTouchCapability !=UIForceTouchCapabilityAvailable) {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"设备不支持3DTouch" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
}
  • swift
1
2
3
4
5
6
 // UIForceTouchCapability:
public enum UIForceTouchCapability : Int{
case unknown //3D Touch检测失败
case unavailable //3D Touch不可用
case available //3D Touch可用
}
1
2
3
4
5
6
7
8
9
// 判断是否支持:

if traitCollection.forceTouchCapability != .available{

let alert = UIAlertView(title: "提示", message: "设备不支持3DTouch", delegate: nil, cancelButtonTitle: "确定")
alert.show()
return

}

Home Screen Quick Actions开发

简介

通过主屏幕的应用Icon,我们可以用3D Touch呼出一个菜单,进行快速定位应用功能模块相关功能的开发,。如微信、京东的App,本文也将重点介绍这项开发技术。

效果如下:

3D Touch快捷入口标签有两种添加方式:

  1. 一种是静态添加,在工程中的info.plist文件中添加相关项目。通过这种方式添加的标签,在app第一次运行前,就可以唤出这些标签。

  2. 另一种是动态添加,通过代码给App添加快速入口。这种方式添加的标签,第一次运行App前是看不到这些标签的,必须先运行一次App,以后就可以唤出这些标签了。

静态添加快捷入口标签

  1. 静态添加入口标签不需要写代码,只需要在info.plist文件中添加相关功能设置即可。不过因为没有语句提示,所以我们只能一个字母一个字母的去添加,这一点不是很友好。

  2. 示例

  3. 说明

    • 首先是UIApplicationShortcutItems,他是一个数组类型,数组中的每一个元素表示一个入口标签。
    • 每个Item是UIApplicationShortcutItems数组中的一个元素,字典类型。在这个字典中配置各个标签的相关属性。
    • 如图如示

    • 效果如图

    • 注:UIApplicationShortcutItemIconFile:当填写上图片图标的我名字就可以显示出图标。

动态添加快捷入口标签

需要用到的类
  • 如图
代码示例
  • OC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    // UIApplicationShortcutIconType是系统的图片类型,29种类型图片

    UIApplicationShortcutIcon *shareShortcutIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeShare];

    UIApplicationShortcutItem *shareShortcutItem = [[UIApplicationShortcutItem alloc] initWithType:@"" localizedTitle:@"分享" localizedSubtitle:@"来点我啊" icon:shareShortcutIcon userInfo:nil];

    //可以用iconWithTemplateImageName:来自定义图片 shortcut_QR:图片名
    UIApplicationShortcutIcon *payShortcutIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"shortcut_imageName"];

    UIApplicationShortcutItem *payShortcutItem = [[UIApplicationShortcutItem alloc] initWithType:@"" localizedTitle:@"消息" localizedSubtitle:@"不怕不怕" icon:payShortcutIcon userInfo:nil];

    //将实例化的shortcutItem将入到UIApplication的shortcutItems中就完成了。
    [UIApplication sharedApplication].shortcutItems = @[shareShortcutItem, payShortcutItem];
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //分享
    let icon1 = UIApplicationShortcutIcon(type: .share)
    let shareItem = UIApplicationShortcutItem(type: "", localizedTitle: "分享", localizedSubtitle: "来点我", icon: icon1, userInfo: nil)

    //支付 shortcut_imageName:图片名称
    let icon2 = UIApplicationShortcutIcon(templateImageName: "shortcut_imageName")
    let playItem = UIApplicationShortcutItem(type: "", localizedTitle: "支付", localizedSubtitle: "不怕不怕", icon: icon2, userInfo: nil)

    UIApplication.shared.shortcutItems = [shareItem,playItem]
  • UIApplicationShortcutIconType补充说明

    系统一共提供了29种类型样式:

    typedef NS_ENUM(NSInteger, UIApplicationShortcutIconType) {
    UIApplicationShortcutIconTypeCompose,
    UIApplicationShortcutIconTypePlay,
    UIApplicationShortcutIconTypePause,
    UIApplicationShortcutIconTypeAdd,
    UIApplicationShortcutIconTypeLocation,
    UIApplicationShortcutIconTypeSearch,
    UIApplicationShortcutIconTypeShare,

    UIApplicationShortcutIconTypeProhibit, //从这开始,iOS 9.1+才支持
    UIApplicationShortcutIconTypeContact,
    UIApplicationShortcutIconTypeHome,
    UIApplicationShortcutIconTypeMarkLocation,
    UIApplicationShortcutIconTypeFavorite,
    UIApplicationShortcutIconTypeLove,
    UIApplicationShortcutIconTypeCloud,
    UIApplicationShortcutIconTypeInvitation,
    UIApplicationShortcutIconTypeConfirmation,
    UIApplicationShortcutIconTypeMail,
    UIApplicationShortcutIconTypeMessage,
    UIApplicationShortcutIconTypeDate,
    UIApplicationShortcutIconTypeTime,
    UIApplicationShortcutIconTypeCapturePhoto,
    UIApplicationShortcutIconTypeCaptureVideo,
    UIApplicationShortcutIconTypeTask,
    UIApplicationShortcutIconTypeTaskCompleted,
    UIApplicationShortcutIconTypeAlarm,
    UIApplicationShortcutIconTypeBookmark,
    UIApplicationShortcutIconTypeShuffle,
    UIApplicationShortcutIconTypeAudio,
    UIApplicationShortcutIconTypeUpdate
    }

快捷启动后的处理(检测App启动方式)

代理接受

通过快捷入口进入或者启动app,会实现appDelegate的代理方法:

1
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandle

我们可以判断shortcutItem.localizedTitle属性的值来判断是从哪一个标签进入的App。

示例代码
  • OC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       
    - (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {

    //如果是从"分享"这个标签进来的,我们把页面背景颜色修改为黄色,如果是从@"消息"这个标签进来的,我们讲页面背景颜色修改为红色。都不是的话,就直接是白色。
    if ([shortcutItem.localizedTitle isEqualToString:@"分享"]) {
    self.window.rootViewController.view.backgroundColor = [UIColor yellowColor];
    } else if ([shortcutItem.localizedTitle isEqualToString:@"消息"]) {
    self.window.rootViewController.view.backgroundColor = [UIColor redColor];
    }
    }
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {

    if shortcutItem.localizedTitle == "来啊"{
    window?.rootViewController?.view.backgroundColor= #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
    }else if shortcutItem.localizedTitle == "后门" {
    window?.rootViewController?.view.backgroundColor= #colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)
    }

    }

补充说明

  • UIApplicationLaunchOptionsShortcutItemKey

    在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(nullable NSDictionary )launchOptions的方法中 。launchOptions 中有个值为UIApplicationLaunchOptionsShortcutItemKey,这个也是获取选择的shortcutItem的方法。

  • 注意

    1. 快捷入口 无论是启动app,还是将后台运行的app转为前台都会执行代理方法。
    2. 反之,如果快捷入口 是将后台运行的app转为前台都会执行代理方法。是不会走didFinishLaunchingWithOptions:的方法。

    3. 总结:

      我们处理Home Screen Quick Actions功能时最好统一在代理回调的方法里处理。如果在遇到特殊功能,需要判明该功能是启动了整个app时,我们就可以在didFinishLaunchingWithOptions:的时候来判断加以处理!


peek And pop

简介

  • 效果演示:

    1. 进 “邮件” 收件箱列表:

    2. 按住环信发过来的邮件,力度慢慢增加,会pop出详情效果框:

    3. 再使劲达到它的力度,就会等同于你选择了该项,进入详情页面了。

功能的基本思路:

  1. 控制器注册预览协议(UIViewControllerPreviewingDelegate)
  2. 遵守协议并实现其必须实现的方法

示例代码

  • OC

    1. 注册

      1
      2
       // self:为视图控制器, soureView: 为实现3DTouch的视图,如果是单独的一个view的话,则效果只对该view有作用, 那到时的gesture和location都是相对于这个soureView的。
      [self registerForPreviewingWithDelegate:self sourceView:self.view];
2. 遵守代理

1
@interface ViewController ()<UIViewControllerPreviewingDelegate>
  1. 实现方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
       //MARK: — UIViewControllerPreviewingDelegate
    //MARK: -- 返回要显示的控制器
    - (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {

    //初始化要显示的控制器
    UIViewController *childVc = [[UIViewController alloc]init];

    //1.获取手势,我在view上加了一个tableView,这里我是想要获取我们重按的点是点在哪个cell上。
    UIGestureRecognizer *gesture = previewingContext.previewingGestureRecognizerForFailureRelationship;
    //2.获取手势所在的tableView中的点位
    CGPoint point = [gesture locationInView:self.tableView];
    //3. 根据这个点获取是第几行, ROWHight是单个cell的高度
    NSInteger index = floor(point.y/ROWHight);
    //4.得到第行,而且得到cell
    self.indexPath = [NSIndexPath indexPathForRow:index inSection:0];
    self.cell =[self.tableView cellForRowAtIndexPath:self.indexPath];

    // 加个白色背景,这个背影是高清显示的。
    UIView *bgView =[[UIView alloc] initWithFrame:CGRectMake(20, 10, kScreenWidth - 40,kScreenHeight - 20 - 64 * 2)];
    bgView.backgroundColor = [UIColor whiteColor];
    bgView.layer.cornerRadius = 10;
    bgView.clipsToBounds = YES;
    [childVc.view addSubview:bgView];

    //展示的标题
    UILabel *lable1 = [[UILabel alloc] initWithFrame:CGRectMake(0,bgView.bounds.size.height/2-40 , bgView.bounds.size.width, 40)];
    lable1.textAlignment = NSTextAlignmentCenter;
    lable1.textColor=[UIColor redColor];
    lable1.text = self.cell.textLabel.text;
    [bgView addSubview:lable1];

    // 展示的详情
    UILabel *lable2 = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(lable1.frame), bgView.bounds.size.width, 40)];
    lable2.textAlignment = NSTextAlignmentCenter;
    lable2.textColor = [UIColor lightGrayColor];
    NSString *text = self.cell.detailTextLabel.text;
    lable2.text= text;
    [bgView addSubview:lable2];

    //提示lable
    UILabel *detail = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(lable2.frame), bgView.bounds.size.width, 40)];
    detail.textAlignment = NSTextAlignmentCenter;
    detail.textColor=[UIColor yellowColor];
    detail.text = @"再按重一点就选择该项了";
    [bgView addSubview: detail];

    //最后返回的展示的控制器
    return childVc;
    }



    //MARK: -- 再次重按的时候表示进行选择了,然后消失后会下走的方法
    - (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController*)viewControllerToCommit{

    //让它走选择了某项的代理方法。
    [self tableView:self.tableView didSelectRowAtIndexPath:self.indexPath];
    }

补充说明

  • location是相对于注册source来获取的。
  • UIViewControllerPreviewing 主要可用的属性,是相对于注册source的属性:

    1
    2
    3
    @property (nonatomic, readonly) UIGestureRecognizer *previewingGestureRecognizerForFailureRelationship //手势
    @property (nonatomic, readonly) UIView *sourceView //source当初注册传入的视图
    @property (nonatomic) CGRect sourceRect //source的frame
  • 注册的时候直接用SourceView为Cell。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
            //MARK:-- 返回cell
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID forIndexPath:indexPath];
    if(cell) {
    cell.textLabel.text = self.dataSouce[indexPath.row][@"title"];
    cell.detailTextLabel.text = self.dataSouce[indexPath.row][@"detail"];
    [self registerForPreviewingWithDelegate:self sourceView:cell];
    }
    return cell;
    }

    // 2.在代理中获取点击cell就简单多了
    - (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {

    UIViewController *childVc = [[UIViewController alloc]init];

    // 方法一:
    self.cell = (UITableViewCell *) previewingContext.sourceView;//这个可以获取注册的sourceView
    CGRect frame = previewingContext.sourceRect;//这个可以获取注册的sourceView.frame及大小

    // 方法二:
    // UIGestureRecognizer *gesture = previewingContext.previewingGestureRecognizerForFailureRelationship;// //2.获取手势
    // UIGestureRecognizer *gesture = previewingContext.previewingGestureRecognizerForFailureRelationship;// //2.获取手势所在的tableView中的点位
    // self.cell = (UITableViewCell *)gesture.view;//这个可以获取点击所得到的cell;

    return childVc;
    }
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
      class NexViewController: UIViewController{
    let CellId = "CellId"
    var currentCell : UITableViewCell?

    @IBOutlet weak var tableView: UITableView!

    var dataSource : [[String : Any]] = [];

    override func viewDidLoad() {
    super.viewDidLoad()
    getData()
    }
    }
    //遵守协议
    extension NexViewController : UITableViewDataSource,UITableViewDelegate,UIViewControllerPreviewingDelegate
    {
    //TODO: -- 加载数据
    func getData() {
    for i in 0..<20{
    let dic = ["title" : "测试标题 + \(i+1)", "detail" : "详情资料是这样的 + \(i+1)"];
    dataSource.append(dic)
    }
    tableView.reloadData()
    }

    //MARK: -- UITableViewDataSource
    //TODO:-- 每个section有多少条
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
    return dataSource.count
    }

    //TODO:-- 返回Cell
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
    let cell = tableView.dequeueReusableCell(withIdentifier: CellId, for: indexPath)
    cell.textLabel?.text = (dataSource[indexPath.row] as! Dictionary)["title"]
    cell.detailTextLabel?.text = (dataSource[indexPath.row] as! Dictionary)["detail”]

    // 注册3DTouch
    registerForPreviewing(with: self, sourceView: cell)
    return cell
    }


    //TODO: -- UITableViewDelegate public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
    tableView.deselectRow(at: indexPath, animated: true)
    performSegue(withIdentifier: "detail", sender: dataSource[indexPath.row])
    }

    //重写segue跳转方法
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == “detail"{
    if let viewcontroller = segue.destination as? DetailViewController{
    viewcontroller.info = sender as! [String : Any]?
    }
    }
    }

    //MARK: -- UIViewControllerPreviewingDelegate
    public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?{

    let childVC = UIViewController()
    currentCell = previewingContext.sourceView as? UITableViewCell

    //TODO: -- 加白色背景
    let bgView = UIView(frame: .init(x: 20, y: 10, width: (view.window?.bounds.size.width)!-40 , height: (view.window?.bounds.size.height)! - 20-64*2))
    bgView.backgroundColor= #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    bgView.layer.cornerRadius = 10.0
    bgView.clipsToBounds = true
    childVC.view.addSubview(bgView)

    // 标题
    let lable1 = UILabel(frame: .init(x: 0, y: bgView.bounds.size.height/2 - 40, width: bgView.bounds.size.width, height: 40))
    lable1.textAlignment = .center
    lable1.textColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
    lable1.text = currentCell?.textLabel?.text
    bgView.addSubview(lable1)

    //详情
    let lable2 = UILabel(frame: .init(x: 0, y: lable1.frame.maxY, width: bgView.bounds.size.width, height: 40))
    lable2.textAlignment = .center
    lable2.textColor = #colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1)
    lable2.text = currentCell?.detailTextLabel?.text
    bgView.addSubview(lable2)

    //提示
    let detail = UILabel(frame: .init(x: 0, y: lable2.frame.maxY, width: bgView.bounds.size.width, height: 40))
    detail.textAlignment = .center
    detail.textColor= #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)
    detail.text = "再按重一点就选择该项了"
    bgView.addSubview(detail)

    return childVC
    }

    //再按重些会选择该项,消失展示框后需要走的代码
    public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController){

    let indexPath = tableView.indexPath(for: currentCell!)
    if indexPath != nil{
    tableView(tableView, didSelectRowAt: indexPath!)
    }
    }

    }

Force Properties 力量属性

简介

iOS9为我们提供了一个新的交互参数:力度。我们可以检测某一交互的力度值,来做相应的交互处理。例如,我们可以通过力度来控制快进的快慢,音量增加的快慢等。

UITouch新增了两个属性:

  • open var force: CGFloat{ get } //force : 手指按下的力度
  • open var maximumPossibleForce: CGFloat { get } //maximumPossibleForce : 最大可能的力度

    在touch中就可以获取这两个值。所以我们就可以触摸按压的时候实现用按压力度来实现酷炫的效果

    示例代码

  • OC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 根据按压的力度来改变当前视图的颜色
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent*)event{

    if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
    UITouch *touch = [[touches allObjects] firstObject];
    NSLog(@"\n force:%f,maximumPossibleForce:%ff",touch.force,touch.maximumPossibleForce);
    CGFloat dito = touch.force/touch.maximumPossibleForce;
    self.view.backgroundColor = [UIColor colorWithRed:dito green:dito blue:1/dito alpha:1];

    }
    }
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     //TODO:--重写touchesMoved的方法,根据按压的力度来改变当前视图的颜色
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    if traitCollection.forceTouchCapability == .available{
    let touch = touches.first
    print("\n force: \(touch!.force),maximumPossibleForce: \(touch?.maximumPossibleForce)")
    let dito = (touch?.force)! / (touch?.maximumPossibleForce)!
    view.backgroundColor = UIColor(red: 0.3+5*dito, green: 0.3+5*dito, blue: dito, alpha:1)
    }
    }

Web View peek and pop API HTML链接预览功能

简介

原生的safari浏览器中的超链接可以支持3D touch,出现超链接的预览,使用方法和前文中提到的方法类似。ios 9 中增加了一个新的浏览器控制器SFSafariViewController。它可以在你的程序中直接嵌入 Safari浏览器,而且支持3DTouch效果。

示例代码

  • OC

    1
    2
    3
    4
    5
    6
    7
    8
    // 1.首先导入SafariServices.h
    #import <SafariServices/SafariServices.h>

    // 2.设置连接:
    SFSafariViewController *safariView = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"] entersReaderIfAvailable:YES];

    // 3.推出控制器:
    [self.navigationController pushViewController:safariView animated:YES];
  • Swift

    1
    2
    3
    4
    5
    6
    7
     // 1. 导入框架
    import SafariServices

    // 2. 使用
    let url = URL(string: "https://www.baidu.com")
    let safariView = SFSafariViewController(url: url! , entersReaderIfAvailable: true)
    navigationController?.pushViewController(safariView, animated: true)

结尾

3D Touch确实挺炫酷的,特别是Home Screen Quick Actions(主屏幕快捷入口)和
peek And pop(预览和弹出功能),增加了用户体验。

这里我只是简单地介绍了它的使用,如有疑问请Q我: 342112780