3d swift4 touch_swift 4.2 app 3d touch - CSDN
  • OneSwift - iOS Tips Based On Swift 今天为大家带来的是给应用添加3D Touch菜单,这样可以方便用户在首页即可快速访问某些页面。 以OneDay为例,通过3D Touch用户可以快速选择进入到添加页面、设置页面、归档页面...

    OneSwift - iOS Tips Based On Swift

    今天为大家带来的是给应用添加3D Touch菜单,这样可以方便用户在首页即可快速访问某些页面。 以OneDay为例,通过3D Touch用户可以快速选择进入到添加页面、设置页面、归档页面、首页。

    一、创建自定义的3D Touch菜单

    AppDelegatedidFinishLaunchingWithOptions中,我们添加下列代码,来实现按钮的添加。

    //添加icon 3d Touch
    let firstItemIcon:UIApplicationShortcutIcon = UIApplicationShortcutIcon(type: .confirmation)
    let firstItem = UIMutableApplicationShortcutItem(type: "1", localizedTitle: NSLocalizedString("Home", comment: "Home icon"), localizedSubtitle: nil, icon: firstItemIcon, userInfo: nil)
    
    let firstItemIcon1:UIApplicationShortcutIcon = UIApplicationShortcutIcon(type: .taskCompleted)
    let firstItem1 = UIMutableApplicationShortcutItem(type: "2", localizedTitle: NSLocalizedString("Archive ", comment: "Archive icon"), localizedSubtitle: nil, icon: firstItemIcon1, userInfo: nil)
    
    
    let firstItemIcon2:UIApplicationShortcutIcon = UIApplicationShortcutIcon(type: .task)
    let firstItem2 = UIMutableApplicationShortcutItem(type: "3", localizedTitle: NSLocalizedString("Setting", comment: "Setting icon"), localizedSubtitle: nil, icon: firstItemIcon2, userInfo: nil)
    
    
    let firstItemIcon3:UIApplicationShortcutIcon = UIApplicationShortcutIcon(type: .add)
    let firstItem3 = UIMutableApplicationShortcutItem(type: "4", localizedTitle: NSLocalizedString("Add", comment: "Add icon"), localizedSubtitle: nil, icon: firstItemIcon3, userInfo: nil)
    
    application.shortcutItems = [firstItem,firstItem1,firstItem2,firstItem3]
    
    复制代码

    其中按钮的icon使用系统的icon图片,其他图样可以参考这个链接。

    3DTouch Xcode原生图标icon图样预览

    二、为每个按钮添加响应事件

    接着我们为每个按钮添加响应事件,因为我的四个按钮刚好都到一个固定页面,所以响应事件实现页面的跳转即可。

    绑定按钮的事件函数:

    func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
            let handledShortCutItem = handleShortCutItem(shortcutItem: shortcutItem)
            completionHandler(handledShortCutItem)
        }
    复制代码

    函数的具体代码:

    func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
        var handled = false
    
        if shortcutItem.type == "1" { //首页
    
            let rootNavigationViewController = window!.rootViewController as? UINavigationController
            let rootViewController = rootNavigationViewController?.viewControllers.first as UIViewController?
    
            rootNavigationViewController?.popToRootViewController(animated: false)
            handled = true
    
        }
        if shortcutItem.type == "2" { //编辑
    
            let rootNavigationViewController = window!.rootViewController as? UINavigationController
            let rootViewController = rootNavigationViewController?.viewControllers.first as UIViewController?
    
            rootNavigationViewController?.popToRootViewController(animated: false)
            rootViewController?.performSegue(withIdentifier: "toArchive", sender: nil)
            handled = true
    
        }
    
        if shortcutItem.type == "3" { //设置
    
            let rootNavigationViewController = window!.rootViewController as? UINavigationController
            let rootViewController = rootNavigationViewController?.viewControllers.first as UIViewController?
    
            rootNavigationViewController?.popToRootViewController(animated: false)
            rootViewController?.performSegue(withIdentifier: "toSetting", sender: nil)
            handled = true
    
        }
    
        if shortcutItem.type == "4" { //编辑
    
            let rootNavigationViewController = window!.rootViewController as? UINavigationController
            let rootViewController = rootNavigationViewController?.viewControllers.first as UIViewController?
    
            rootNavigationViewController?.popToRootViewController(animated: false)
            rootViewController?.performSegue(withIdentifier: "addTodo", sender: nil)
            handled = true
    
        }
    
        return handled
    }
    复制代码

    这里我用到了performSegue,所以在Main.storyboard中会给每个跳转绑定ID。

    后续将补充介绍如何自定义icon、如何在页面内实现3D Touch,欢迎关注OneSwift的后续更新。

    GitHub:OneSwift - iOS Tips Based On Swift

    微博:xDEHANG

    展开全文
  • 2015年,苹果发布了iOS9以及iphone6s/iphone6s Plus,其中最具有创新的就是新的触控方式3D Touch,相对于多点触摸在平面二维空间的操作,3D Touch技术增加了对力度和手指面积的感知,可以通过长按快速预览、查看你想...

    Swift开发之3DTouch实用演练

    2015年,苹果发布了iOS9以及iphone6s/iphone6s Plus,其中最具有创新的就是新的触控方式3D Touch,相对于多点触摸在平面二维空间的操作,3D Touch技术增加了对力度和手指面积的感知,可以通过长按快速预览、查看你想要的短信、图片或者超链接等内容,Peek和Pop手势的响应时间可迅捷到 10ms和15ms等。

    • 用户现在可以按主屏幕图标立即访问应用程序提供的功能。
    • 在您的应用程序中,用户现在可以按视图来查看其他内容的预览,并获得对功能的加速访问
    • 在日常开发中,我们经常需要使用3D Touch中的两个功能
      • 在主屏幕上对应用图标使用3DTouch操作
      • 在应用程序内对某一控件使用3DTouch操作
    • 功能需要iOS9以上系统和iphone6s/iphone6s Plus及以上机型(模拟机现在也是可以的)
    • demo地址

    一. 效果演练

    1. 主屏幕快速操作

    • 通过按下iPhone 6s或iPhone 6s Plus上的应用程序图标,用户可以获得一组快速操作。
    • 当用户选择快速操作时,您的应用程序激活或启动,并跳转到相应界面
      主屏幕快速操作

    2. Peek and Pop

    • 对界面内某一控件的3DTouch操作
    • Peek和Pop是应用内的一种全新交互模式,当用户不断增加力量在控件上按压,会依次进入四个阶段
    • 轻按控件,除触发Peek的控件外,其他区域全部虚化
    • 继续用力Peek被触发,展示Pop界面快照
    • 向上滑动展示快捷选项
    • 继续用力跳转进入Pop界面

    轻按控件,除触发Peek的控件外,其他区域全部虚化

    继续用力Peek被触发,展示Pop界面快照

    向上滑动展示快捷选项

    3. 注意

    • 3D Touch仅在3D Touch设备上可用,如果启用。在iOS 9以上,默认情况下启用3D Touch。
    • 用户可以在设置>常规>辅助功能> 3D触摸中关闭3D触摸。
    • 当3D Touch可用时,利用其功能。当它不可用时,提供替代方法,例如通过使用触摸和保持。
    • 3D Touch功能支持VoiceOver。

    二. 主屏幕操作

    • ShortcutItem功能允许用户在主屏幕上对应用图标使用3DTouch操作,如果本次操作有效,则会给出几个快捷可选项允许用户进行操作
    • 主屏幕icon上的快捷标签的实现方式有两种,一种是在工程文件info.plist里静态设置,另一种是代码的动态实现
    • 优先显示静态添加,总数达到4个不再显示

    1. 静态设置

    • 在info.plist中添加UIApplicationShortcutItems关键字,以如下方式配置即可

    UIApplicationShortcutItems配置

    其中各个关键字释义如下:
    - UIApplicationShortcutItemType: 快捷可选项的特定字符串(必填)
    - UIApplicationShortcutItemTitle: 快捷可选项的标题(必填)
    - UIApplicationShortcutItemSubtitle: 快捷可选项的子标题(可选)
    - UIApplicationShortcutItemIconType: 快捷可选项的图标(可选)
    - UIApplicationShortcutItemIconFile: 快捷可选项的自定义图标(可选)
    - UIApplicationShortcutItemUserInfo: 快捷可选项的附加信息(可选)

    2. 动态添加UIApplicationShortcutItem

    2-1. UIApplicationShortcutItem初始化方法

    UIApplicationShortcutItem(type: String, localizedTitle: String, localizedSubtitle: String?, icon: UIApplicationShortcutIcon?, userInfo: [AnyHashable : Any]?)
    • 参数介绍
      • type: 快捷可选项的特定字符串(必填)
      • localizedTitle: 快捷可选项的标题(必填)
      • localizedSubtitle: 快捷可选项的子标题(可选)
      • icon: 快捷可选项的图标(可选)
      • userInfo: 快捷可选项的附加信息(可选)

    2-1. 图标

    2-1-1. 初始化方式
    //方式一: 自定义图标
    //注: 自定义图标需要使用镂空图标,同时建议1倍图标大小为35*35
    UIApplicationShortcutIcon(templateImageName: String)
    
    //方式二: 使用系统图标
    UIApplicationShortcutIcon(type: UIApplicationShortcutIconType)
    2-1-2. 系统图标样式如下

    系统图片一览表

    2-3. 具体实现代码如下

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
        //3D Touch
        let homeIcon = UIApplicationShortcutIcon(type: .compose)
        let homeItem = UIApplicationShortcutItem(type: "homeAnchor", localizedTitle: "首页", localizedSubtitle: "点击进入首页", icon: homeIcon, userInfo: nil)
        let playIcon = UIApplicationShortcutIcon(type: .play)
        let playItem = UIApplicationShortcutItem(type: "play", localizedTitle: "播放", localizedSubtitle: "", icon: playIcon, userInfo: nil)
        let userIcon = UIApplicationShortcutIcon(type: .search)
        let userItem = UIApplicationShortcutItem(type: "username", localizedTitle: "用户名", localizedSubtitle: "", icon: userIcon, userInfo: nil)
    
        UIApplication.shared.shortcutItems = [homeItem, playItem, userItem]
    
        return true
    }
    

    2-4. item点击跳转

    • 可根据type标识判断
    • 可根据localizedTitle标识判断
    //菜单跳转
    func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
        guard let tabBarVC = window?.rootViewController as? MainViewController else { return }
    
        //根据type唯一标识进行判断跳转, 或者根据localizedTitle判断
        switch shortcutItem.type {
        case "homeAnchor":
            tabBarVC.selectedIndex = 1
        case "play":
            let username = ShowRoomViewController()
            username.hidesBottomBarWhenPushed = true
            tabBarVC.selectedViewController?.childViewControllers.first?.present(username, animated: true, completion: nil)
        case "username":
            let username = NameViewController()
            username.hidesBottomBarWhenPushed = true
            tabBarVC.selectedViewController?.childViewControllers.last?.navigationController?.pushViewController(username, animated: true)
        default:
            tabBarVC.selectedIndex = 0
        }
    }
    

    三. Peek and Pop

    • Peek和Pop是应用内的一种全新交互模式,当用户不断增加力量在控件上按压,会依次进入四个阶段
    • 这里小编将通过ViewController里面的UITableViewCell进行延时功能

    注意: 在动态添加快捷可选项前,需要用判断是否支持3D Touch功能,以免在不支持的设备上运行程序导致闪退

    1. 判断是否支持3D Touch功能

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        let model = happyVM.anchorGroups[indexPath.section].anchors[indexPath.row]
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
            cell?.textLabel?.text = model.room_name
            cell?.accessoryType = .disclosureIndicator
        }
    
    ---
        //这里是添加判断是否支持3D Touch的代码
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == .available {
                //支持3D Touch
                //注册Peek & Pop功能
                registerForPreviewing(with: self, sourceView: cell!)
            }
        }
    ---
    
        return cell!
    }
    

    检测是否支持3D Touch:UIForceTouchCapability是一个枚举值,取值如下:

    case unknown      //3D Touch检测失败
    case unavailable //3D Touch不可用
    case available  //3D Touch可用

    2. 给对应view注册3Dtouch事件

    • 在判断支持3Dtouch里面注册
    //注册Peek & Pop功能
    self.registerForPreviewing(with: self, sourceView: cell!)

    3. 遵守UIViewControllerPreviewingDelegate协议

    • 需要实现Peek & Pop交互的控件所在的控制器遵循协议并实现两个代理方法

    3-1. 当进入Peek状态时,系统会回调如下方法

    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        //1. 获取按压的cell所在的行
        guard let cell = previewingContext.sourceView as? UITableViewCell else { return UIViewController() }
        let indexPath = tableVIew.indexPath(for: cell) ?? IndexPath(row: 0, section: 0)
    
        //2. 设定预览界面
        let vc = ShowRoomViewController()
        // 预览区域大小(可不设置), 0为默认尺寸
        vc.preferredContentSize = CGSize(width: 0, height: 0)
        vc.showStr =  "我是第\(indexPath.row)行用力按压进来的"
    
        //调整不被虚化的范围,按压的那个cell不被虚化(轻轻按压时周边会被虚化,再少用力展示预览,再加力跳页至设定界面)
        let rect = CGRect(x: 0, y: 0, width: kScreenWidth, height: 44)
        //设置触发操作的视图的不被虚化的区域
        previewingContext.sourceRect = rect
    
        //返回预览界面
        return vc
    }
    

    3-2. 当进入Pop状态时,系统会回调如下方法

    • 用力按压进入viewControllerToCommit
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
        viewControllerToCommit.hidesBottomBarWhenPushed = true
        show(viewControllerToCommit, sender: self)
    }
    

    来看看效果

    3D Touch1演示.gif

    3-4. 当弹出预览时,上滑预览视图,出现预览视图中快捷选项

    var previewActionItems: [UIPreviewActionItem] { get }
    • previewActionItems用户在3D Touch预览上向上滑动时显示的快速操作
    • 在将要弹出的页面内重写previewActionItems的get属性
    extension ShowRoomViewController {
        //重写previewActionItems的get方法
        override var previewActionItems: [UIPreviewActionItem] {
            let action1 = UIPreviewAction(title: "跳转", style: .default) { (action, previewViewController) in
                let showVC = ShowRoomViewController()
                showVC.hidesBottomBarWhenPushed = true
                previewViewController.navigationController?.pushViewController(showVC, animated: true)
            }
    
            let action3 = UIPreviewAction(title: "取消", style: .destructive) { (action, previewViewController) in
                print("我是取消按钮")
            }
    
            ////该按钮可以是一个组,点击该组时,跳到组里面的按钮。
            let subAction1 = UIPreviewAction(title: "测试1", style: .selected) { (action, previewViewController) in
                print("我是测试按钮1")
            }
            let subAction2 = UIPreviewAction(title: "测试2", style: .selected) { (action, previewViewController) in
                print("我是测试按钮2")
            }
            let subAction3 = UIPreviewAction(title: "测试3", style: .selected) { (action, previewViewController) in
                print("我是测试按钮3")
            }
            let groupAction = UIPreviewActionGroup(title: "更多", style: .default, actions: [subAction1, subAction2, subAction3])
    
            return [action1, action3, groupAction]
        }
    }
    

    action的各种样式

    public enum UIPreviewActionStyle : Int {
    
        //默认样式
        case `default`
        //右侧有对勾的样式
        case selected
        //红色字体的样式
        case destructive
    }

    3-5. forcemaximumPossibleForce

    到此,3DTouch在APP中的集成就先介绍这些,3DTouch中还有个重要的属性–压力属性(force 和 maximumPossibleForce)这里简单介绍下

    • 手指在屏幕上慢慢增加力度在减少力度,可以看到view背景色的变化
    • 程序运行后找到我的 -> 头像(用户名)查看效果
    • 代码找到NameViewController.swift查看
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first ?? UITouch()
        //获取重按力度
        print("平均触摸的力--\(touch.force)")
        print("触摸的最大可能力--\(touch.maximumPossibleForce)")
    
        let change = touch.force / touch.maximumPossibleForce
            view.backgroundColor = UIColor(red: 0.5, green: 0.5, blue: change, alpha: 1)
    }
    

    此外还有以下属性, 详细可参考3D Touch官方文档

    var tapCount: Int
    //手指触摸此次触摸的次数。
    
    var timestamp: TimeInterval
    //触摸发生的时间或最后一次突变的时间。
    
    var type: UITouchType
    //触摸的类型。
    
    enum UITouchType
    //接收的触摸类型。
    
    var phase: UITouchPhase
    //触摸的阶段。
    
    enum UITouchPhase
    //手指触摸的阶段。
    
    var maximumPossibleForce: CGFloat
    //触摸的最大可能力。
    
    var force: CGFloat
    //触摸力,其中值表示平均触摸的力(由系统预定,不是用户特定的)。1.0
    
    var altitudeAngle: CGFloat
    //手写笔的高度(弧度)。
    
    func azimuthAngle(in: UIView?)
    //返回触控笔的方位角(弧度)。
    
    func azimuthUnitVector(in: UIView?)
    //返回指向触控笔方位角方向的单位向量。

    最后附上Demo地址


    参考资料

    iOS 3D touch开发

    3D Touch官方文档

    展开全文
  • 3D Touch swift 版本

    2015-12-01 15:24:58
    OSX 10.10.5 (14F27) XCODE Version 7.1.1 (7B1005) ...swift -version --->Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76) AppDelegate.swift: // // AppDelegate.swift // 3DTouchDemo /

    OSX 10.10.5 (14F27)

    XCODE Version 7.1.1 (7B1005)

    swift -version --->Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)

    AppDelegate.swift:

    //
    //  AppDelegate.swift
    //  3DTouchDemo
    //
    //  Created by Johnson on 15/11/13.
    //  Copyright © 2015年 Johnson. All rights reserved.
    //
    
    import UIKit
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
    
    
        func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            
            //给App图标添加3D Touch菜单  重按
            //拍照
            
            //菜单图标
            let cameraIcon = UIApplicationShortcutIcon(type: .Add)
            
            let cameraItem = UIMutableApplicationShortcutItem(type: "camera", localizedTitle: "拍照")
            
            cameraItem.icon = cameraIcon
            
            //给App图标添加3D Touch菜单
            //拍照
            let photoLibraryIcon = UIApplicationShortcutIcon(type: .Search)
    
            let photoLibrayItem =  UIMutableApplicationShortcutItem(type: "photos", localizedTitle: "相册")
            photoLibrayItem.icon = photoLibraryIcon
            
            
            application.shortcutItems = [cameraItem,photoLibrayItem]
            
            return true
        }
        
        
        func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
            if shortcutItem.type == "camera"
            {
                let picker = UIImagePickerController()
                picker.allowsEditing = true
                picker.sourceType = UIImagePickerControllerSourceType.Camera
                
                //进入照相界面
                self.window?.rootViewController?.presentViewController(picker, animated: true, completion: nil)
                
            }
            else if shortcutItem.type == "photos"
            {
                let libraryPicker = UIImagePickerController()
                libraryPicker.allowsEditing = true
                libraryPicker.sourceType = UIImagePickerControllerSourceType.Camera
                
                //进入图片库
                self.window?.rootViewController?.presentViewController(libraryPicker, animated: true, completion: nil)
            }
        }
    
        func applicationWillResignActive(application: UIApplication) {
            // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
            // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
        }
    
        func applicationDidEnterBackground(application: UIApplication) {
            // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
            // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        }
    
        func applicationWillEnterForeground(application: UIApplication) {
            // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
        }
    
        func applicationDidBecomeActive(application: UIApplication) {
            // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        }
    
        func applicationWillTerminate(application: UIApplication) {
            // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        }
    
    
    }
    
    

    在两个application级别的监听器函数中完成:1.对图标菜单的设置   2.当点击图标后的事件分配和处理


    另附上一个长按图片分享的效果:

    //
    //  ViewController.swift
    //  3DTouchDemo
    //
    //  Created by Johnson on 15/11/13.
    //  Copyright © 2015年 Johnson. All rights reserved.
    //
    
    import UIKit
    
    class ThreeDTouchViewController : UIViewController,UIViewControllerPreviewingDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
         func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
        {
            //ASPreviewViewController
            return  nil
        }
        
         func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)
        {
            
        }
        
        //预览时,添加底部菜单
        override func previewActionItems() -> [UIPreviewActionItem] {
            
            let shareItem = UIPreviewAction(title: "分享", style: UIPreviewActionStyle.Default) { (<#UIPreviewAction#>, <#UIViewController#>) -> Void in
                print("点击了分享")
            }
            
            let savingItem = UIPreviewAction(title: "收藏", style: UIPreviewActionStyle.Default) { (<#UIPreviewAction#>, <#UIViewController#>) -> Void in
                print("点击了收藏")
            }
            return [shareItem,savingItem]
        }
        
    }
    
    







    展开全文
  • 本文讲的是用 Swift 枚举完美实现 3D touch 快捷操作, 完美实现 3D Touch 我不确定是否一开始 Swift 的创造者们能够估计到他们创造的这一门极其优美的语言,将带给开发者们如此激昂的热情。 我只想说,Swift 社区...
    本文讲的是用 Swift 枚举完美实现 3D touch 快捷操作,

    完美实现 3D Touch

    我不确定是否一开始 Swift 的创造者们能够估计到他们创造的这一门极其优美的语言,将带给开发者们如此激昂的热情。 我只想说,Swift 社区已经成长且语言已经稳定(ISH)到一个地步,现在甚至有个专有名词赞美 Swift 编程的美好未来。

    Swifty.

    “That code isn’t Swifty”. “This should be more Swifty”. “This is a Swifty pattern”. “We can make this Swifty”.(反正就是漂亮,美得让人窒息之类的话)

    这些赞扬的话还会越来越多。虽然我不太提倡说这些赞赏的话语,但是我真的找不到其它可以替代的话来夸赞,用 Swift 为 3D touch 编写快捷操作的那种“美感”。

    这周,让我们来看看在 UIApplicationShortcutItem 实现细节中,Swift 是如何让我们成为 “一等公民” 的。

    实现方案

    当一个用户在主屏开始一个快捷操作时,会发生下面两件事中的一个。应用程序可以调用指定的函数来处理该快捷方式,或快速休眠再启动 — — 这意味着最终还是通过熟悉的 didFinishLaunchingWithOptions 来执行。

    无论哪种方式,开发人员通常根据 UIApplicationShortcutItem 类型属性来决定用哪种操作。

    if shortcutItem.type == "bundleid.shortcutType"
    {
        //Action triggered
    }
    

    上面代码是正确的,项目中只是用一次的话还是可以的。

    可惜的是,即便在 Swiftosphere 中,switch 条件用字符串实例有额外好处的情况下,随着增加越来越多的快捷操作,这种方法还是很快令人觉得十分繁琐。同时它也被大量证明,对于这种情况使用字符串字面值可能是白费功夫:

    if shortcutItem.type == "bundleid.shortcutType"
    {
        //Action triggered
    }
    else if shortcutItem.type == "bundleid.shortcutTypeXYZ"
    {
        //Another action
    }
    //and on and on
    

    处理这些快捷操作就像你代码库的一小部分,尽管如此—— Swift 能处理的更好而且更安全些。所以,让我们看看 Swift 如何发挥它的“魔法”,给我们提供一个更好的选择。

    Enum .Fun

    讲真, Swift 的枚举很“疯狂”。当 Swift 在 14 年发布的时候,我从来没有想过在枚举中可以使用属性,进行初始化和调用函数,但现在我们已经在这样子做了。

    不管怎么说,我们可以在工作中用上它们。当你考虑支持 UIApplicationShortcutItem 的实现细节时,几个关键点应该注意:

    • 必须通过 type 属性给快捷方式指定一个名称
    • 根据苹果官方指南,必须以 bundle id 作为这些操作的前缀
    • 可能会有多个快捷方式
    • 可能会在应用程序多个位置采取基于类型的特定操作

    我们的游戏计划很简单。我们不采用硬编码字符串字面量,而是初始化一个枚举实例来表示这就是被调用的快捷方式。

    具体实现

    我们虚构两个快捷方式,每个都额外附加一个之后,现在就是由一个枚举表示。

    enum IncomingShortcutItem : String
    {
        case SomeStaticAction
        case SomeDynamicAction
    }
    

    如果是用 Objective-C,我们可能到这就结束了。我认为,使用枚举远远优于之前使用字符串字面量的观点,已经被大家所接受。然而,对于为应用每个操作类型属性指定 bundle id 为前缀(例如,com.dreaminginbinary.myApp.MyApp)来说,使用一些字符串插值仍是最佳解决办法。

    但是,因为 Swift 枚举超级厉害,我们可以用它以一种非常简洁的方法来实现:

    enum IncomingShortcutItem : String
    {
        case SomeStaticAction
        case SomeDynamicAction
        private static let prefix: String = {
            return NSBundle.mainBundle().bundleIdentifier! + "."
        }()
    }
    

    看!厉害吧!我们能安全的从计算属性中获取应用的包路径。回忆起上个星期的一篇文章,在介绍闭包的最后提到了插入值,我们希望将_前缀_分配给闭包的返回语句,并不是闭包本身。

    最佳模式

    最终方案,将用上两个我们最喜爱的 Swift 功能。那就是为枚举创建一个可能会失败的初始化函数的时候,使用 guard 语句清除空值以确保安全。

    enum IncomingShortcutItem : String
    {
        case SomeStaticAction
        case SomeDynamicAction
        private static let prefix: String = {
            return NSBundle.mainBundle().bundleIdentifier! + "."
        }()
    
        init?(shortCutType: String)
        {
            guard let bundleStringRange = shortCutType.rangeOfString(IncomingShortcutItem.prefix) else
            {
                return nil
            }
            var enumValueString = shortCutType
            enumValueString.removeRange(bundleStringRange)
            self.init(rawValue: enumValueString)
        }
    }
    

    这个允许失败的初始化是很重要的。如果没有匹配到快捷操作对应的字符串,应该跳出。它还能告诉我,如果我是维护者,当该使用它的时候,它可能更适合使用 guard 语句。

    我特别喜欢这部分,这也是我们如何能够利用枚举 rawValue 的优势,且很容易把它拼接到包路径上。这一切都在正确的地方,一个初始化函数的内部。

    别忘了,一旦其初始化,我们还可以当枚举来用的。这意味着我们会有一个可读很高的 switch 语句,后面有些反对的理由。

    下面可能是最终产品的样子,所有的东西都集成进来了,与线上应用相比略有删减:

    static func handleShortcutItem(shortcutItem:UIApplicationShortcutItem) -> Bool
    {
        //Initialize our enum instance to check for a shortcut
        guard let shortCutAction = IncomingShortcutItem(shortCutType: shortcutItem.type) else
        {
            return false
        }
        //Now we've got a valid shortcut, and can use a switch
        switch shortCutAction
        {
            case .ShowFavorites:
                return ShortcutItemHelper.showFavorites()
            case .ShowDeveloper:
                return ShortcutItemHelper.handleAction(with: developer)
        }
    }
    

    至此,通过使用这种模式,我们的快捷操作变的可分类和内容安全,这也是我为什么这么喜欢它的原因。在方法的末尾提供一个最终的 “return false” 语句其实没什么必要(甚至在 switch 语句中是默认启动),因为我们已经十分了解,最后给代码精简一下。

    和之前的代码比较一下:

    static func handleShortcutItem(shortcutItem:UIApplicationShortcutItem) -&gt; Bool
    {
        //Initialize our enum instance to check for a shortcut
        let shortcutAction = NSBundle.mainBundle().bundleIdentifier! + "." + shortcutItem.type
    
        if shortCutAction == "com.aCoolCompany.aCoolApp.shortCutOne"
        {
            return ShortcutItemHelper.showFavorites()
        }
        else if shortCutAction == "com.aCoolCompany.aCoolApp.shortCutTwo"
        {
             return ShortcutItemHelper.handleAction(with: developer)
        }
        return false
    }
    

    真的,这看起来比用 switch 简单点。但我之前见过很多类似的代码(当然是我自己写的啦),虽然能很好的运行,但我认为可以利用 Swift 特性的优势,写出更好的代码。

    最后的感想

    当我刚开始阅读 Swift 枚举的返回时,发现它们有点“重”。有类的 inits(),为什么我还要枚举符合协议,这看起来有点多余。多年以后,我想这种模式已经充分展示了为什么就是这样的原因。

    当我看到苹果实现了这种模式,确实很开心。我觉得这是个非常好的方式来解决一个小问题,同时对于快捷操作的实现细节来说也是个“团队友好”的方法。我认为他们也会同意我的观点,毕竟这种方式也在他们两个 3D touch 示例项目中。

    下次再见





    原文发布时间为:2016年09月10日

    本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
    展开全文
  • Swift 2.3 IOS 8.0 XCode 8.0 添加入口标签在这里只说下静态的好了,暂时还用不到动态的,e.g.<key>UIApplicationShortcutItems <key>UIApplicationShortcutItemType <string>ReceiveMoney</s

    Swift 2.3 IOS 8.0 XCode 8.0

    添加入口标签

    在这里只说下静态的好了,暂时还用不到动态的,e.g.

    <key>UIApplicationShortcutItems</key>
    <array>
        <dict>
            <key>UIApplicationShortcutItemType</key>
            <string>ReceiveMoney</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>当面收款</string>
            <key>UIApplicationShortcutItemIconFile</key>
            <string>short_icon_sk</string>
        </dict>
    </array>

    字段说明:

    字段名 功能 是否必须 备注
    UIApplicationShortcutItemType 这个键值设置一个快捷通道类型的字符串 必有项 我们可以监听该项的值来判断用户是从哪一个标签进入App的,该字段的值可以为空。
    UIApplicationShortcutItemTitle 这个键值设置标签的标题 必有项 我们可以监听该项的值来判断用户是从哪一个标签进入App的
    UIApplicationShortcutItemSubtitle 设置标签的副标题 可选项
    UIApplicationShortcutItemIconType 设置标签的图标样式,系统提供了29中样式的图标,但大部分只能在9.1的系统上使用,只有少数可以在9.0的系统中使用,这一部分会在后边详细介绍 可选项
    UIApplicationShortcutItemIconFile 设置自定义标签图片文件的路径 可选项
    UIApplicationShortcutItemUserInfo 设置用户信息,是一个字典类型,可以用来传值 可选项

    提示

    UIApplicationShortcutItemIconFile存在的话,UIApplicationShortcutItemIconType就会失效

    UIApplicationShortcutItemIconType的图标

    这里写图片描述

    很多人都只放出一个枚举值,我嘛~麻烦点,把所有的图标都从官网上下载下来,截了个图放上来

    typedef NS_ENUM(NSInteger, UIApplicationShortcutIconType) {
        UIApplicationShortcutIconTypeCompose,
        UIApplicationShortcutIconTypePlay,
        UIApplicationShortcutIconTypePause,
        UIApplicationShortcutIconTypeAdd,
        UIApplicationShortcutIconTypeLocation,
        UIApplicationShortcutIconTypeSearch,
        UIApplicationShortcutIconTypeShare,
        UIApplicationShortcutIconTypeProhibit       NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeContact        NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeHome           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeMarkLocation   NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeFavorite       NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeLove           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeCloud          NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeInvitation     NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeConfirmation   NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeMail           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeMessage        NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeDate           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeTime           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeCapturePhoto   NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeCaptureVideo   NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeTask           NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeTaskCompleted  NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeAlarm          NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeBookmark       NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeShuffle        NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeAudio          NS_ENUM_AVAILABLE_IOS(9_1),
        UIApplicationShortcutIconTypeUpdate         NS_ENUM_AVAILABLE_IOS(9_1)
    } NS_ENUM_AVAILABLE_IOS(9_0) __TVOS_PROHIBITED;

    short_icon_sk是我自己的图片,因为上面的图标实在找不到合适的,这个时候,在手机上就可以看到了

    e.g.
    这里写图片描述

    怎么样,还好吧。

    检测App启动方式

    启动方式

    1. 热启动,App已经打开,使用3D touch再次打开应用
    2. 冷启动,App关闭状态

    不关是哪种启动方式,都会调用下面的方法

    @available(iOS 9.0, *)
    func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
        // shortcutItem.type 就是 UIApplicationShortcutItemType 的值
        // TODO: 只要是3D touch 入口,都会触发到这里的
    }

    按道理,只要到这里就好了,根据shortcutItem.type的值跳转到制定的viewController就好了。问题就是要找到当前活动的viewController才可以,否则怎么跳转。

    extension UIWindow {
        // 获取当前活动的控制器
        public var visibleViewController:UIViewController? {
            return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
        }
    
        public static func getVisibleViewControllerFrom(vc:UIViewController?) -> UIViewController? {
            if let nc = vc as? UINavigationController {
                return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
            } else if let tc = vc as? UITabBarController {
                return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
            } else {
                if let pvc = vc?.presentedViewController {
                    return UIWindow.getVisibleViewControllerFrom(pvc)
                } else {
                    return vc
                }
            }
        }
    }

    这个问题解决了,那就跳呗。热启动状态下没问题。冷启动的时候就不行了。window!.visibleViewController == nil 了。尝试着延后点执行。放到applicationDidBecomeActive方法中

    func applicationDidBecomeActive(application: UIApplication) {
        // TODO: 这里开始
    }

    applicationDidBecomeActive只要应用从后台激活就会调用,我们需要增加一个状态判断是否触发了3D touch

    // 触发3D Touch的type
    var activeFrom3Dtouch:ShortcutIdentifier?
    // 3Dtouch的type
    enum ShortcutIdentifier:String {
        // 当面收款
        case ReceiveMoney = "ReceiveMoney"
    }
    // 
    @available(iOS 9.0, *)
    func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
        activeFrom3Dtouch = ShortcutIdentifier(rawValue: shortcutItem.type)
    }
    
    func applicationDidBecomeActive(application: UIApplication) {
        // 判断入口
        guard let touchType = activeFrom3Dtouch else {
            return
        }
    
        // TODO: 判断跳转操作
    
        // 清除,防止再次激活应用重复触发
        activeFrom3Dtouch = nil
    }

    看起来差不多了,运行了一下,冷启动还是不动。看了下微信、支付宝一类的应用,发现都是应用启动起来一会儿后才跳转。

    然后 的然后,我也做了一个延迟,果然,冷启动也OK了

    AppDelegate中的完整代码

    // 触发3D Touch的type
    var activeFrom3Dtouch:ShortcutIdentifier?
    // 3Dtouch的type
    enum ShortcutIdentifier:String {
        // 当面收款
        case ReceiveMoney = "ReceiveMoney"
    }
    // 
    @available(iOS 9.0, *)
    func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
        activeFrom3Dtouch = ShortcutIdentifier(rawValue: shortcutItem.type)
    }
    
    func applicationDidBecomeActive(application: UIApplication) {
        // 判断入口
        guard let touchType = activeFrom3Dtouch else {
            return
        }
    
         // 分类处理
        if touchType == .ReceiveMoney {
            // 延时0.5就足够了
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
                // 获取活动的Controller
                guard let currentViewController = self.window?.visibleViewController else {
                    return
                }
    
                // 不要重复打开付款页面
                guard !currentViewController.isKindOfClass(ReceiveMoneyViewController) else {
                    return
                }
    
                let board = UIStoryboard(name: "MyAccount", bundle: nil)
                let viewController = board.instantiateViewControllerWithIdentifier("receiveMoneyViewController")
    
                // 判断是否模态
                if(currentViewController.navigationController == nil) {
                    currentViewController.presentViewController(viewController, animated: true, completion: nil)
                } else {
                    currentViewController.navigationController?.pushViewController(viewController, animated: true)
                }
            }
        }
    
        // 清除,防止再次激活应用重复触发
        activeFrom3Dtouch = nil
    }

    总结

    做到这里基本上就可以用了,3D Touch本身并不复杂,但获取当前的viewController是比较麻烦的。在冷启动的时候,使用延时执行的方式获取到当前活动的viewController。

    参考:

    http://stackoverflow.com/questions/11637709/get-the-current-displaying-uiviewcontroller-on-the-screen-in-appdelegate-m

    展开全文
  • 什么是3D Touch 3D Touch 是iOS9之后专为 iPhone6s 机型加入的新特性,这一新技术移植于 Mac Book 上的 ForceTouch 更准确地说应该是 ForceTouch 在iPhone 上的实现吧。3D Touch 实质是一种新型的快捷单点触控技术,...
  • 3D Touch 之死

    2019-09-18 10:52:34
    作者:I/O来源:雷锋网(leiphone-sz)虽然没有被市场认可,3D Touch 依然不失为苹果在产品层面的一次勇敢尝试。2015 年 9 月 9 日,苹果在当天举...
  • 写几点自己认为比较有用的地方,具体看上面链接 快捷编辑 info.plist在往 info.plist 中添加东西的时候,不需要以记事本的方式打开,只需要在其页面中右击,选择show Raw Keys/Values 即可看到原始的键名,然后正常...
  • 之前记录过OC版实现3D Touch功能的小小演示,最近无事整整swift,也能弄一过swift版的3D Touoch。。。 上代码。 一、3D Touch重按主屏icon出现快捷标签有两种添加方式: 1.静态添加,在工程中的info.plist文件中...
  • 从iphone6s开始,苹果手机加入了3d touch技术,最简单的理解就是可以读取用户的点击屏幕力度大小,根据力度大小给予不同的反馈。 二、how? 实现方式? 如果静态方式和动态方式同时使用,app默认调用静态方式...
  • 之前一直在忙着维护项目,偶然的一天闲着玩手机玩到3Dtouch,看着效果挺不错的 于是就尝试着搜索了一下相关的文案,结果除了官网给的全英文api意外国内的基本都 是互相抄袭,我搜了37篇文章,29篇相似度惊人,而且仅...
  • 例子综合---swift

    2016-07-06 16:34:29
    1.广告轮播 ...http://www.swiftmi.com/code4swift/112.html3.逗视 iOS客户端 Swift2.0编写(带有3D Touch 功能) http://www.swiftmi.com/code4swift/141.html 4.TagsView 标签展示页ht
  • swift(UIFont)

    2020-07-26 23:30:49
    iOS font demo ,3d touch demo,swift 3.0 xcode 8
  • 在网上找了下资料,貌似很少的,只找到一篇简单介绍的  ... 当然根据这篇文章的资料,很难做出这个有趣的功能,至少我是这么认为。  于是发挥出我那无敌于世的搜索功能,终于还是让我找到了突破点。...
  • 3D Touch Peek、 Pop
  • iOS 为 App 图标添加 3D Touch 快捷访问菜单,Demo 地址: github.com/EyreFree/EF… 1. 注意事项 3D Touch 只在 iOS 9 及以上版本得到支持,之前版本的 iOS 并不支持该功能; 3D Touch 只在 iPhone 6s 及以后型号...
  • 3D Touch大法

    2016-09-12 15:09:25
    3D Touch最先应用在Apple Watch上面,但叫Force Touch,后在iPhone6s上加入了此特性,并改名3D Touch。值得注意的是目前3D Touch只支持iPhone6S以后的机型,包括现有Xcode7中6s的模拟器也不支持,不过Github上的...
1 2 3 4 5 ... 20
收藏数 1,289
精华内容 515
关键字:

3d swift4 touch