23、3D Touch 功能的实现与应用

3D Touch 功能的实现与应用

1. 检测压力

若要检测用户按压屏幕的压力,需先确保设备支持 3D Touch。可在以下环境中模拟 3D Touch:
- 在配备触摸板的笔记本 Macintosh 或带有 Magic Trackpad 的台式 Macintosh 上运行模拟器。
- 通过 USB 线将 iPhone 6s 或更高版本连接到 Macintosh 上。

检测 3D Touch 压力的两个属性为 “force” 和 “maximumPossibleForce”。“force” 属性测量当前压力值,“maximumPossibleForce” 属性定义 iOS 可识别的最大压力。

以下是检测压力的实现步骤:
1. 在 Main.storyboard 文件中拖放一个 UILabel。
2. 打开 Assistant Editor,从 UILabel 按住 Control 键拖动到 ViewController.swift 文件,创建一个名为 forceLabel 的 IBOutlet:

@IBOutlet var forceLabel: UILabel!
  1. 修改 touchesMoved 函数:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
                let force = touch.force/touch.maximumPossibleForce
                forceLabel.text = "\(force * 100)% force"
            } else {
                print ("3D Touch not available")
            }
        } else {
            print ("Need iOS 9 or higher")
        }
    }
}

此修改后的代码从 touch.force 属性获取屏幕压力,将其除以 maximumPossibleForce 属性,最后在应用界面的标签中以百分比形式显示该值。运行应用时,可按压 Macintosh 触控板或 iPhone 屏幕,查看按压力度。

2. 创建主屏幕快速操作

快速操作让用户可通过不同选项打开应用,为应用常用功能提供快捷方式。例如,在主屏幕上对 Safari 图标使用快速操作,可选择打开新标签、隐私标签、阅读列表或书签列表。

创建快速操作分为两部分:

2.1 定义快速操作菜单

在 info.plist 文件的字典中定义多个字符串,创建最多四个快速操作菜单。每个快速操作可显示标题、副标题和图标。具体操作如下:
1. 点击 Navigator 面板中的 Info.plist 文件,显示应用的信息属性列表。
2. 点击属性列表中任意键项右侧的上下箭头图标,出现 “+” 和 “-” 图标。
3. 点击 “+” 键创建一个新的属性列表项,命名为 UIApplicationShortcutItems,确保其类型为数组。
4. 重复上述步骤创建两个项目,编号为 Item 0 和 Item 1,创建两个主屏幕快速操作。
5. 在每个项目下创建三个额外的行,分别命名为:
- UIApplicationShortcutItemTitle:定义快速操作快捷方式的标题。
- UIApplicationShortcutItemSubtitle:定义快速操作快捷方式的副标题,以较小字体显示在标题下方(可选)。
- UIApplicationShortcutItemType:定义创建快速操作快捷菜单所需的字符串。

以下是创建两个快速操作快捷方式的示例:
| Key | Type | Value |
| — | — | — |
| UIApplicationShortcutItems | Array | (2 items) |
| Item 0 | Dictionary | (3 items) |
| UIApplicationShortcutItemTitle | String | View |
| UIApplicationShortcutItemSubtitle | String | View favorite items |
| UIApplicationShortcutItemType | String | $(PRODUCT_BUNDLE_IDENTIFIER).First |
| Item 1 | Dictionary | (3 items) |
| UIApplicationShortcutItemTitle | String | Share |
| UIApplicationShortcutItemSubtitle | String | Share items with friends |
| UIApplicationShortcutItemType | String | $(PRODUCT_BUNDLE_IDENTIFIER).Second |

2.2 编写 Swift 代码处理快速操作

在 AppDelegate.swift 文件中编写 Swift 代码处理每个快速操作。首先,创建一个枚举来标识每个快速操作项:

enum MenuItems: String {
    case First
    case Second
    init?(fullType: String) {
        guard let last = fullType.components(separatedBy: ".").last else { return nil }
        self.init(rawValue: last)
    }
    // MARK: Properties
    var type: String {
        return Bundle.main.bundleIdentifier! + ".\(self.rawValue)"
    }
}

