Захват тела и контекста SwiftUI View

#swiftui

#swiftui

Вопрос:

Я хочу реализовать кнопку SwiftUI, которая принимает onTapGesture и onLongPressGesture с переопределяемым обработчиком

Мне не удалось заставить встроенную кнопку работать с обоими, поэтому я прибегнул HStack к. Кнопки используют CommandController шаблон и отправляют команду из onTapGesture . Базовый код был:

 struct CmdButton : View {
    @EnvironmentObject var appController : AppController
    
    var command : AppCommand

    init(command : AppCommand) {
       self.command = command

       self.onTapGesture {
         self.doCommand()
       }
    }
    
    var body: some View {
        HStack {
            Image(self.command.icon).resizable().scaledToFit().frame(width: 35)
        }
        .padding(6)
        .cornerRadius(5)
        .contentShape(Rectangle())
        .animation(.default)
    }
    
    func doCommand(){
        appController.dispatchCommand(command: self.command)
    }
}
 

Цель состояла в том, чтобы CmdButton вызывать doCommand() from onTapGesture по умолчанию, если он не был переопределен разработчиком, таким образом:

 CmdButton(command: SomeCommand)
  .onTapGesture {
    doSomethingFirst()
    instance.doCommand()
  }
  .onLongPressGesture {
    doLongPressAction()
  }
 

У меня есть 2 основные проблемы.

Кажется, я никак не могу захватить правильный self контекст в CmdButton реализации, чтобы добавить значение по умолчанию onTapGesture , если я добавлю во init время получения mutating self ошибки. Если я добавлю жест в HStack, он не может быть переопределен, и я не вижу, как привести some View к конкретному типу для назначения непосредственно на var body .

Когда я переопределяю onTapGesture , я не могу получить self контекст экземпляра в переопределенном onTapGesture обработчике для вызова doCommand() метода.

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

Редактировать

Чтобы уточнить, согласно документам Apple, допустимо:

 struct MyView : View {
    let img : String = "asset"
    let name : String = "Some Name"
    
    var body: some View {
        Image(img).onTapGesture {
          onTap()
        }
    }
    
    func onTap(){
        print(name)
    }
}
 

Где захвачен правильный контекст и обработчик жестов, прикрепленный к Image . Мой вопрос заключается в том, что вместо того, чтобы жест присваивался внутренней реализации, как я могу назначить его телу внешнего представления, чтобы его можно было переопределить.

 MyView()
  .onTapGesture {
    doSomethingElse()
    self_instance.onTap()
  }
 

Опять же, я могу вызвать:

 MyView().onTap()
 

Как мне заставить экземпляр представления вызывать onTap внутри обработчика?

Комментарии:

1. Вы пытаетесь применить проактивные принципы ООП к миру SwiftUI, основанному на реактивных значениях, поэтому потерпели неудачу. Вернитесь к сеансам SwiftUI WWDC-19.

2. Работает ли исходный код вообще? Вы должны получать предупреждение типа «Результат вызова ‘onTapGesture’ не используется». Если это и работает, то из-за какого-то побочного эффекта, который почти наверняка является неопределенным поведением. Вызов .onTapGesture не изменяет представление. Это создает совершенно новый вид, который обтекает цель. Итак, вы создаете представление, которое реагирует на жесты касания, а затем просто выбрасываете его. Ваш подход здесь невозможен, потому что представления не являются объектами среды выполнения, такими как UIViews; это просто данные, которые описывают макет и поведение.

3. Привет, Роб, никакое присвоение onTapGesture init не считается изменяющим self, поэтому не может быть выполнено. Я хочу сказать, как создать обработчик жестов по умолчанию, который можно переопределить, и захватить правильный контекст в этом сценарии.