Как создать значок и меню в строке состояния с помощью SwiftUI, как в macos Big Sur

Что такое SwiftUI API для создания меню строки состояния?

Согласно инспектору доступности, Apple, похоже, использует представления SwiftUI в меню батареи и Wi-Fi. Скриншот меню батареи прилагается, а также его иерархия представлений.

Снимок экрана меню батареи  Просмотр иерархии меню батареи

РЕДАКТИРОВАТЬ:

Выложил решение отдельным ответом.


person Ruzard    schedule 22.11.2020    source источник
comment
Не могли бы вы уточнить, где именно в AppDelegate идет этот код? У меня возникают сбои и значки в строке состояния, которые временно работают, но быстро исчезают.   -  person Dominic Holmes    schedule 28.11.2020
comment
Где именно вылетает? Здесь NSStatusBar.system?   -  person Ruzard    schedule 29.11.2020
comment
Ага, но я решил это. Я неправильно понял ваш ответ и подумал, что NSStatusBar уже создан и предоставлен системой.   -  person Dominic Holmes    schedule 29.11.2020


Ответы (2)


Поскольку в последнее время этому вопросу уделялось больше внимания, и единственный ответ не решает проблему полностью, я хотел бы повторить отредактированную часть своего вопроса и отметить ее как решенную.

Нашел способ показывать swiftui, не раздражая NSPopover. Вам понадобится AppDelegate или NSApplicationDelegateAdaptor, если вы используете жизненный цикл приложения SwiftUI. Затем вы создаете NSMenu и добавляете NSMenuItem, который может иметь настраиваемое представление.

Вот код:

    let contentView = VStack {
        Text("Test Text")
        Spacer()
        HStack {
            Text("Test Text")
            Text("Test Text")
        }
        Spacer()
        Text("Test Text")
    }
    let view = NSHostingView(rootView: contentView)
    // Don't forget to set the frame, otherwise it won't be shown.
    view.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
    
    let menuItem = NSMenuItem()
    menuItem.view = view
    
    let menu = NSMenu()
    menu.addItem(menuItem)
    
    // StatusItem is stored as a class property.
    self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    self.statusItem?.menu = menu
    self.statusItem?.button?.title = "Test"

введите описание изображения здесь

person Ruzard    schedule 10.01.2021
comment
Спасибо тебе за это! Именно то, что я искал. Однако я не могу понять, как заставить работать какие-либо интерактивные элементы в представлении (кнопки, текстовые поля). Есть предположения? - person Brad G.; 11.01.2021
comment
@BradG. Если вы добавите кнопку в представление содержимого, с ней можно будет взаимодействовать. В моем случае он печатает данные gist.github.com/kmalyshev/7ef834efaeed83014f6ba851582fca0d. Есть ли что-нибудь, что блокирует взаимодействие в вашем случае? - person Ruzard; 11.01.2021
comment
Вы правы, кнопки работают. Однако текстовые поля этого не делают. У меня также есть меню в представлении, которое тоже не работает. - person Brad G.; 12.01.2021
comment
Я создал минимальный проект для демонстрации. TextField действительно работает изначально, но в какой-то момент перестает принимать новый ввод (ввод чего-либо в нем закрывает меню). Меню никогда не работает. github.com/bgreenlee/MinimalMenuBarApp - person Brad G.; 12.01.2021
comment
@BradG. К сожалению, я не зашел так далеко. Мне было достаточно иметь какой-то индивидуальный вид. По этой теме есть документация. Это говорит об ограничениях такого подхода. Возможно, есть способ преодолеть ограничения, обработав события мыши и клавиатуры вручную. developer.apple.com/library/archive/documentation/Cocoa/ - person Ruzard; 12.01.2021

Внутри AppDelegate добавьте следующий код:

// Create the status item in the Menu bar 
self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))

// Add a menu and a menu item
let menu = NSMenu()
let editMenuItem = NSMenuItem()
editMenuItem.title = "Edit"
menu.addItem(editMenuItem)

//Set the menu 
self.statusBarItem.menu = menu

//This is the button which appears in the Status bar
if let button = self.statusBarItem.button {
    button.title = "Here"
}

Это добавит кнопку с настраиваемым меню в вашу панель меню.

введите описание изображения здесь

Изменить - как использовать SwiftUI View

Как вы спросили, вот ответ, как использовать SwiftUI View.

Сначала создайте NSPopover, а затем оберните представление SwiftUI в NSHostingController.

var popover: NSPopover


let popover = NSPopover()
popover.contentSize = NSSize(width: 350, height: 350)
popover.behavior = .transient
popover.contentViewController = NSHostingController(rootView: contentView)
    self.popover = popover

Затем вместо отображения NSMenu переключите всплывающее окно:

self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
if let button = self.statusBarItem.button {
     button.title = "Click"
     button.action = #selector(showPopover(_:))
}

Со следующим действием:

@objc func showPopover(_ sender: AnyObject?) {
    if let button = self.statusBarItem.button
    {
        if self.popover.isShown {
            self.popover.performClose(sender)
        } else {
            self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
        }
    }
}

введите описание изображения здесь

person davidev    schedule 22.11.2020
comment
К сожалению, опубликованный вами код является способом создания этих меню с помощью AppKit. Это не способ SwiftUI и не поддерживает представления SwiftIUI. - person Ruzard; 22.11.2020
comment
Что ж, если вы создаете проект с жизненным циклом AppKit App Delegate Life cycle, у вас все равно есть AppDelegate. - person davidev; 22.11.2020
comment
Да, но я не об этом спросил. Мне интересно показать swiftUI в этом верхнем меню. - person Ruzard; 22.11.2020
comment
Спасибо, первоначальная версия моего вопроса, которая в конечном итоге была заблокирована из-за недостаточной прямолинейности, содержала строку о NSPopover. Этот NSPopover кажется обходным решением, которого я хотел избежать. Я очень ценю ваш ответ и постараюсь наградить вас, как только смогу их наградить. Показанный вами код определенно поможет другим людям. На всякий случай я уточню свой вопрос более подробной информацией. - person Ruzard; 22.11.2020
comment
Я добавил альтернативное решение без NSPopover. Возможно, вы тоже сочтете это полезным. - person Ruzard; 22.11.2020