然后,在 AppDelegate.swift 文件中创建一个 UIApplicationShortcutItem 类型的变量:

var launchedShortcutItem: UIApplicationShortcutItem?

接着,编写两个应用函数:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
        launchedShortcutItem = shortcutItem
    }
    return true
}

func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
    completionHandler(handleShortCutItem(shortcutItem))
}

最后,编写 handleShortCutItem 函数:

func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) -> Bool {
    var handled = false
    guard MenuItems(fullType: shortcutItem.type) != nil else {
        return false
    }
    guard let shortCutType = shortcutItem.type as String? else {
        return false
    }        
    switch (shortCutType) {
    case MenuItems.First.type:
        print ("View favorites")
        handled = true
    case MenuItems.Second.type:
        print ("Share")
        handled = true
    default:
        break
    }
    let alertController = UIAlertController(title: "Shortcut Chosen", message: "\"\(shortcutItem.localizedTitle)\"", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
    alertController.addAction(okAction)
    window!.rootViewController?.present(alertController, animated: true, completion: nil)
    return handled
}

3. 添加动态主屏幕快速操作

之前定义的快速操作为静态操作,始终显示。动态快速操作可在应用运行后通过 Swift 代码创建,使快速操作菜单根据用户当前操作显示不同选项。

注意,快速操作最多只能有四个,如一个静态快速操作和三个动态快速操作,或四个静态快速操作和零个动态快速操作。

添加动态快速操作的步骤如下:
1. 修改枚举,为每个要添加的动态快速操作添加一个枚举项:

enum MenuItems: String {
    case First
    case Second
    case Third
    case Fourth
    init?(fullType: String) {
        guard let last = fullType.components(separatedBy: ".").last else { return nil }
        self.init(rawValue: last)
    }
    // MARK: Properties
    var type: String {
        return Bundle.main.bundleIdentifier! + ".\(self.rawValue)"
    }
}
  1. 修改 existing application didFinishLaunchingWithOptions 函数:
    • 定义每个动态快速操作:
let shortcut3 = UIMutableApplicationShortcutItem(type: MenuItems.Third.type, localizedTitle: "Play", localizedSubtitle: "Play audio", icon: UIApplicationShortcutIcon(type: .play))
let shortcut4 = UIMutableApplicationShortcutItem(type: MenuItems.Fourth.type, localizedTitle: "Add", localizedSubtitle: "Add an item", icon: UIApplicationShortcutIcon(type: .add))
- 将动态快速操作添加到 shortcutItems 数组:
application.shortcutItems = [shortcut3, shortcut4]

完整的 AppDelegate.swift 文件如下:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var launchedShortcutItem: UIApplicationShortcutItem?
    enum MenuItems: String {
        case First
        case Second
        case Third
        case Fourth
        init?(fullType: String) {
            guard let last = fullType.components(separatedBy: ".").last else { return nil }
            self.init(rawValue: last)
        }
        // MARK: Properties
        var type: String {
            return Bundle.main.bundleIdentifier! + ".\(self.rawValue)"
        }
    }

    func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) -> Bool {
        var handled = false
        guard MenuItems(fullType: shortcutItem.type) != nil else {
            return false
        }
        guard let shortCutType = shortcutItem.type as String? else {
            return false
        }        
        switch (shortCutType) {
        case MenuItems.First.type:
            print ("View favorites")
            handled = true
        case MenuItems.Second.type:
            print ("Share")
            handled = true
        default:
            break
        }
        let alertController = UIAlertController(title: "Shortcut Chosen", message: "\"\(shortcutItem.localizedTitle)\"", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(okAction)
        window!.rootViewController?.present(alertController, animated: true, completion: nil)
        return handled
    }

    func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
        completionHandler(handleShortCutItem(shortcutItem))
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
            launchedShortcutItem = shortcutItem
        }
        if let shortcutItems = application.shortcutItems, shortcutItems.isEmpty {
            let shortcut3 = UIMutableApplicationShortcutItem(type: MenuItems.Third.type, localizedTitle: "Play", localizedSubtitle: "Play audio", icon: UIApplicationShortcutIcon(type: .play))
            let shortcut4 = UIMutableApplicationShortcutItem(type: MenuItems.Fourth.type, localizedTitle: "Add", localizedSubtitle: "Add an item", icon: UIApplicationShortcutIcon(type: .add))
            application.shortcutItems = [shortcut3, shortcut4]
        }
        return true
    }
}

