你多久没用Mac的TouchBar了?教你如何在这实现GPT跑马灯效果

7,081 阅读2分钟

前言

上面文章提到了我做的Mac客户端Ai Chat - 你问我答。恰好我的开发机子是19款的Macbook Pro (苹果最后一款支持Touch Bar的机器)。你们有多久没用过这个Bar了?本着捣鼓的精神,我研究了如何使用它以及在此实时返回GPT的返回内容,实现了一个跑马灯效果,以下是实现方案,效果如下

output.gif

实现方案

  • 如何返回一个自定义的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就完成了,是不是很简单?

output.gif