通向榮譽的路上,並不鋪滿鮮花。前端
用Java爬到我房間電錶電量使用狀況後,封裝了一個接口,用於客戶端的調用😝 在平常生活中,我用Mac的時間是最多的,若是將爬到的數據,展現在Mac的頂欄上,是一件很美好的事情😌 做爲一個對Swift一無所知的我,花了兩天時間,用Swift語言開發了一個Mac應用,接下來就跟你們分享下這個應用的開發過程,歡迎各爲感興趣的開發者閱讀本文。git
先跟你們看下最終實現的效果: github
Swift: 4.2.1npm
Xcode: 10.1json
MacOS: 10.14.2swift
庫管理工具: Carthageapi
Alamofire: 4.0 (網絡請求庫)xcode
SwiftyJSON: 3.0 (Json解析庫)bash
此時項目爲一個空白項目,點運行後會出現一個空的window窗口,同時dock上出現應用圖標,這並非咱們要的,因此要添加配置來移除他們。 網絡
// 建立狀態欄按鈕
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
複製代碼
// applicationDidFinishLaunching生命週期
if let button = statusItem.button {
button.image = NSImage(named: "StatusIcon")
}
複製代碼
extension PopoverViewController {
static func freshController() -> PopoverViewController {
//獲取對Main.storyboard的引用
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
// 爲PopoverViewController建立一個標識符
let identifier = NSStoryboard.SceneIdentifier("PopoverViewController")
// 實例化PopoverViewController並返回
guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? PopoverViewController else {
fatalError("Something Wrong with Main.storyboard")
}
return viewcontroller
}
}
複製代碼
// 聲明一個Popover
let popover = NSPopover()
複製代碼
// 控制Popover狀態
@objc func togglePopover(_ sender: AnyObject) {
if popover.isShown {
closePopover(sender)
} else {
showPopover(sender)
}
}
// 顯示Popover
@objc func showPopover(_ sender: AnyObject) {
if let button = statusItem.button {
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
}
}
// 隱藏Popover
@objc func closePopover(_ sender: AnyObject) {
popover.performClose(sender)
}
複製代碼
if let button = statusItem.button {
button.image = NSImage(named: "StatusIcon")
button.action = #selector(togglePopover(_:))
}
popover.contentViewController = PopoverViewController.freshController()
複製代碼
執行完上個步驟後,咱們會發現彈層只會在點擊時關閉或者消失,接下來咱們來優化下,失去焦點時,也讓它隱藏
import Cocoa
public class EventMonitor {
private var monitor: Any?
private let mask: NSEvent.EventTypeMask
private let handler: (NSEvent?) -> Void
public init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) {
self.mask = mask
self.handler = handler
}
deinit {
stop()
}
public func start() { //開啓監視器
monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler)
}
public func stop() { //關閉監視器
if monitor != nil {
NSEvent.removeMonitor(monitor!)
monitor = nil
}
}
}
複製代碼
// 聲明監視器
var eventMonitor: EventMonitor?
複製代碼
eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in
if let strongSelf = self, strongSelf.popover.isShown {
strongSelf.closePopover(event!)
}
}
複製代碼
// 顯示Popover
@objc func showPopover(_ sender: AnyObject) {
if let button = statusItem.button {
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
}
eventMonitor?.start()
}
// 隱藏Popover
@objc func closePopover(_ sender: AnyObject) {
popover.performClose(sender)
eventMonitor?.stop()
}
複製代碼
// 接管togglePopover
@objc func mouseClickHandler() {
if let event = NSApp.currentEvent {
switch event.type {
case .leftMouseUp:
togglePopover(popover)
default:
statusItem.menu = Menu
statusItem.button?.performClick(nil)
}
}
}
複製代碼
// 點擊事件
button.action = #selector(mouseClickHandler)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
複製代碼
extension AppDelegate: NSMenuDelegate {
// 爲了保證按鈕的單擊事件設置有效,menu要去除
func menuDidClose(_ menu: NSMenu) {
self.statusItem.menu = nil
}
}
複製代碼
// 修復按鈕單擊事件無效問題
Menu.delegate = self
複製代碼
// 關閉App
@IBAction func Quit(_ sender: Any) {
NSApplication.shared.terminate(self)
}
複製代碼
執行完上述步驟後,咱們建立了一個空的Popover,接下來咱們往Popover添加內容,調用接口,顯示咱們房間電錶電量使用狀況。
Cartfile是一個優秀的庫管理工具,至關於咱們前端的npm。
Alamofire,爲一個優秀的網絡請求庫,他封裝了各類http請求。
SwiftyJSON,爲一個優秀的json解析庫
咱們能夠經過Cartfile來獲取他們
github "Alamofire/Alamofire" ~> 4.0
github "SwiftyJSON/SwiftyJSON" ~> 3.0
複製代碼
carthage update --platform macOS
複製代碼
Xcode默認不容許http請求,按照如圖所示的操做進行便可。
在PopoverViewController.swift文件中添加以下代碼
import Cocoa
import Alamofire
import SwiftyJSON
class PopoverViewController: NSViewController {
// 今日用電
@IBOutlet weak var electricityToday: NSTextField!
// 本月已用
@IBOutlet weak var currentMonthBatteryTotal: NSTextField!
// 剩餘電量
@IBOutlet weak var remainingBattery: NSTextField!
// 統計時間
@IBOutlet weak var time: NSTextField!
private var timer: Timer?
// 定時器記數: 每20分鐘執行一次,3輪爲1小時
private var timeCount = 3
override func viewDidLoad() {
super.viewDidLoad()
// 獲取並設置頁面數據
setPageData()
// 啓動定時器
loop()
}
// 獲取並設置數據
func setPageData(){
// 發起post請求
Alamofire.request("https://www.xxx.com",method: .post,parameters: ["userName":"xxx","password":"xxx"],encoding: JSONEncoding.default).responseJSON { (response) in
switch response.result {
// 請求成功
case .success(let resData):
// 將返回的數據轉爲JSON對象
let jsonData = JSON.init(resData as Any)
// 變量賦值
self.electricityToday.stringValue = jsonData["data"]["electricityToday"].string!
self.currentMonthBatteryTotal.stringValue = jsonData["data"]["currentMonthBatteryTotal"].string!
self.remainingBattery.stringValue = jsonData["data"]["remainingBattery"].string!
self.time.stringValue = jsonData["data"]["time"].string!
break
case .failure(let error):
print("接口調用失敗")
print(error);
break
}
}
}
// GCD 方式的定時器,循環
func loop() {
print("\(Date()): 定時器初始化")
// timeInterval: 隔多少秒執行一次
timer = Timer(timeInterval: 1200, repeats: true, block: { timer in
self.loopFireHandler(timer)
})
// 添加定時器
RunLoop.main.add(timer!, forMode: .common)
}
// 定時器須要執行的內容
@objc private func loopFireHandler(_ timer: Timer?) -> Void {
// 定時器執行結束結束
if self.timeCount <= 0 {
print("\(Date()): 執行完1輪,開始下一輪")
self.timeCount = 3
return
}
// 獲取並設置頁面數據
setPageData()
// 執行完分鐘
self.timeCount -= 1
}
}
extension PopoverViewController {
static func freshController() -> PopoverViewController {
//獲取對Main.storyboard的引用
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
// 爲PopoverViewController建立一個標識符
let identifier = NSStoryboard.SceneIdentifier("PopoverViewController")
// 實例化PopoverViewController並返回
guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? PopoverViewController else {
fatalError("Something Wrong with Main.storyboard")
}
return viewcontroller
}
}
複製代碼
如何爬取你房間內電錶使用狀況,請移步這篇文章:Java爬取電錶電量使用狀況
本篇文章對應的代碼地址: home-battery-tool