通过修改 shortcutItems 数组,应用可动态更改快速操作菜单中的快速操作,如添加或删除项目,但总数不能超过四个。

4. 添加窥视、弹出和预览功能

3D Touch 的最后一个用途是为项目添加窥视功能。窥视让用户按压项目以聚焦该项目,按住手指会弹出该项目的小视图,预览则可查看执行任务的菜单项。

4.1 创建项目和界面

创建一个新的 Single View App iOS 项目,命名为 3D PeekPop。点击 Main.storyboard 文件,在视图中间放置一个 UIButton,设置描述性标题,如 “Touch Me to Peek”,并更改背景颜色。最后选择 “Editor ➤ Resolve Auto Layout Issues ➤ Reset to Suggested Constraints” 创建简单用户界面。

4.2 实现窥视、弹出和预览功能

  1. 打开 Assistant Editor,从按钮按住 Control 键拖动创建一个名为 peekButton 的 IBOutlet。
  2. 让视图控制器采用 UIViewControllerPreviewingDelegate 协议:
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
    @IBOutlet var peekButton: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        if traitCollection.forceTouchCapability == .available {
            registerForPreviewing(with: self, sourceView: view)
        }
    }
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        guard let showMyView = storyboard?.instantiateViewController(withIdentifier: "PeekVC"), peekButton.frame.contains(location) else {
            return nil
        }
        showMyView.preferredContentSize = CGSize(width: 0.0, height: 300.0)
        return showMyView
    }
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
        show(viewControllerToCommit, sender: self)
    }
}

4.3 创建第二个视图和代码文件

  1. 在 Navigator 面板中点击 Main.storyboard 文件,拖放一个 View Controller 到现有视图旁边。
  2. 在新视图顶部附近拖动一个 UILabel,调整大小,设置标题为 “Touch Me to Peek”,并更改背景颜色。
  3. 点击第二个视图顶部的黄色圆圈,选择 “View ➤ Utilities ➤ Show Identity Inspector”,在 Storyboard ID 中输入 “PeekVC”。
  4. 创建一个名为 ViewControllerPeek 的 Cocoa Touch Class 文件,确保它是 UIViewController 的子类并使用 Swift 语言。
  5. 在 Identity Inspector 面板中,点击 Class 列表框,选择 ViewControllerPeek,将视图与 ViewControllerPeek.swift 文件连接。

4.4 定义预览菜单项

在 ViewControllerPeek.swift 文件中定义预览菜单项:

import UIKit
class ViewControllerPeek: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    override var previewActionItems : [UIPreviewActionItem] {
        let defaultAction = UIPreviewAction(title: "Default style", style: .default) { (action, viewController) -> Void in
            print("Default")
        }
        let selectAction = UIPreviewAction(title: "Selected style", style: .selected) { (action, viewController) -> Void in
            print("Selected")
        }
        let destructiveAction = UIPreviewAction(title: "Destructive style", style: .destructive) { (action, viewController) -> Void in
            print("Destructive")
        }
        return [defaultAction, selectAction, destructiveAction]
    }
}

通过以上步骤,可实现 3D Touch 的各种功能,为应用增添更多交互性和便捷性。

5. 3D Touch 功能实现流程总结

为了更清晰地展示 3D Touch 各项功能的实现步骤,下面通过 mermaid 流程图和表格的形式进行总结。

5.1 检测压力功能流程图

graph LR
    A[确保设备支持 3D Touch] --> B[模拟运行环境选择]
    B --> |笔记本 Mac 或台式 Mac 模拟器| C[在模拟器运行]
    B --> |iPhone 6s 及以上连接 Mac| D[在 iPhone 运行]
    C --> E[在 Main.storyboard 拖放 UILabel]
    D --> E
    E --> F[创建 forceLabel 的 IBOutlet]
    F --> G[修改 touchesMoved 函数]
    G --> H[运行应用查看按压力度]

