iMessage应用开发:贴纸、信息拓展与交互会话实现
1. 为现有应用添加iMessage应用扩展
在iMessage应用开发中,有时候我们需要为现有的iOS应用添加iMessage应用扩展,以便能够发送自定义贴纸并为现有的iMessage应用提供额外功能。以下是具体的操作步骤:
1. 打开Xcode中的项目。
2. 为项目添加一个类型为iMessage扩展的新目标。
3. 在下一个屏幕中,输入扩展的产品名称和其他信息。
4. 将新扩展保存到磁盘并添加到项目中。
在创建扩展时,添加所需的图标是重要的一步,这样它们才能在iMessage应用列表中正确显示。虽然没有图标扩展也能正常工作和测试,但如果没有合适的图标,扩展将无法被接受进入iMessage应用商店。
2. 在贴纸包应用中利用扩展视图
默认情况下,应用在iMessage窗口中渲染自身的空间可能不够大,我们可以通过以下步骤创建一个允许用户扩展其视图的iMessage应用扩展:
1. 打开Xcode,确保你有一个带有iMessage扩展的应用。
2. 打开扩展的MainInterface.storyboard文件,将一个集合视图控制器和一个普通视图控制器拖到场景中。将集合视图控制器的类设置为StickersViewController,普通视图控制器的类设置为ExpandedStickersViewController。
3. 创建一个类型为UICollectionViewController的新Cocoa Touch类,命名为StickersViewController,不创建XIB文件,在IB的Identity inspector中将StickersViewController设置为该视图控制器的故事板ID。
4. 创建另一个类型为UIViewController的Cocoa Touch类,命名为ExpandedStickersViewController,在IB的Identity inspector中将ExpandedStickersViewController设置为该视图控制器的故事板ID。
5. 选择故事板集合视图控制器,在IB中已为你创建的单元格中拖放一个UIButton实例,将其文本设置为简单的“+”,并增大字体,使其对普通用户可见,同时在IB中将此单元格的重用标识符设置为Cell。
6. 确保放置在单元格上的按钮没有启用用户交互,否则它将捕获所有触摸事件,我们希望通过父集合视图控制器捕获触摸事件。选择按钮后,转到IB的Attributes inspector,取消选择“User Interaction Enabled”复选框。
7. 打开StickersViewController.swift文件,为集合视图控制器定义一个协议,以便任何其他类都可以成为其委托:
import UIKit
protocol StickersViewControllerDelegate : class{
func plusButtonTappedOn(controller: UIViewController)
}
protocol HasStickersDelegate : class{
weak var delegate: StickersViewControllerDelegate? {get set}
}
class StickersViewController: UICollectionViewController, HasStickersDelegate {
weak var delegate: StickersViewControllerDelegate?
//...
}
- 为集合视图提供足够的信息以显示单个单元格:
private let reuseIdentifier = "Cell"
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
return cell
}
- 确保当“+”单元格被点击时,将其报告给委托:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard indexPath.row == 0 && indexPath.section == 0 else {return}
delegate?.plusButtonTappedOn(controller: self)
}
- 转到MessagesViewController.swift文件,定义刚刚创建的两个视图控制器的故事板标识符:
import UIKit
import Messages
struct Identifiers{
static let StickersViewController = "StickersViewController"
static let ExpandedStickersViewController = "ExpandedStickersViewController"
}
- 扩展UIViewController,添加一个将任何视图控制器添加到Messages应用视图控制器的函数:
extension UIViewController{
func addTo(appViewController host: MSMessagesAppViewController){
if let delegate = host as? StickersViewControllerDelegate, let vc = self as? HasStickersDelegate{
vc.delegate = delegate
}
willMove(toParentViewController: host)
host.addChildViewController(self)
view.frame = host.view.bounds
view.translatesAutoresizingMaskIntoConstraints = false
host.view.addSubview(view)
view.leftAnchor.constraint(equalTo: host.view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: host.view.rightAnchor).isActive = true
view.topAnchor.constraint(equalTo: host.view.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: host.view.bottomAnchor).isActive = true
didMove(toParentViewController: host)
}
}
- 为MessagesViewController类添加一个名为loadViewController(forPresentationStyle:)的函数,根据传入的MSMessagesAppPresentationStyle类型的展示样式加载相应的视图控制器:
class MessagesViewController : MSMessagesAppViewController, StickersViewControllerDelegate {
func loadViewController(forPresentationStyle: MSMessagesAppPresentationStyle) -> Bool{
childViewControllers.forEach{
$0.willMove(toParentViewController: nil)
$0.view.removeFromSuperview()
$0.removeFromParentViewController()
($0 as? HasStickersDelegate)?.delegate = nil
}
let vcId: String
switch presentationStyle{
case .compact:
vcId = Identifiers.StickersViewController
case .expanded:
vcId = Identifiers.ExpandedStickersViewController
case .transcript:
return false
}
guard let vc = storyboard?.instantiateViewController(withIdentifier: vcId) else {return false}
vc.addTo(appViewController: self)
return true
}
//...
}
- 实现集合视图控制器的委托方法:
func plusButtonTappedOn(controller: UIViewController) {
let _ = loadViewController(forPresentationStyle: .expanded)
requestPresentationStyle(.expanded)
}
- 根据报告的展示样式加载适当的视图控制器:
override func willBecomeActive(with conversation: MSConversation) {
let _ = loadViewController(forPresentationStyle: .compact)
}
override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
let _ = loadViewController(forPresentationStyle: presentationStyle)
}
override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
let _ = loadViewController(forPresentationStyle: presentationStyle)
}
运行项目后,你会在列表中看到一个加号按钮,用户点击该按钮后,扩展将请求扩展展示样式,系统会在导航栏上提供一个条形按钮项,点击它可将扩展恢复到紧凑模式。
3. 为贴纸附加丰富信息
如果想在iMessage应用中的贴纸和消息上附加额外信息,如标题、副标题等,可以按照以下步骤操作:
1. 创建一个MSMessage实例。
2. 创建一个类型为MSMessageTemplateLayout的布局对象,并设置其属性,如图像和标题。
3. 模板准备好后,将其设置为消息对象的模板属性。
4. 使用MSConversation类型的活动对话对象的insert(_:completionHandler:)函数将消息发送到当前对话。
以下是具体的代码实现:
func plusButtonTappedOn(controller: UIViewController) {
guard let conversation = activeConversation else {fatalError()}
let session = conversation.selectedMessage?.session ?? MSSession()
let message = MSMessage(session: session)
let layout = MSMessageTemplateLayout()
layout.image = messageImage
layout.caption = "Caption"
layout.imageTitle = "Image title"
layout.imageSubtitle = "Image subtitle"
layout.trailingCaption = "Trailing caption"
layout.subcaption = "Subcaption"
layout.trailingSubcaption = "Trailing subcaption"
message.layout = layout
conversation.insert(message) {error in
// empty for now
}
}
var messageImage: UIImage? {
guard let image = UIImage(named: "Accounts") else {return nil}
let rect = image.size.rectWithZeroOrigin
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image {context in
let bgColor: UIColor = .black
bgColor.setFill()
context.fill(rect)
image.draw(at: .zero)
}
}
MSMessageTemplateLayout类有许多有用的属性,如下表所示:
| 属性 | 描述 |
| ---- | ---- |
| image: UIImage? | 作为消息发送的实际图像,可选属性 |
| caption: String? | iMessage会在图像底部插入一个彩色条,用于渲染标题,这是可以添加到图像的额外信息 |
| subcaption: String? | 显示在标题下方的额外条中 |
| imageTitle: String? | 显示在图像底部,白色字体,确保图像背景不是白色 |
| imageSubtitle: String? | 显示在标题下方,位于图像左下角 |
| trailingCaption: String? | 显示在右下角 |
| trailingSubcaption: String? | 显示在右下角,位于trailingCaption下方 |
下面是实现为贴纸附加丰富信息的流程mermaid图:
graph LR
A[创建MSMessage实例] --> B[创建MSMessageTemplateLayout布局对象]
B --> C[设置布局对象属性]
C --> D[将布局对象设置为消息模板属性]
D --> E[获取活动对话对象]
E --> F[发送消息到当前对话]
4. 创建iMessage应用的交互式对话
在iMessage应用中,若想让用户发送的数据(如图像或文本)在接收方能够被更改并替换现有消息,而不是发送新消息,可以按以下步骤实现:
4.1 整体思路
在扩展的MSMessagesAppViewController实例中,查看activeConversation.selectedMessage属性是否已设置。若已设置,则存在之前由iMessage应用发送的选定消息。找到该选定消息后,使用其url属性创建该消息的可变实例。
4.2 具体实现步骤
4.2.1 修改视图控制器界面
在Recipe 21.6的基础上,当用户点击之前由应用发送的消息时,会触发MSMessagesAppViewController的willTransition(to:)函数,将应用切换到扩展模式,同时activeConversation.selectedMessage属性会被设置为代表选定消息的MSMessage实例。此时,修改视图控制器界面,添加一个按钮,如下代码所示:
import UIKit
class ExpandedStickersViewController: UIViewController, HasStickersDelegate {
weak var delegate: StickersViewControllerDelegate?
@IBAction func appendButtonTapped(_ sender: AnyObject) {
delegate?.plusButtonTappedOn(self)
}
}
4.2.2 扩展URL类
为了方便计算URL的路径组件数量,扩展URL类,添加pathCount属性:
extension URL{
var pathCount: Int{
let components = NSURLComponents(url: self, resolvingAgainstBaseURL: false)
return components?.path?
.components(separatedBy: "/")
.filter{$0.characters.count > 0}
.count ?? 0
}
}
4.2.3 实现plusButtonTappedOn委托方法
在plusButtonTappedOn(controller:)委托方法中,根据是否存在之前选定的消息,构建不同的URL和标题,并发送消息:
func plusButtonTappedOn(controller: UIViewController) {
let paths = [
"library/", "prerelease/", "ios/",
"releasenotes/", "General/", "WhatsNewIniOS/"
]
let base = "developer.apple.com/"
guard let conversation = activeConversation else {fatalError()}
let session = conversation.selectedMessage?.session ?? MSSession()
let url: URL
let caption: String?
if let selectedMessageUrl = conversation.selectedMessage?.url{
let pathCount = selectedMessageUrl.pathCount
if pathCount < paths.count{
let lastPath = paths[pathCount]
url = selectedMessageUrl.appendingPathComponent(lastPath)
caption = "\(base) (\(lastPath))"
} else if let lastPath = paths.last {
url = selectedMessageUrl
caption = "\(base) (\(lastPath))"
} else {
return
}
} else {
url = URL(string: "https://\(base)")!
caption = base
}
let message = MSMessage(session: session)
let layout = MSMessageTemplateLayout()
layout.image = messageImage
layout.caption = caption
message.layout = layout
message.url = url
conversation.insert(message) {[weak self]error in
guard let strongSelf = self else {return}
strongSelf.dismiss()
}
}
4.3 交互过程说明
- 当用户A向用户B发送第一条消息时,selectedMessage属性为nil,此时发送的URL为https://developer.apple.com ,并附带相应的图像和标题。
- 接收方点击该消息后,应用进入扩展模式,点击按钮可向URL追加新的路径组件。
- 最终构建的路径为library/prerelease/ios/releasenotes/General/WhatsNewIniOS/ ,最多可进行六次消息的来回切换。
下面是实现交互式对话的流程mermaid图:
graph LR
A[检查是否有选定消息] -->|有| B[获取选定消息URL]
A -->|无| C[构建初始URL]
B --> D[计算路径组件数量]
D -->|小于路径数组长度| E[追加新路径组件]
D -->|等于路径数组长度| F[使用最后一个路径组件]
C --> G[设置初始标题]
E --> H[设置新标题]
F --> I[设置最终标题]
G --> J[创建MSMessage实例]
H --> J
I --> J
J --> K[设置消息布局和URL]
K --> L[插入消息到对话]
L --> M[关闭扩展视图]
4.4 总结
通过以上步骤,我们可以实现iMessage应用的交互式对话功能,让用户能够在对话中对消息进行修改和更新,增强了应用的交互性和用户体验。在实际开发中,还可以根据具体需求对消息的内容和交互方式进行进一步的定制和扩展。
超级会员免费看
3360

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



