Swift-制作一个macOS上的基金菜单栏程序
成品展示
准备工作
首先新建一个macOS上的APP,选择SwiftUI+AppKit LifeCycle即可,因为SwiftUI的lifecycle并不支持Catalina
https://zhuanlan.zhihu.com/p/87960477
https://zhuanlan.zhihu.com/p/88235396
开始开发
使用的API
-
http://fund.eastmoney.com/js/fundcode_search.js
所有基金代码名称类型
-
http://fundgz.1234567.com.cn/js/005827.js
返回基金代码、名称、净值日期、单位净值、估算值、估算增值率、估算时间
-
通过i:00000代表查询的市场代码,对应该网址http://quote.eastmoney.com/center/qqzs.html
-
某基金某时间段净值及日增长率,去掉sdate和edate可以查询最新20天的
菜单栏UI
popover
准备工作中的两个链接,我们能够创建支持左右双键的应用,但是左键暂时还没有用
结合此篇文章就可以创建一个空的页面
https://juejin.cn/post/6844904101877121037
然后拖拽两人Table View到PopoverViewController之中,双击更改Header的标题
点击TableCellView更改Identifier
调整一下列宽后就可以开始写代码了,使用⌃⌥⌘↩,然后按住Control拖拽TableView到代码中,更改名字即可。
接下来开发过程相对iOS来说区别不大,NSTableView和UITableView的主要区别是没有了Section,但是多了Column,需要进行判断
如图所示。
对于一个CellView,只有默认的TextField和ImageView,如果要增加其他的可以拖拽控件实现。
网络请求采用Alamofire实现,不过需要在App Sandbox中设置一下。
详细代码可见GitHub:https://github.com/Zrzzzz/EzFund.git
菜单栏轮播基金情况
控制板UI
有搜索功能,其中SearchField只要通过设置action即可实现自动搜索,十分方便。
Cocoa Binding
这里可以提一句Cocoa Binding
,十分方便,首先在代码中定义一个变量,并使用@objc dynamic
修饰
1 | @objc dynamic var searchText: String = "" |
在StoryBoard中设置三个地方即可,则能动态绑定数据,不过要注意不要选错了,要选择中间的Cell(NSTextField)
界面显示和隐藏
关于界面的显示和隐藏,有几个坑点。界面我使用了Window来操作,没有用到NSWindowController。首先我们希望失去焦点后隐藏,并且不被释放掉。
1 | window.isReleasedWhenClosed = false |
同时,contentViewController
需要从Storyboard中取实例,不能直接初始化赋值。
然后如果在显示时只使用window.makeKeyAndOrderFront
的话,在关闭一次界面后不再显示了,因为此时App是Deactive的,所以使用以下语句。
1 | @IBAction func openBoard(_ sender: Any) { |
TableView的删除、拖拽
拖拽其实挺简单的,但是不支持[String]
类型,需要自己做一点功夫,主要使用到三个函数
1 | func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? |
详情见代码吧,需要使用到FilePromiseProvider。参考自https://developer.apple.com/documentation/appkit/nstableviewdatasource/supporting_table_view_drag_and_drop_through_file_promises
注意一点就是需要注册拖动类型
1 | tableView.registerForDraggedTypes |
不过对比FilePromiseProvider,更简单的方式是使用基金的代码(unique)
1 | extension BoardViewController { |
删除来讲就比较简单了,不过根据苹果的Document,有使用顺序之分
This method deletes from the table the rows represented at
indexes
and automatically decreasesnumberOfRows
by the count ofindexes
.The row indexes should be with respect to the current state displayed in the table view, and not the final state, because the specified rows do not exist in the final state.
Calling this method multiple times within the same
beginUpdates()
andendUpdates()
block is allowed, and changes are processed incrementally.Changes are processed incrementally as the
insertRows(at:withAnimation:)
,removeRows(at:withAnimation:)
, and themoveRow(at:to:)
methods are called. It is acceptable to delete row0
multiple times, as long as there is still a row available.Note
NSCell
-based table views must first callbeginUpdates()
before calling this method.
而且需要调用beginUpdate()
1 | @IBAction func deleteRow(_ sender: Any) { |