5.2 快速操作功能实现步骤表格

功能 步骤 操作内容
创建主屏幕快速操作 定义菜单 1. 点击 Info.plist 文件
2. 点击上下箭头,出现 “+” 和 “-” 图标
3. 点击 “+” 键创建 UIApplicationShortcutItems 数组
4. 创建两个项目 Item 0 和 Item 1
5. 在每个项目下创建三个额外行并命名
编写代码处理 1. 创建枚举标识快速操作项
2. 创建 UIApplicationShortcutItem 类型变量
3. 编写两个应用函数
4. 编写 handleShortCutItem 函数
添加动态主屏幕快速操作 修改枚举 为每个动态快速操作添加枚举项
修改函数 1. 定义每个动态快速操作
2. 将动态快速操作添加到 shortcutItems 数组

5.3 窥视、弹出和预览功能流程图

graph LR
    A[创建 3D PeekPop 项目] --> B[在 Main.storyboard 放置 UIButton]
    B --> C[设置按钮标题和背景颜色]
    C --> D[创建 peekButton 的 IBOutlet]
    D --> E[视图控制器采用 UIViewControllerPreviewingDelegate 协议]
    E --> F[创建第二个视图和代码文件]
    F --> G[定义预览菜单项]
    G --> H[实现窥视、弹出和预览功能]

6. 代码优化与注意事项

6.1 代码优化建议

  • 检测压力功能 :可以添加更多的错误处理机制,例如当获取压力值失败时,给出更详细的错误提示,而不仅仅是打印信息。
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
                do {
                    let force = try getForceValue(from: touch)
                    forceLabel.text = "\(force * 100)% force"
                } catch {
                    print("获取压力值失败: \(error.localizedDescription)")
                }
            } else {
                print ("3D Touch not available")
            }
        } else {
            print ("Need iOS 9 or higher")
        }
    }
}

func getForceValue(from touch: UITouch) throws -> CGFloat {
    guard touch.maximumPossibleForce > 0 else {
        throw NSError(domain: "ForceError", code: 1, userInfo: [NSLocalizedDescriptionKey: "最大可能压力值为 0"])
    }
    return touch.force / touch.maximumPossibleForce
}
  • 快速操作功能 :可以将一些重复的代码提取成函数,提高代码的复用性。例如将创建 UIAlertController 的代码提取成一个单独的函数。
func showAlertForShortcut(_ shortcutItem: UIApplicationShortcutItem) {
    let alertController = UIAlertController(title: "Shortcut Chosen", message: "\"\(shortcutItem.localizedTitle)\"", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
    alertController.addAction(okAction)
    window!.rootViewController?.present(alertController, animated: true, completion: nil)
}

func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) -> Bool {
    var handled = false
    guard MenuItems(fullType: shortcutItem.type) != nil else {
        return false
    }
    guard let shortCutType = shortcutItem.type as String? else {
        return false
    }        
    switch (shortCutType) {
    case MenuItems.First.type:
        print ("View favorites")
        handled = true
    case MenuItems.Second.type:
        print ("Share")
        handled = true
    default:
        break
    }
    showAlertForShortcut(shortcutItem)
    return handled
}

6.2 注意事项

  • 拼写问题 :在定义快速操作的图标类型时,要特别注意拼写,包括大小写。Key 列使用 UIApplicationShortcutItemIconType,Value 列使用 UIApplicationShortcutIconType 后跟图标名称。
  • 快速操作数量限制 :快速操作最多只能有四个,在添加或删除动态快速操作时,要确保总数不超过这个限制。
  • 代码兼容性 :涉及到 iOS 版本的代码,如 #available(iOS 9.0, *) ,要确保应用的目标用户群体的 iOS 版本支持该功能。

通过以上的优化和注意事项,可以让 3D Touch 功能的实现更加稳定和高效,为用户带来更好的交互体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值