前言
上面文章提到了我做的Mac客户端Ai Chat - 你问我答。恰好我的开发机子是19款的Macbook Pro (苹果最后一款支持Touch Bar的机器)。你们有多久没用过这个Bar了?本着捣鼓的精神,我研究了如何使用它以及在此实时返回GPT的返回内容,实现了一个跑马灯效果,以下是实现方案,效果如下
实现方案
- 如何返回一个自定义的TouchBar
TouchBar的开发实际上只要遵循苹果App Kit固定的API,可以非常简单的实现你想要的自定义Toubar。
第一步在ViewController 或者 Window下面重写makeTouchBar方法,新建你的TouchBar。
open override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = .customBar
touchBar.defaultItemIdentifiers = [.textFieldItem]
return touchBar
}
然后重写public func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem?方法,构建你Toubar的UI,代码如下,由于Mac开发与iOS开发本质上没有太大区别,所以布局还是直接用SnapKit即可。
public func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItem.Identifier.textFieldItem:
let item = NSCustomTouchBarItem(identifier: .textFieldItem)
let textField = NSTextField(labelWithString: "🤖 Say Something")
self.textField = textField
textField.isEditable = false
textField.isBordered = false
textField.textColor = .white
textField.maximumNumberOfLines = 1
textField.backgroundColor = NSColor.clear
textField.alignment = .center
let scrollView = NSScrollView()
scrollView.documentView = textField
scrollView.hasHorizontalRuler = true
self.scrollView = scrollView
item.view = scrollView
scrollView.snp.makeConstraints { make in
make.left.equalTo(item.view.snp.left).offset(0)
make.top.equalTo(item.view.snp.top).offset(0)
make.bottom.equalTo(item.view.snp.bottom).offset(0)
make.right.equalTo(item.view.snp.right).offset(0)
}
textField.snp.makeConstraints { make in
make.left.equalTo(item.view.snp.left).offset(0)
make.top.equalTo(item.view.snp.top).offset(0)
make.bottom.equalTo(item.view.snp.bottom).offset(0)
}
return item
case NSTouchBarItem.Identifier.buttonItem:
let item = NSCustomTouchBarItem(identifier: identifier)
let button = NSButton(title: "Send Message", target: self, action: #selector(sendButtonTapped(_:)))
button.bezelColor = NSColor.systemBlue
item.view = button
return item
default:
return nil
}
}
同意第一响应
open override var acceptsFirstResponder: Bool {
return true
}
就这样我们启动Mac应用我们的自定义Toubar就构建好了,默认它会返回一句内容🤖️ Say Something
- 如何实现接受GPT流信息,并实现跑马灯效果
-
Open AI,GPT流式返回我不多赘述,很多文章都有提到Flutter如何实现OpenAi流式返回,我的Mac客户端是Flutter编写,只需要把OpenAi的接口设置为流接口,然后HttpClient使用流的方式返回即可。然后自定义一个插件,从Flutter返回流信息到Mac原生刷新TouchBar。
跑马灯效果其实实现起来和iOS原生是一样的,我是基于一个ScrollView无限长度的Label,然后计算Label的宽 如果大于TouchBar的最大宽度,每次刷新文本时候,自动滚到Label的末尾来做的 代码如下。
从插件更新文本
OpenAiStreamResPlugin.shareInstance.responseStreamText = { [weak flutterViewController] text in
autoreleasepool {
flutterViewController?.addText(text: text)
}
}
跑马灯效果
func addText(text: String) {
if text.count == 0 {
self.textField?.stringValue = "🤖 Say Something"
self.scrollView?.contentView()?.scroll(NSPoint(x: 0, y: 0))
return
}
var newText = text.trimmingCharacters(in: .whitespacesAndNewlines)
newText = newText.replacingOccurrences(of: "\n", with: "")
self.textField?.stringValue = newText
if let scrollView = self.scrollView {
self.textField?.sizeToFit()
let textF = self.textField!
if textF.frame.size.width > scrollView.frame.size.width {
self.scrollView?.contentView()?.scroll(NSPoint(x: textF.frame.size.width, y: 0))
}
}
}
就这样一个支持GPT实现返回信息的跑马灯TouchBar就完成了,是不是很简单?