iOS应用功能增强:3D Touch、设置与图像滤镜
1. 3D Touch功能介绍
在iPhone 6S、6S Plus或任何iPhone 7机型上,可利用3D Touch功能,在SFSafariViewController中通过用力按压链接来快速预览链接内容。部分iOS设备能够检测并利用用户触摸屏幕时施加的压力,以提供对应用功能的快速访问,主要有以下两种使用方式:
1.1 主屏幕快速操作(Home Screen Quick Actions)
主屏幕快速操作是指用户用力按压应用图标时出现的菜单项,可让应用在后台快速执行操作或直接跳转到应用中的常用目标。主屏幕快速操作分为静态和动态两种:
-
静态操作
:不会改变,通过在应用的Info.plist中添加名为UIApplicationShortcutItems的数组来设置,数组中的每个字典包含以下项目:
| 项目 | 是否必需 | 说明 |
| ---- | ---- | ---- |
| UIApplicationShortcutItemType | 是 | 应用将接收的字符串 |
| UIApplicationShortcutItemTitle | 是 | 用户将看到的标签 |
| UIApplicationShortcutItemSubtitle | 否 | 用户将在标题下方看到的标签 |
| UIApplicationShortcutItemIconFile | 否 | 提供自己的文件 |
| UIApplicationShortcutIconType | 否 | 使用系统提供的图标 |
| UIApplicationShortcutItemUserInfo | 否 | 将传递给应用的字典 |
-
动态操作
:是UIApplicationShortcutItem对象的实例,应用的共享UIApplication对象有一个名为shortcutItems的数组,可控制该数组来添加快捷操作。
以下是添加一个静态操作以允许从主菜单创建新笔记的步骤:
1. 修改info.plist以支持创建笔记操作:
- 打开info.plist,插入一个新的键值对,键为UIApplicationShortcutItems,类型为数组。
- 将数组中的第一个且唯一的项目修改为字典。
- 在字典中添加一个新的键值对,键为UIApplicationShortcutItemTitle,值为字符串“New Note”。
- 添加另一个键值对,键为UIApplicationShortcutItemType,值为字符串“au.com.secretlab.Notes.new-note”,需将“au.com.secretlab.Notes”替换为应用的捆绑标识符。
- 最后,添加一个键值对,键为UIApplicationShortcutItemIconType,值为字符串“UIApplicationShortcutIconTypeCompose”。
2. 编写代码处理用户执行快速操作的情况:
// AppDelegate.swift
let createNoteActionType = "au.com.secretlab.Notes.new-note"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Did we launch as a result of using a shortcut option?
if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem {
// We did! Was it the 'create note' shortcut?
if shortcutItem.type == createNoteActionType {
// Create a new document.
createNewDocument()
}
// Return false to indicate that 'performActionForShortcutItem'
// doesn't need to be called
return false
}
return true
}
func createNewDocument() {
// Ensure that the root view controller is a navigation controller
guard let navigationController = self.window?.rootViewController as? UINavigationController else {
fatalError("The root view controller is not a navigationcontroller")
}
// Ensure that the navigation controller's root view controller is the
// Document List
guard let documentList = navigationController.viewControllers.first as? DocumentListViewController else {
fatalError("first view controller isn't DocumentListViewController")
}
// Move back to the root view controller
navigationController.popToRootViewController(animated: false)
// Ask the document list to create a new document
documentList.createDocument()
}
1.2 预览与弹出(Peek and Pop)
Peek and Pop功能允许用户通过用力按压屏幕上的元素来快速预览内容,若用户继续用力按压,内容将打开。以下是为应用添加Peek and Pop支持的步骤:
1. 在Storyboard中为DocumentViewController添加标识符:
- 打开main.storyboard,选择DocumentViewController。
- 打开检查器,转到身份检查器选项卡,在身份部分将DocumentViewController添加到Storyboard ID。
2. 注册文档列表视图控制器以支持Peek and Pop:
// DocumentListViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// Mark the collection view as preview-able, if our current device supports
// 3D Touch.
if self.traitCollection.forceTouchCapability == .available {
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
}
}
extension DocumentListViewController : UIViewControllerPreviewingDelegate {
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
// Determine which cell was tapped; if we can't
// return nil to indicate that we can't offer a preview
guard let indexPath = self.collectionView?.indexPathForItem(at: location) else {
return nil
}
// Get the cell object for this location
guard let cell = self.collectionView?.cellForItem(at: indexPath) else {
fatalError("We have an index path, but not a cell")
}
// Determine the document URL that this cell represents
let selectedItem = availableFiles[indexPath.item]
// Tell the previewing context about
// the shape that should remain unblurred
previewingContext.sourceRect = cell.frame
// Create a DocumentViewController for showing this file
guard let documentVC = self.storyboard?.instantiateViewController(withIdentifier: "DocumentViewController") as? DocumentViewController else {
fatalError("Expected to get a DocumentViewController here " +
"- make sure that the Document View Controller's" +
"storyboard identifier is set up correctly")
}
// Give the document URL to the document view controller
documentVC.documentURL = selectedItem
// Create a navigation controller to embed this in
let navigationVC = UINavigationController(rootViewController: documentVC)
// Return this navigation controller
return navigationVC
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
// The viewControllerToCommit is a navigation controller
// that contains a document view controller.
// Ensure that this is the case.
guard let navigationVC = viewControllerToCommit as? UINavigationController else {
fatalError("Expected the preview view controller" +
"to be a navigation controller")
}
guard let documentVC = navigationVC.viewControllers.first as? DocumentViewController else {
fatalError("View controller is not a document view controller")
}
// Get the document view controller's URL
guard let url = documentVC.documentURL else {
fatalError("Expected the document view controller" +
"to have a document set")
}
// Present the segue, just as if we'd tapped the cell
self.performSegue(withIdentifier: "ShowDocument", sender: url)
}
}
2. 设置功能添加
为应用添加一个设置项,用于控制文档打开时是否处于编辑状态。设置信息存储在UserDefaults类中,该类类似于一个持久化的大字典,即使应用退出也会保留数据。UserDefaults只能存储特定类型的对象,如字符串、数字、日期、数组、数据和字典,适用于存储非常小的信息。以下是添加设置项的步骤:
1. 创建设置包:
- 打开文件菜单,选择“New→File”。
- 添加一个新的“iOS→Resource→Settings Bundle”,命名为“Settings”并添加到Notes - iOS目标。
- 打开刚添加的Settings.bundle,打开其中的Root.plist文件。
- 移除偏好设置项列表中的所有项目。
- 选择偏好设置项并按Enter键创建一个新项。
- 将新项的类型设置为“Toggle Switch”。
- 将标题设置为“Documents are editable when opened.”。
- 将标识符设置为“document_edit_on_open”。
2. 让应用使用该偏好设置:
// DocumentViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
self.isEditing = UserDefaults.standard.bool(forKey: "document_edit_on_open")
}
3. 撤销支持功能
为文本视图添加撤销更改的支持,利用内置的撤销管理器。以下是实现步骤:
1. 在DocumentViewController中添加属性:
// DocumentViewController.swift
var undoButton : UIBarButtonItem?
var didUndoObserver : AnyObject?
var didRedoObserver : AnyObject?
- 在viewDidLoad中注册撤销或重做更改的通知:
// DocumentViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let respondToUndoOrRedo = { (notification: Notification) -> Void in
self.undoButton?.isEnabled = self.textView?.undoManager?.canUndo == true
}
didUndoObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSUndoManagerDidUndoChange, object: nil, queue: nil, using: respondToUndoOrRedo)
didRedoObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSUndoManagerDidRedoChange, object: nil, queue: nil, using: respondToUndoOrRedo)
}
- 在updateBarItems中更改撤销按钮的启用状态:
// DocumentViewController.swift
func updateBarItems() {
var rightButtonItems : [UIBarButtonItem] = []
rightButtonItems.append(self.editButtonItem)
if isEditing {
undoButton = UIBarButtonItem(barButtonSystemItem: .undo, target: self.textView?.undoManager, action: #selector(UndoManager.undo))
undoButton?.isEnabled = self.textView?.undoManager?.canUndo == true
rightButtonItems.append(undoButton!)
}
// the button to segue to the attachment view controller
let image = UIImage(named: "Position")
let showButton = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(showLocation))
// if there is already a location
if self.document?.locationWrapper != nil {
// we show the segue button
rightButtonItems.append(showButton)
} else {
// if we don't have permission or permission is denied
let status = CLLocationManager.authorizationStatus()
if status == .denied || status == .restricted {
// we don't have permission
rightButtonItems.append(showButton)
} else {
// the activity indicator to show when determining location
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
spinner.startAnimating()
let spinItem = UIBarButtonItem(customView: spinner)
rightButtonItems.append(spinItem)
}
}
self.navigationItem.rightBarButtonItems = rightButtonItems
}
- 在textViewDidChange中更新撤销按钮状态:
// DocumentViewController.swift
func textViewDidChange(_ textView: UITextView) {
self.undoButton?.isEnabled = self.textView.undoManager?.canUndo == true
document?.text = textView.attributedText
document?.updateChangeCount(.done)
}
- 在documentStateChanged方法中更新撤销按钮:
// DocumentViewController.swift
document.revert(toContentsOf: document.fileURL, completionHandler: { (success) -> Void in
self.textView.attributedText = document.text
self.attachmentsCollectionView?.reloadData()
self.updateBarItems()
})
for version in conflictedVersions {
version.isResolved = true
}
4. 图像滤镜功能添加
为应用添加用户对图像应用滤镜的功能,当用户查看图像时,屏幕底部将显示三个不同滤镜版本的图像按钮,点击按钮可将主图像视图更改为显示滤镜版本的图像。将使用Core Image框架来应用滤镜,该框架提供图像过滤、增强、编辑等有用的无损或基于管道的图像编辑功能。以下是实现步骤:
1. 在界面中添加UIView和按钮:
- 打开Main.storyboard,进入图像附件视图控制器。
- 添加一个UIView,使其填充屏幕宽度,高度为80点。
- 将其固定在底部和两侧。
- 将其背景颜色设置为黑色,透明度为0.5。
- 添加三个按钮,分别放置在左侧、中间和右侧,使其与包含它们的视图高度相同且为正方形。
- 将按钮的内容模式设置为“aspect fit”。
2. 在代码中实现滤镜功能:
// ImageAttachmentViewController.swift
@IBOutlet var filterButtons: [UIButton]!
@IBAction func showFilteredImage(_ sender: UIButton) {
self.imageView?.image = sender.image(for: UIControlState())
self.imageView?.contentMode = .scaleAspectFit
}
func prepareFilterPreviews() {
let filters : [CIFilter?] = [
CIFilter(name: "CIPhotoEffectChrome"),
CIFilter(name: "CIPhotoEffectNoir"),
CIFilter(name: "CIPhotoEffectInstant")
]
guard let image = self.imageView?.image else {
return
}
let context = CIContext(options: nil)
for (number, filter) in filters.enumerated() {
let button = filterButtons[number]
let unprocessedImage = CIImage(image: image)
filter?.setValue(unprocessedImage, forKey: kCIInputImageKey)
if let processedCIImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
// Render the result into a CGImage
let image = context.createCGImage(processedCIImage, from: CGRect(origin: CGPoint.zero, size: image.size))
button.setImage(UIImage(cgImage: image!), for: UIControlState())
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// If we have data, and can make an image out of it...
if let data = attachmentFile?.regularFileContents, let image = UIImage(data: data) {
// Set the image
self.imageView?.image = image
prepareFilterPreviews()
}
}
通过以上步骤,我们为iOS应用添加了3D Touch、设置、撤销和图像滤镜等功能,提升了应用的用户体验和实用性。
5. 各功能操作流程总结
为了更清晰地展示前面介绍的各项功能的操作步骤,下面以流程图和表格的形式进行总结。
5.1 主屏幕快速操作添加流程
graph LR
A[修改info.plist] --> B[插入UIApplicationShortcutItems数组]
B --> C[将数组首项设为字典]
C --> D[添加UIApplicationShortcutItemTitle键值对]
D --> E[添加UIApplicationShortcutItemType键值对]
E --> F[添加UIApplicationShortcutItemIconType键值对]
F --> G[编写AppDelegate.swift代码]
G --> H[添加createNoteActionType属性]
H --> I[在application didFinishLaunchingWithOptions中检查快捷操作]
I --> J[编写createNewDocument函数]
5.2 各功能操作步骤对比
| 功能 | 操作步骤 |
|---|---|
| 主屏幕快速操作 | 1. 修改info.plist;2. 编写AppDelegate.swift代码 |
| Peek and Pop | 1. 在Storyboard中添加标识符;2. 注册文档列表视图控制器;3. 实现委托方法 |
| 设置功能 | 1. 创建设置包;2. 让应用使用偏好设置 |
| 撤销支持 | 1. 添加属性;2. 注册通知;3. 更新按钮状态 |
| 图像滤镜 | 1. 在界面添加UIView和按钮;2. 编写代码实现滤镜功能 |
6. 功能实现注意事项
在实现上述功能时,有一些注意事项需要我们关注,以确保功能的正常运行。
6.1 3D Touch功能
- 设备兼容性 :3D Touch功能仅在支持该功能的设备上可用,如iPhone 6S、6S Plus和iPhone 7系列。在代码中需要进行设备检测,避免在不支持的设备上出现错误。
- 模拟器模拟 :虽然可以使用配备Force Trackpad的Macbook在iOS模拟器中模拟3D Touch,但这只是一种近似模拟,与真实设备的体验仍有差异,建议在真实设备上进行测试。
6.2 设置功能
- UserDefaults存储类型 :UserDefaults只能存储特定类型的对象,如字符串、数字、日期、数组、数据和字典。在存储和读取设置信息时,要确保数据类型的正确性。
6.3 图像滤镜功能
- Core Image框架 :使用Core Image框架时,要注意滤镜的名称和参数的正确性。可以参考Core Image Filter Reference获取完整的滤镜列表和使用说明。
- 图像转换 :在将处理后的CIImage转换为UIImage时,建议先转换为CGImage,再转换为UIImage,避免出现图像显示异常的问题。
7. 功能扩展建议
除了上述已经实现的功能,还可以对应用进行进一步的扩展,以提升用户体验和应用的实用性。
7.1 3D Touch功能扩展
- 增加更多快速操作 :可以在主屏幕快速操作中添加更多的菜单项,如快速分享、快速搜索等,方便用户快速访问常用功能。
- 优化Peek and Pop体验 :可以在预览界面中添加更多的交互元素,如分享按钮、收藏按钮等,让用户在预览时就能进行一些操作。
7.2 设置功能扩展
- 增加更多设置项 :可以添加更多的设置项,如字体大小、主题颜色等,让用户可以根据自己的喜好定制应用的外观和功能。
- 同步设置信息 :可以将设置信息同步到云端,让用户在不同设备上都能使用相同的设置。
7.3 图像滤镜功能扩展
- 增加更多滤镜效果 :可以添加更多的滤镜效果,如复古滤镜、艺术滤镜等,满足用户不同的审美需求。
- 支持滤镜组合 :允许用户将多个滤镜组合使用,创造出独特的图像效果。
通过以上的功能扩展建议,可以进一步提升应用的竞争力和用户满意度。在实际开发中,可以根据应用的需求和用户的反馈,选择合适的扩展方向进行开发。
超级会员免费看
28

被折叠的 条评论
为什么被折叠?



