StocksBar - 在mac菜单栏上看股票
缘起
最近A股趋势不错,错过了之前的底部坑。于是就想着做一个Mac状态栏应用,可以在工作的时候偶尔关注一下股票的行情。

主要的功能有
- 显示股票的当前价格、涨跌幅
- 搜索联想股票,添加到自选列表
- 拖拽编辑自选列表顺序
- 删除自选股股票
- 一些常用的配置选项,如轮询显示自选股、设置刷新间隔等
基本满足了我的日常需求。本文只要讲讲如何实现这个软件。
股票API
股票API用的新浪股票的接口:http://hq.sinajs.cn/list=sh601933 返回结果如下:
var hq_str_sh601933="永辉超市,9.390,9.480,9.400,9.650,9.310,9.400,9.420,45031643,427349791.000,38767,9.400,19100,9.390,26500,9.380,14000,9.370,20400,9.360,1200,9.420,9300,9.430,39200,9.440,122072,9.450,26300,9.460,2019-05-09,15:00:00,00";
可以一次请求多个股票的行情,参数用逗号分隔即可。https://hq.sinajs.cn/list=sh601933,sz000651 对应的返回值如下:
var hq_str_sh601933="永辉超市,9.390,9.480,9.400,9.650,9.310,9.400,9.420,45031643,427349791.000,38767,9.400,19100,9.390,26500,9.380,14000,9.370,20400,9.360,1200,9.420,9300,9.430,39200,9.440,122072,9.450,26300,9.460,2019-05-09,15:00:00,00";
var hq_str_sz000651="格力电器,51.880,52.330,51.800,52.520,50.520,51.790,51.800,73733501,3800419825.700,5300,51.790,62400,51.780,6000,51.770,8700,51.760,5946,51.750,113623,51.800,17400,51.810,6400,51.820,4000,51.830,2200,51.840,2019-05-09,15:00:03,00";
具体的字段说明如下:
| 字段索引 | 字段名称 | 数据实例 | 字段说明 |
|---|---|---|---|
| 0 | 股票简称 | 永辉超市 | |
| 1 | 今日开盘价 | 9.390 | |
| 2 | 昨日收盘价 | 9.480 | |
| 3 | 最近成交价 | 9.400 | 当前的股价(成交价格,可能为0) |
| 4 | 最高成交价 | 9.650 | 当天最高成交价格 |
| 5 | 最低成交价 | 9.310 | |
| 6 | 买入价 | 9.400 | |
| 7 | 卖出价 | 9.420 | |
| 8 | 成交数量 | 45031643 | 股为单位 |
| 9 | 成交金额 | 427349791.000 | |
| 10 | 买数量一 | 38767 | |
| 11 | 买价位一 | 9.400 | |
| 12 | 买数量二 | 19100 | |
| 13 | 买价位二 | 9.390 | |
| 14 | 买数量三 | 26500 | |
| 15 | 买价位三 | 9.380 | |
| 16 | 买数量四 | 14000 | |
| 17 | 买价位四 | 9.370 | |
| 18 | 买数量五 | 20400 | |
| 19 | 买价位五 | 9.360 | |
| 20 | 卖数量一 | 1200 | |
| 21 | 卖价位一 | 9.420 | |
| 22 | 卖数量二 | 9300 | |
| 23 | 卖价位二 | 9.430 | |
| 24 | 卖数量三 | 39200 | |
| 25 | 卖价位三 | 9.440 | |
| 26 | 卖数量四 | 122072 | |
| 27 | 卖价位四 | 9.450 | |
| 28 | 卖数量五 | 26300 | |
| 29 | 卖价位五 | 9.460 | |
| 30 | 日期 | 2019-05-09 | |
| 31 | 时间 | 15:00:00 | |
| 32 | 停牌状态 | 00 | 00-正常,01~05-各类停牌,07-暂停 |
我们对其中的股票名称、开盘价、当前价格、昨天收盘价等字段感兴趣,解析字符串的代码比较简单就不贴了。
定义对应的Model为
class Stock: NSObject, Codable {
var code: String
/// 股票简称
var symbol: String = "永辉超市"
/// 今日开盘价
var openPrice: Float = 0.0
/// 昨日收盘价
var lastClosedPrice: Float = 0.0
/// 最近成交价格
var current: Float = 10.24
/// 最高成交价
var high: Float = 0.0
/// 最低成交价
var low: Float = 0.0
init(code: String) {
self.code = code
}
}
搜索建议
新浪股票同样有一个搜索建议的api接口。https://suggest3.sinajs.cn/suggest/type=&key=,参数key就是关键字,如果是中文需要urlencode。
比如我们搜索格力电器,输入geli,返回结果如下:
var suggestvalue="格力电器,11,000651,sz000651,格力电器,,格力电器,99;格林美,11,002340,sz002340,格林美,,格林美,99;合力泰,11,002217,sz002217,合力泰,,合力泰,99;安徽合力,11,600761,sh600761,安徽合力,,安徽合力,99;18合力01,81,152035,sh152035,18合力01,,18合力01,99;18格力02,81,143870,sh143870,18格力02,,18格力02,99;18格力01,81,143869,sh143869,18格力01,,18格力01,99;PR合力01,81,124769,sh124769,PR合力01,,PR合力01,99;16合力01,81,112487,sz112487,16合力01,,16合力01,99;PR合力02,81,124770,sh124770,PR合力02,,PR合力02,99;格林国际控股,31,02700,02700,格林国际控股,,格林国际控股,99;香格里拉,31,00069,00069,香格里拉,,香格里拉,99;歌礼制药 B,31,01672,01672,歌礼制药 B,,歌礼制药 B,99;格菱控股,31,01318,01318,格菱控股,,格菱控股,99;格林货币B,24,004866,of004866,格林货币B,,格林货币B,99;博时合利货币,24,002960,of002960,博时合利货币,,博时合利货币,99;歌力思,11,603808,sh603808,歌力思,,歌力思,99;格林货币A,24,004865,of004865,格林货币A,,格林货币A,99;格力地产,11,600185,sh600185,格力地产,,格力地产,99;合力科技,11,603917,sh603917,合力科技,,合力科技,99;蒙古图格里克BRX,71,mntbrx,mntbrx,蒙古图格里克BRX,,蒙古图格里克BRX,99;乌克兰格里夫纳立陶宛立特参考汇率,71,uahltx,uahltx,乌克兰格里夫纳立陶宛立特参考汇率,,乌克兰格里夫纳立陶宛立特参考汇率,99;欧元蒙古图格里克,71,eurmnt,eurmnt,欧元蒙古图格里克,,欧元蒙古图格里克,99;美元蒙古图格里克,71,usdmnt,usdmnt,美元蒙古图格里克,,美元蒙古图格里克,99;瑞士法郎蒙古图格里克,71,chfmnt,chfmnt,瑞士法郎蒙古图格里克,,瑞士法郎蒙古图格里克,99;加拿大元乌克兰格里夫纳,71,caduah,caduah,加拿大元乌克兰格里夫纳,,加拿大元乌克兰格里夫纳,99;日元蒙古图格里克,71,jpymnt,jpymnt,日元蒙古图格里克,,日元蒙古图格里克,99;英镑蒙古图格里克,71,gbpmnt,gbpmnt,英镑蒙古图格里克,,英镑蒙古图格里克,99;加拿大元蒙古图格里克,71,cadmnt,cadmnt,加拿大元蒙古图格里克,,加拿大元蒙古图格里克,99;乌克兰格里夫纳RUX,71,uahrux,uahrux,乌克兰格里夫纳RUX,,乌克兰格里夫纳RUX,99;英镑乌克兰格里夫纳,71,gbpuah,gbpuah,英镑乌克兰格里夫纳,,英镑乌克兰格里夫纳,99;美元乌克兰格里夫纳,71,usduah,usduah,美元乌克兰格里夫纳,,美元乌克兰格里夫纳,99;乌克兰格里夫纳匈牙利福林参考汇率,71,uahhux,uahhux,乌克兰格里夫纳匈牙利福林参考汇率,,乌克兰格里夫纳匈牙利福林参考汇率,99;乌克兰格里夫纳波兰兹罗提参考汇率,71,uahplx,uahplx,乌克兰格里夫纳波兰兹罗提参考汇率,,乌克兰格里夫纳波兰兹罗提参考汇率,99;欧元乌克兰格里夫纳,71,euruah,euruah,欧元乌克兰格里夫纳,,欧元乌克兰格里夫纳,99;乌克兰格里夫纳BRX,71,uahbrx,uahbrx,乌克兰格里夫纳BRX,,乌克兰格里夫纳BRX,99;瑞士法郎乌克兰格里夫纳,71,chfuah,chfuah,瑞士法郎乌克兰格里夫纳,,瑞士法郎乌克兰格里夫纳,99;日元乌克兰格里夫纳,71,jpyuah,jpyuah,日元乌克兰格里夫纳,,日元乌克兰格里夫纳,99;乌克兰格里夫纳英镑,71,uahgbp,uahgbp,乌克兰格里夫纳英镑,,乌克兰格里夫纳英镑,99;招商盛合灵活混合C,21,004143,of004143,招商盛合灵活混合C,,招商盛合灵活混合C,99;格林伯盛混合C,21,004817,of004817,格林伯盛混合C,,格林伯盛混合C,99;银华合利债券,21,002306,of002306,银华合利债券,,银华合利债券,99;招商盛合灵活混合A,21,004142,of004142,招商盛合灵活混合A,,招商盛合灵活混合A,99;格林泓鑫纯债债券C,21,006185,of006185,格林泓鑫纯债债券C,,格林泓鑫纯债债券C,99;长信合利混合C,21,005306,of005306,长信合利混合C,,长信合利混合C,99;格林泓鑫纯债债券A,21,006184,of006184,格林泓鑫纯债债券A,,格林泓鑫纯债债券A,99;格林伯锐灵活配置混合A,21,006181,of006181,格林伯锐灵活配置混合A,,格林伯锐灵活配置混合A,99;格林伯元灵活配置混合A,21,004942,of004942,格林伯元灵活配置混合A,,格林伯元灵活配置混合A,99;格林伯锐灵活配置混合C,21,006182,of006182,格林伯锐灵活配置混合C,,格林伯锐灵活配置混合C,99;格林伯盛混合A,21,004816,of004816,格林伯盛混合A,,格林伯盛混合A,99";
可以看到返回的结构跟行情很类似,处理搜索的时候有一些细节要处理,比如在输入第二个字符的时候,应该把输入第一个字符的请求取消掉(如果结果还没有返回的话),以及停留多少秒就立马去处理请求的情况,这个在后续会讲到。
NSTableView
NSTableView 与 UITableView 有相似的地方,也有很多不同的地方。
滑动
NSTableView 本身不支持滑动,需要嵌套在 NSScrollView 中才能滑动。通过是直接在Storyboard或者Xib中进行布局,然后添加IBOutlet到ViewController中,也可以在代码中创建NSScrollView与NSTableView,设置 TableView为ScrollView的documentView。
scrollView = NSScrollView(frame: .zero)
scrollView.automaticallyAdjustsContentInsets = false
scrollView.drawsBackground = false
scrollView.hasVerticalScroller = true
scrollView.borderType = .noBorder
scrollView.backgroundColor = .clear
view.addSubview(scrollView)
scrollView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
上传代码创建了
tableView = NSTableView()
tableView.rowHeight = 40.0
tableView.backgroundColor = NSColor(white: 1, alpha: 0.6)
tableView.register(NSNib(nibNamed: "StockTableCellView", bundle: nil), forIdentifier: reuseIdentifier)
tableView.selectionHighlightStyle = .none
tableView.dataSource = self
tableView.delegate = self
tableView.floatsGroupRows = true
tableView.intercellSpacing = NSSize.zero
tableView.registerForDraggedTypes([dragType])
tableView.draggingDestinationFeedbackStyle = .gap
scrollView.documentView = tableView
let column = NSTableColumn()
column.width = view.bounds.width
tableView.headerView = nil
tableView.addTableColumn(column)
分隔符
通过设置tableView的 GridMaskStyle 可以设置分隔符的样式。但是不同于 UITableView有一个 tableFooterView的属性可以设置为空的视图可以将没有
纯代码 NSViewController
默认NSViewController 不能由代码实例化,如果 let controller = YourNSViewController 会报如下的错误信息
-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: ProjectName.YourNSViewController in bundle (null).
借助如下的代码
override func loadView() {
self.view = NSView()
}
修改视图的背景颜色
不同于 UIView, NSView 并不提供 backgroundColor属性来修改自身的背景颜色
view.wantsLayer = true
view.layer.backgroundColor = NSColor.white.cgColor