Дорога к чести не усыпана цветами.
предисловие
После того, как с помощью Java забрался к потреблению электроэнергии электросчетчиком в моей комнате, я инкапсулировал интерфейс для клиентских вызовов 😝 В своей повседневной жизни я использую Mac чаще всего, было бы замечательно, если бы данные, к которым я залез, отображались на верхней панели Mac 😌 Как человек, который ничего не знает о Swift, я потратил два дня на разработку приложения для Mac на языке Swift.Далее я поделюсь с вами процессом разработки этого приложения.Все заинтересованные разработчики могут прочитать эту статью.
Давайте посмотрим на окончательный результат:
Строительство окружающей среды
Swift: 4.2.1
Xcode: 10.1
MacOS: 10.14.2
Инструмент управления библиотекой: Carthage
Alamofire: 4.0 (библиотека сетевых запросов)
SwiftyJSON: 3.0 (библиотека разбора Json)
Создать проект
- Открываем Xcode, видим интерфейс как показано на рисунке
- Слева раздел «Созданный проект», справа недавно открытый проект
- Нажмите на место, отмеченное красным на картинке
- Как показано на рисунке, нажмите на серийные номера, указанные на рисунке.
- Как показано на рисунке, заполните соответствующую информацию о проекте соответственно.
- Выберите, где создать проект
- После появления страницы, показанной на рисунке, проект успешно создан.
Настройте элемент как приложение строки меню
В настоящее время проект является пустым проектом. После нажатия кнопки «Выполнить» появится пустое окно, а на доке появится значок приложения. Это не то, что нам нужно, поэтому нам нужно добавить конфигурацию, чтобы удалить их.
- Как показано на рисунке, добавьте элемент конфигурации под информацией
- Выбор ключаApplication is agent (UI Element),Значение выбрать Да
- В этот момент снова запустим программу, и мы обнаружим, что значок программы больше не отображается в док-баре, но окно окна все еще там.
- Фигура показана, удаленаWindow Controller Sceneа такжеView Controller Scene
- В это время мы находимся в приложении облачного сердца и обнаружили, что окно исчезло.
Создать значок в строке меню
- Как показано на рисунке, нажмитеAssets.xcassetsфайл, создайте новыйImage Set
- Как показано, назовите его какStatusIcon, сделайте свою любимую картинку в 3 вида спецификаций и поместите ее в соответствующую позицию.
- После перетаскивания значка следуйте инструкциям на картинке, чтобы значок адаптировался к темному режиму системы.
- Откройте файл AppDelegate.swift и добавьте следующий код.
// 创建状态栏按钮
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
// applicationDidFinishLaunching生命周期
if let button = statusItem.button {
button.image = NSImage(named: "StatusIcon")
}
- Запустив программу, мы обнаружили, что в строке меню есть значок, который мы только что установили, и после нажатия на него ничего не происходит.
Добавить кPopoverконтейнер
- В каталоге проекта создайте новыйCocoa Class, по имениPopoverViewController, этот файл представляет собой всплывающую страницу, которая открывается при нажатии
- Добавить контейнер View Controller
- Как показано на рисунке, измените контейнер, добавленный на предыдущем шаге.
- Откройте файл PopoverViewController.swift и добавьте в конец следующий код.
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
}
}
- Откройте файл AppDelegate.swift и добавьте в класс следующий код.
// 声明一个Popover
let popover = NSPopover()
Создайте функцию для отображения/скрытия всплывающего окна.
- Добавьте следующий код в класс файла AppDelegate.swift.
// 控制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)
}
- в файле AppDelegate.swiftapplicationDidFinishLaunchingДобавьте следующий код в функцию
if let button = statusItem.button {
button.image = NSImage(named: "StatusIcon")
button.action = #selector(togglePopover(_:))
}
popover.contentViewController = PopoverViewController.freshController()
- В этот момент запустите проект, щелкните значок приложения в строке меню, отобразится всплывающий слой, и снова щелкните всплывающий слой, он исчезнет.
Оптимизировать всплывающее окно
После выполнения предыдущего шага мы обнаружим, что всплывающий слой будет закрываться или исчезать только при нажатии.Далее давайте оптимизируем его и скроем при потере фокуса.
- Создайте новый файл EventMonitor.swift для мониторинга событий, код этого файла выглядит следующим образом
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
}
}
}
- Откройте AppDelegate.swift и объявите этот монитор в классе
// 声明监视器
var eventMonitor: EventMonitor?
- Добавить функцию applicationdiDfinishlaumping
eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in
if let strongSelf = self, strongSelf.popover.isShown {
strongSelf.closePopover(event!)
}
}
- Измените функции showPopover и closePopover.
// 显示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()
}
- Запустив проект, мы обнаружили, что слой маркера был скрыт после потери фокуса.
Добавить контекстное меню
- Как показано на рисунке, добавьте компонент меню, чтобы войти
- Связать меню с AppDelegate.swift
- Удалите лишние элементы, добавьте значки выхода и записи
- Измените файл AppDelegate.swift и добавьте обработчик, чтобы взять на себя функцию togglePopover.
// 接管togglePopover
@objc func mouseClickHandler() {
if let event = NSApp.currentEvent {
switch event.type {
case .leftMouseUp:
togglePopover(popover)
default:
statusItem.menu = Menu
statusItem.button?.performClick(nil)
}
}
}
- statusItem.button добавьте следующий код
// 点击事件
button.action = #selector(mouseClickHandler)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
- Добавить в конец AppDelegate.swift
extension AppDelegate: NSMenuDelegate {
// 为了保证按钮的单击事件设置有效,menu要去除
func menuDidClose(_ menu: NSMenu) {
self.statusItem.menu = nil
}
}
- Добавить внутри приложенияDidFinishLaunching
// 修复按钮单击事件无效问题
Menu.delegate = self
- В этот момент кликаем правой кнопкой мыши, обнаруживаем, что он успешно добавлен
Реализовать функцию выхода
- Как показано на рисунке, свяжите функцию push с файлом AppDelegate.
- Улучшить функцию выхода из приложения
// 关闭App
@IBAction func Quit(_ sender: Any) {
NSApplication.shared.terminate(self)
}
- После повторного запуска щелкните правой кнопкой мыши и выберите «Выход», чтобы закрыть приложение.
Написание всплывающих страниц
После выполнения вышеперечисленных шагов мы создали пустой Popover, а затем добавили в него содержимое, вызвали интерфейс и отобразили потребление электроэнергии счетчиком электроэнергии в нашей комнате.
страница макета
- Как показано на рисунке, добавьте компоненты Text Field и Label, перетащите их в PopoverViewController и свяжите с PopoverViewController.swift.
- Отрегулируйте размер перетаскиваемого элемента управления, чтобы он выглядел как на картинке.
Установите инструмент управления библиотекой Cartfile.
CartfileЭто отличный инструмент управления библиотекой, эквивалентный нашему внешнему интерфейсу npm.
- нажмитеадрес загрузки корзины, перейдите на страницу загрузки репозитория Cartfile на github, выберите файл pkg для загрузки, а затем установите его.
Установите библиотеку сетевых запросов и библиотеку разбора Json.
Alamofire, превосходная библиотека сетевых запросов, инкапсулирует различные HTTP-запросы.
SwiftyJSON, отличная библиотека для разбора json.
Мы можем получить их через Cartfile
- Создайте файл Cartfile в корневом каталоге нашего проекта и добавьте следующее содержимое
github "Alamofire/Alamofire" ~> 4.0
github "SwiftyJSON/SwiftyJSON" ~> 3.0
- Откройте терминал, войдите в корневой каталог нашего проекта и выполните следующую команду
carthage update --platform macOS
- После выполнения мы обнаружили, что в корневом каталоге проекта есть еще папки Carthage
- В этот момент открываем, xcode, открываем страницу как показано на рисунке
- Как показано на рисунке, выберите файл build->Mac->framework в только что созданной папке Carthage.
- После успешного добавления, как показано на рисунке
Включить доступ к сети
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 сканирует потребление электроэнергии электросчетчиком
использованная литература:Что такое стерео.top/2018/06/29/…
Кодовый адрес, соответствующий этой статье:home-battery-tool
- Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
- Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