使用 Watson 和 IoT Platform 服务构建家庭助理移动应用程序(6)
 
- UID
- 1066743
|

使用 Watson 和 IoT Platform 服务构建家庭助理移动应用程序(6)
开发移动应用程序
因为此应用程序类似于一个聊天消息接口,所以我使用了流行的 iOS 用户界面小部件 JSQMessagesViewController。我可以使用一段类似的,但它是用 Objective C 编写的。不过它仍可提供有用的参考。
开发用户界面准备 Xcode 项目- 从 Xcode,单击 File > Project 创建一个单视图应用程序。 选择 Single View Application 并指定产品名称:Home Assistant。
- 转到项目文件夹并初始化 CocoaPods。生成了一个 Podfile。
- 将这行代码添加到该 Podfile 来安装 JSQMessagesViewController 小部件。
1
| pod 'JSQMessagesViewController'
|
- 运行下面的命令来安装 JSQMessagesViewController 依赖项。生成了一个 Xcode 工作区。应该使用 Workspace (*.wcworkspace) 而不是 Project (*.xcodeproj) 来重新打开 Xcode。
- 在 ViewController.swift 文件中,导入 JSQMessagesViewController 模块并将 ViewController 类更改为继承自 JSQMessagesViewController 类。声明消息数组来保存聊天消息。
1
2
3
4
| import JSQMessagesViewController
class ViewController: JSQMessagesViewController {
var messages = [JSQMessage]()
}
|
- 最后,在 Info.plist 中输入以下代码来启用麦克风。
1
2
| <key>NSMicrophoneUsageDescription</key>
<string>Need microphone to talk to Watson</string><
|
设置用户界面并创建一个扩展要设置用户界面并创建一个扩展,请执行以下操作:
- 创建一个名为 UIExt.swift 的文件,以包含所有 UI 相关逻辑作为 Swift 扩展。然后声明一个扩展。
1
2
| extension ViewController {
}
|
- 创建一个 SetupUI() 函数用于:
- 初始化 title、senderId 和 senderDisplayName。
- 创建一个麦克风按钮并向 callback 函数注册 touchDown 和 touchUpInside 事件。
- 注册一个菜单项(合成为 callback 函数)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| func setupUI() {
self.title = "Watson Chat"
self.senderId = UIDevice.current.identifierForVendor?.uuidString
self.senderDisplayName = UIDevice.current.identifierForVendor?.uuidString
JSQMessagesCollectionViewCell.registerMenuAction(#selector(synthesize(sender ))
// Create mic button
let microphoneImage = UIImage(named:"microphone")!
let microphoneButton = UIButton(type: .custom)
microphoneButton.setImage(microphoneImage, for: .normal)
microphoneButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
self.inputToolbar.contentView.leftBarButtonItem = microphoneButton
// Add press and release mic button
microphoneButton.addTarget(self, action:#selector(didPressMicrophoneButton), for: .touchDown)
microphoneButton.addTarget(self, action:#selector(didReleaseMicrophoneButton), for: .touchUpInside)
setAudioPortToSpeaker()
}
|
- 添加麦克风图标作为资产。单击 Assets.xcassets,将图标文件拖到工作区来创建图像集。
点击查看大图

实现用户界面的处理函数在 UIExt.swift 文件中:
- 添加以下函数来处理麦克风按钮按下事件。按下此按钮来说出命令时,它就会开始将信息传输到 Watson Speech to Text 服务。
1
2
3
4
5
6
7
8
9
| func didPressMicrophoneButton(sender: UIButton) {
let microphonePressedImage = UIImage(named:"microphone_pressed")!
sender.setImage(microphonePressedImage, for: .normal)
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
// Clear the input text
self.inputToolbar.contentView.textView.text = ""
// speech-to-text startStreaming
sttStartStreaming()
}
|
- 添加以下函数来处理麦克风按钮释放事件,这会停止向 Watson Speech to Text 服务传输数据。
1
2
3
4
5
6
| func didReleaseMicrophoneButton(sender: UIButton){
let microphoneImage = UIImage(named:"microphone")!
sender.setImage(microphoneImage, for: .normal)
// speech-to-text stop streaming
self.sttStopStreaming()
}
|
- 重写 didPressSend() 函数来处理发送按钮按下事件。该代码将消息附加到消息数组,然后向 Watson Conversation 服务发送一条请求并接收一条响应。
1
2
3
4
5
6
7
8
| override func didPressSend(
_ button: UIButton!,
withMessageText text: String!,
senderId: String!,
senderDisplayName: String!,
date: Date!) {
send(text)
}
|
重写 UI 回调函数在 UIExt.swift 文件中,添加以下函数:
- 重写 collectionView | numberOfItemsInSection 回调函数,以便返回数组中的消息数量。
1
2
3
4
| override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.messages.count
}
|
- 重写 collectionView | cellForItemAt 回调函数,该函数负责设置索引路径上的特定单元的文本并呈现它的颜色。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
let message = self.messages[indexPath.item]
if !message.isMediaMessage {
if message.senderId == self.senderId {
cell.textView.textColor = UIColor(R: 0x72, G: 0x9B, B: 0x79)
} else {
cell.textView.textColor = UIColor(R: 0x47, G: 0x5B, B: 0x63)
}
let attributes : [String:AnyObject] =
[NSForegroundColorAttributeName:cell.textView.textColor!, NSUnderlineStyleAttributeName: 1 as AnyObject]
cell.textView.linkTextAttributes = attributes
}
return cell
}
|
- 重写 collectionView | messageBubbleImageDataForItemAt 回调函数,以返回响应来表明它是索引路径上的特定单元的传入还是传出气泡。
1
2
3
4
5
6
7
8
9
10
| override func collectionView(_ collectionView: JSQMessagesCollectionView!,
messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let data = messages[indexPath.row]
switch(data.senderId) {
case self.senderId:
return self.outgoingBubble
default:
return self.incomingBubble
}
}
|
- 重写 collectionView | attributedTextForMessageBubbleTopLabelAt 回调函数,以便设置并返回发送者名称作为消息气泡上的标签(在样式属性旁边):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| override func collectionView(_ collectionView: JSQMessagesCollectionView!,
attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString! {
if let message = firstMessage(at: indexPath) {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.left
let attrs = [
NSParagraphStyleAttributeName: paragraphStyle,
NSBaselineOffsetAttributeName: NSNumber(value: 0),
NSForegroundColorAttributeName: UIColor(R: 0x1e, G: 0x90, B: 0xff)
]
return NSAttributedString(string: message.senderDisplayName, attributes: attrs)
} else {
return nil
}
}
|
- 重写 collectionView | heightForMessageBubbleTopLabelAt 回调函数,以便返回文本标签的高度。
1
2
3
4
5
6
7
8
9
| override func collectionView(_ collectionView: JSQMessagesCollectionView!,
layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!,
heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
if let _ = firstMessage(at: indexPath) {
return kJSQMessagesCollectionViewCellLabelHeightDefault
} else {
return 0.0
}
}
|
|
|
|
|
|
|