Как обновить элемент состояния, созданный AppDelegate из NSViewController - PullRequest
0 голосов
/ 01 января 2019

Я пытаюсь создать приложение «Таймер обратного отсчета», которое запускается в строке меню, без значка окна или дока.Я строил это, в основном, из уроков, которые я нахожу в Интернете, и я знаю, что код немного запутан (я планирую убирать после того, как он функционирует должным образом).Проблема, с которой я сталкиваюсь.В AppDelegate я создаю элемент StatusBar без проблем, но я не могу понять, как обновить его из viewController.Вместо этого он создает новый элемент StatusBar.

// Информация AppDelegate

class AppDelegate: NSObject, NSApplicationDelegate
{

    let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    let popover = NSPopover()

    func applicationDidFinishLaunching(_ aNotification: Notification)
    {
       menuBarRefresh(self)
    }

    func menuBarRefresh(_ sender: Any?)
    {
        if let button = item.button
        {
            button.image = NSImage(named: NSImage.Name("2"))
        //button.title = initialTime.stringValue
            button.action = #selector(togglePopover(_:))
        }
        popover.contentViewController = TimerViewController.freshController()
    }

    @objc func togglePopover(_ sender: Any?)
    {
        if popover.isShown
        {
            closePopover(sender: sender)
        }
        else
        {
            showPopover(sender: sender)
        }
    }

    func showPopover(sender: Any?)
    {
        if let button = item.button
        {
            popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
        }
    }

    func closePopover(sender: Any?)
    {
        popover.performClose(sender)
    }

// Код контроллера

import Cocoa
import AVFoundation

//Checking to ensure entered data is numeric
extension String
{
    var isNumeric: Bool
    {
        let range = self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted)
        return (range == nil)
    }
}

class TimerViewController: NSViewController
{

    //Here's the texts fields for the user to enter content.
    @IBOutlet var hourInput: NSTextField!
    @IBOutlet var minuteInput: NSTextField!
    @IBOutlet var secondInput: NSTextField!

    //This is the label used to display the counter
    @IBOutlet var initialTime: NSTextField!

    //Here are the variables we're going to need
    var hours = Int()                   //Place holder for the hours
    var minutes = Int()                 //Place holder for the hours
    var seconds = Int()                 //Place holder for the hours
    var timer = Timer()                 //The timer we'll use later
    var audioPlayer = AVAudioPlayer()   //The audio player
    var timeRemaining = Int()           //Place holder for the total 'seconds' to be counted
    var firstRun = Bool()

    let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

    override func viewDidLoad()
    {
        super.viewDidLoad()
        getData()                                   //Pull last saved time from Core Data and load it.
        hourInput.stringValue = "\(hours)"          //Loading the hours into the hours field
        minuteInput.stringValue = "\(minutes)"      //Loading the minutes into the minutes field
        secondInput.stringValue = "\(seconds)"      //Loading the seconds into the seconds field
        initialTime.stringValue = "00:00:00"        //Resetting the 'counter' to 0
        firstRun = true
        updateStatusBar(self)

        //Here we load up the audio file for the 'done' chime.  If not available we print the catch
        do
        {
            let audioPath = Bundle.main.path(forResource: "Done", ofType: "m4a")
            try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath!))
        }
        catch
        {
            print("No Joy")
        }
/*      if let button = item.button
        {
            button.image = NSImage(named: NSImage.Name("2"))
            button.title = initialTime.stringValue
            button.action = #selector(togglePopover(_:))
        }
*/   }

}

// MARK: Storyboard instantiation

extension TimerViewController
{
    static func freshController() -> TimerViewController
    {
        let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
        let identifier = NSStoryboard.SceneIdentifier("TimerViewController")
        guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? TimerViewController
            else
            {
                fatalError("Why can't I find TimerViewController? - Check Main.storyboard")
            }
        return viewcontroller
    }
}

//Button actions follow

extension TimerViewController
{
    @IBAction func clearButton(_ sender: Any)
    {
        clearFields()
        timer.invalidate()
        audioPlayer.stop()
    }

    @IBAction func pauseButton(_ sender: Any)
    {
        timer.invalidate()
    }

    @IBAction func quitButton(_ sender: Any)
    {
        exit(0)
    }

    @IBAction func startButton(_ sender: Any)
    {
        grabData()
        setData()

        timeRemaining = (hours*3600)+(minutes*60)+seconds

        if timeRemaining <= 0
        {
            initialTime.stringValue = "Enter Time"
        }

        else
        {
            displayTime()

            timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.startCountDown), userInfo: nil, repeats: true)

            clearFields()
            updateStatusBar(self)
        }
    }
}

//MARK: Other Functions

extension TimerViewController
{
    func displayTime()
    {
        let secondsDisplay = String(format: "%02d", (timeRemaining%60))
        let minutesDisplay = String(format: "%02d", (timeRemaining%3600)/60)
        initialTime.stringValue = "\(timeRemaining/3600):\(minutesDisplay):\(secondsDisplay)"
    }

    func grabData()
    {
        hours = hourInput.integerValue
        minutes = minuteInput.integerValue
        seconds = secondInput.integerValue
    }

    func clearFields()
    {
        hourInput.stringValue = ""
        minuteInput.stringValue = ""
        secondInput.stringValue = ""
        initialTime.stringValue = "00:00:00"
    }

    func setData()
    {
        setHour()
        setMinute()
        setSecond()
    }

    func getData()
    {
        getHour()
        getMinute()
        getSecond()
    }

    @objc func showTimer(_ sender: Any?)
    {
        print("Are we here")
    }
    @objc func startCountDown()
    {

        timeRemaining -= 1
        displayTime()
        updateStatusBar(self)
        print(timeRemaining)

        if timeRemaining == 0
        {
            timer.invalidate()
            audioPlayer.play()
        }
    }

/*    func setNeedsStatusBarAppearanceUpdate()
    {
       button.image = NSImage(named: NSImage.Name("2"))
       button.action = #selector(showTimer(_:))
    }
*/
    func updateStatusBar(_ sender: Any?)
    {
        if let button = item.button
        {
            button.image = NSImage(named: NSImage.Name("2"))
            button.action = #selector(showTimer(_:))
            button.title = initialTime.stringValue
        }

        //let menu = NSMenu()
        //menu.addItem(NSMenuItem(title: "Clear Timer", action: #selector(AppDelegate.theDv2), keyEquivalent: "R"))
        //menu.addItem(NSMenuItem(title: "Quit Timer", action: #selector(AppDelegate.quit), keyEquivalent: "Q"))
        //item.menu = menu
    }

}

// Есть куча вещей CoreData послездесь, но я оставил это.Я просто использую CoreData, главным образом, чтобы узнать, как и функциональная причина хранить и загружать последнее использованное время

. В настоящее время это работает, я получаю два элемента StatusBar вместо того, чтобы создать один с AppDelegate, а затем обновлять этот элемент.из ViewController.

1 Ответ

0 голосов
/ 02 января 2019

Да ... ошибка Id-10-t здесь.Просто нужно было объявить «предмет» за пределами класса, и все хорошо.После хорошего сна и свободного времени от компьютера я понял, что не объявляю «товар» глобально.

...