Ошибка инициализации класса при добавлении в него ObservedObject
Хочу реализовать горячие кнопки для проекта с помощью NSEvent дублирующие основные Button.
При привязке class ViewController - @ObservedObject var answers: Answers , class ViewController пишет ошибку:
Class 'ViewController' has no initializers
В примере пока только горячие кнопки для одного объекта (Эта часть работает).
В чем может быть проблема с инициализацией?
import SwiftUI
import Carbon
class Answer: ObservableObject, Identifiable {
let id = UUID()
@Published var answerYes: Bool?
init(answerYes: Bool?) {
self.answerYes = answerYes
}
}
class Answers: ObservableObject {
@Published var items = [Answer]()
init(_ items: [Answer]) {
self.items = items
}
}
class ViewController: NSViewController {
@ObservedObject var answers: Answers
override func viewDidLoad() {
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return nil
}
}
override func keyDown(with event: NSEvent) {
if event.keyCode == 18 {
// answers.items[0].answerYes = true
print("1")
}
if event.keyCode == 19 {
// answers.items[0].answerYes = false
print("2")
}
}
}
var viewControlle = ViewController()
struct ContentView: View {
@ObservedObject var answers: Answers
var body: some View {
HStack {
ForEach(answers.items) { item in
GameView(answer: item)
}
}
.frame(width: 600, height: 400, alignment: .center)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(answers: Answers([
Answer(answerYes: nil),
Answer(answerYes: nil),
Answer(answerYes: nil),
Answer(answerYes: nil)
]))
}
}
struct GameView: View {
@ObservedObject var answer: Answer
var body: some View {
VStack {
// buttons
HStack {
HStack {
Button("Yes") {
answer.answerYes = true
} // Выводит Yes в первом поле
Button("No") {
answer.answerYes = false
} // Выводит No в первом поле
}
}
// circle
ZStack {
Circle()
.fill(Color.yellow)
.frame(width: 100, height: 100)
.shadow(color: .black, radius: 2, x: 3, y: 3)
Text(answer.answerYes == nil ? "" : answer.answerYes! ? "yes" : "no")
.foregroundColor(Color(#colorLiteral(red: 0.8039215686, green: 0.5215686275, blue: 0.09803921569, alpha: 1)))
.font(.system(size: 40))
.offset(y: -5)
.opacity(answer.answerYes == nil ? 0 : 1)
}
}
}
}
Ответы (2 шт):
Вопрос легко гуглится по запросу "swift viewcontroller has no initializers", суть в том, что переменная answers у вас не инициализирована, исправить можно так
@ObservedObject var answers = Answers([])
либо к примеру так
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
answers = Answers([])
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder: NSCoder) {
answers = Answers([])
super.init(coder: coder)
}
Дополнение
Отлов нажатий лучше вынести в отдельный класс-обработчик (вью контроллер тут не нужен), который можно привязать к каждому объекту Answer как показано в примере, при этом остается открытым вопрос, как определять в какой круг посылать нажатия, очевидно еще нужен механизм определения выбранного круга с вопросом
import Foundation
import SwiftUI
struct AnswerKeysHandler {
@ObservedObject var answer: Answer
init(answer: Answer) {
self.answer = answer
registerKeysHandler()
}
private func registerKeysHandler() {
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return nil
}
}
private func keyDown(with event: NSEvent) {
switch event.keyCode {
case 18:
answer.answerYes = true
print("1")
case 19:
answer.answerYes = false
print("2")
default:
print("key pressed with code \(event.keyCode)")
}
}
}
Использование на примере первого вопроса
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
var handler: AnswerKeysHandler!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let answer1 = Answer(answerYes: nil)
let contentView = ContentView(answers: Answers([
answer1,
Answer(answerYes: nil),
Answer(answerYes: nil),
Answer(answerYes: nil)
]))
handler = AnswerKeysHandler(answer: answer1)
// Create the window and set the content view.
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(nil)
}
}
Дополнение 2 - разрешение ввода текста в текстовых полях
private func registerKeysHandler() {
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return $0 // <--- передаем событие дальше для обработки
}
}
Этот код очень хорошо отслеживает нажатие кнопок клавиатуры:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return nil
}
}
override func keyDown(with event: NSEvent) {
if event.keyCode == 18 {
//print(answers.items)
//answers.items[1].answerYes = true
print("1")
}
if event.keyCode == 19 {
// answers.items[0].answerYes = false
print("2")
}
}
}
var viewControlle = ViewController()
Помещаю его в переменную viewControlle
И в файле AppDeligate добавляю:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(answers: Answers([
Answer(answerYes: nil),
Answer(answerYes: nil),
Answer(answerYes: nil),
Answer(answerYes: nil)
]))
viewControlle.viewDidLoad()
// Create the window and set the content view.
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(nil)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}