В моем приложении есть 2 ярлыка siri. Я использую NSUserActivity, чтобы пожертвовать эти ярлыки. Я также создал 2 NSUserActivityTypes в моем info.plist.
Есть 2 контроллера представления, которые обрабатывают эти ярлыки (1 контроллер представления для 1 ярлыка).
Если я добавлю 1 ярлык siri из 1 контроллера представления, а затем перейду к контроллеру второго представления, собственная кнопка ярлыка siri (INUIAddVoiceShortcutButton) на контроллере второго представления автоматически выберет первый ярлык (созданный из контроллера первого представления) и покажет «Добавлено в Siri» с предложенным фразу вместо отображения кнопки «Добавить в Siri». Я дважды проверил, что каждый NSUserActivity имеет разные идентификаторы, но все же каким-то образом выбирает неправильный ярлык.
Просмотр контроллера 1:
let userActivity = NSUserActivity(activityType: "com.activity.type1")
userActivity.isEligibleForSearch = true
userActivity.isEligibleForPrediction = true
userActivity.title = shortcut.title
userActivity.suggestedInvocationPhrase = suggestedPhrase
let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity.contentAttributeSet = attributes
let shortcut = INShortcut(userActivity: userActivity)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)
Просмотр контроллера 2:
let userActivity2 = NSUserActivity(activityType: "com.activity.type2")
userActivity2.isEligibleForSearch = true
userActivity2.isEligibleForPrediction = true
userActivity2.title = shortcut.title
userActivity2.suggestedInvocationPhrase = suggestedPhrase
let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity2.contentAttributeSet = attributes
let shortcut = INShortcut(userActivity: userActivity2)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)
Аналогичная вещь происходит, когда я удаляю приложение и переустанавливаю его, не удаляя ярлыки из приложения «Настройки телефона».





Похоже, это ошибка IOS. Я нашел способ решения этой проблемы. Вам нужно создавать новую кнопку siri каждый раз, когда пользователь добавляет / редактирует ярлык siri. Перед созданием кнопки siri выполните следующие действия
1- Получите все голосовые ярлыки от INVoiceShortcutCenter, вызвав функцию. Обратите внимание, что это происходит асинхронно, поэтому вам нужно сделать это за некоторое время, прежде чем вам понадобятся данные (например, в вашем AppDelegate). Вам также потребуется перезагружать его всякий раз, когда пользователь добавляет ярлык Siri (возможно, в методе INUIAddVoiceShortcutViewControllerDelegate.addVoiceShortcutViewController(_:didFinishWith:error)).
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
if let error = error as NSError? {
os_log("Failed to fetch voice shortcuts with error: %@", log: OSLog.default, type: .error, error)
}
return
}
self.voiceShortcuts = voiceShortcutsFromCenter
}
2- В View Controller-1 проверьте, добавлен ли уже ярлык, повторяя все голосовые ярлыки
let voiceShorcut = voiceShortcuts.first { (voiceShortcut) -> Bool in
if let activity = voiceShortcut.shortcut.userActivity, activity.activityType == "com.activity.type1" {
return true
}
return false
}
3- Если ваш голосовой ярлык зарегистрирован, передайте INShortcut кнопке siri, иначе не устанавливайте его.
if voiceShorcut != nil {
let shortcut = INShortcut(userActivity: userActivity1)
siriButton.shortcut = shortcut
}
Сделайте то же самое в Second View Controller.
Я сам исправил эту проблему, изменив свою реализацию (изначально основанную на приложении soupchef) на этот образец кода, предоставленный Apple (https://developer.apple.com/documentation/sirikit/inuiaddvoiceshortcutbutton):
Обновлено: я добавил код, который показывает, как я создаю и передаю shortcutObject (INShortcut) как для UserActivity, так и для пользовательских ярлыков Intent.
Класс Shortcut - это перечисление, которое содержит вычисляемое свойство, называемое намерением, которое возвращает экземпляр пользовательского намерения.
private func addShortcutButton(shortcut: Shortcut, parentViewController: UIViewController, shortcutViewControllerDelegate: INUIAddVoiceShortcutViewControllerDelegate) {
guard let view = parentViewController.view else { return }
if let intent = shortcut.intent {
shortcutObject = INShortcut(intent: intent)
} else if let userActivity = view.userActivity {
shortcutObject = INShortcut(userActivity: userActivity)
}
self.shortcutViewControllerDelegate = shortcutViewControllerDelegate
addSiriButton(to: shortcutButtonContainer)
}
func addSiriButton(to view: UIView) {
let button = INUIAddVoiceShortcutButton(style: .whiteOutline)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
view.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: button.centerYAnchor).isActive = true
button.addTarget(self, action: #selector(addToSiri(_:)), for: .touchUpInside)
}
// Present the Add Shortcut view controller after the
// user taps the "Add to Siri" button.
@objc
func addToSiri(_ sender: Any) {
guard let shortcutObject = shortcutObject else { return }
let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcutObject)
viewController.modalPresentationStyle = .formSheet
viewController.delegate = shortcutViewControllerDelegate
parentViewController?.present(viewController, animated: true, completion: nil)
}
У меня есть свойство, в котором я устанавливаю активность пользователя для ярлыка, который я хотел бы использовать, и использую его для создания INShortcut. Я обновлю это позже, добавив более подробный код, когда вернусь к своей машине разработчика.
Привет. Я борюсь с этой настройкой на данный момент. Не могли бы вы рассказать, как вы использовали пример для UserActivity, предоставленный Apple? if let shortcut = INShortcut (userActivity: Используете ли вы функцию для выбора активности, чтобы войти сюда?
Дэн, я передаю контроллер представления, у которого есть пожертвованная активность пользователя (parentViewController), и вытаскиваю userActivity из его представления. Я добавил еще код, чтобы показать это.
Это ошибка iOS 12.0. Вы можете исправить это, обновив INUIAddVoiceShortcutButton.voiceShortcut с правильным значением. Используйте KVO для наблюдения за свойством voiceShortcut и при его изменении присвойте ему правильное значение.
Я не вижу свойства voiceShortcut на INUIAddVoiceShortcutButton - это личное?
Хорошо, даже не наблюдая, я смог исправить это, просто позвонив в [addToSiriButton setValue:voiceShortcut forKey:@"voiceShortcut"], где я когда-то звонил addToSiriButton.shortcut = voiceShortcut.shortcut;.
@ Виталий Алексеев, можешь лучше это объяснить?
Я перешел к настройке намерений и обнаружил, что даже имея только одну настройку намерений и работая с INUIAddVoiceShortcutButton, я не могу отслеживать мой ярлык. После того, как фраза записана, отображается фраза, добавленная в Siri.
Но каждый раз, когда приложение перезапускает, появляется кнопка «Добавить в Siri» вместо кнопки «Добавлено в Siri» с записанной фразой.
Я попытался пойти по предложению Билала, и хотя я вижу, что INVoiceShortcutCenter показывает мне мой ярлык, он не загружает его в кнопку Siri.
Мой код выглядит так для самой кнопки.
private func addSiriButton() {
let addShortcutButton = INUIAddVoiceShortcutButton(style: .blackOutline)
addShortcutButton.delegate = self
addShortcutButton.shortcut = INShortcut(intent: engine.intent )
addShortcutButton.translatesAutoresizingMaskIntoConstraints = false
siriButtonSubView.addSubview(addShortcutButton)
siriButtonSubView.centerXAnchor.constraint(equalTo: addShortcutButton.centerXAnchor).isActive = true
siriButtonSubView.centerYAnchor.constraint(equalTo: addShortcutButton.centerYAnchor).isActive = true
}
У меня есть все протоколы, и я внимательно посмотрел на приложение Soup, но просто не могу понять, что вызывает эту неточность.
Как ни странно, даже разработчики приложений British Airways отказались от этого, поскольку их кнопка имеет точно такое же поведение ошибки.
Обновлять: Я построил еще один тестовый проект с минимальной реализацией Intent, и Add to Siri и Added to Siri отлично работают. На данный момент я предполагаю, что в кодовой базе моих собственных приложений есть что-то, что вызывает такое нежелательное поведение.
обновление 2 Просто хотел, чтобы все знали, что я исправил проблему. Использование намерений работает нормально, но в самом файле определения намерений определенно есть небольшая чувствительность. Все, что мне нужно было сделать, это создать новое намерение, которое затем было сгенерировано и сработало. Кажется, мое первоначальное намерение было каким-то образом испорчено, но ошибок не было. После создания другого намерения и переназначения ему функции обработки намерения все работало по назначению. (предназначен каламбур)
Есть ли способ отобразить «Добавлено в Siri» без фразы под ним? Я вижу, как некоторые приложения это делают, но сам не могу понять.
Привет, Рюмин, я не видел способа удалить текст ниже. Сам текст добавляется только после того, как фраза была записана на сервер в качестве напоминания о том, что у вас есть фраза, это одна из причин, по которой Apple предлагает короткую запоминающуюся фразу. Другая причина, по которой отображается текст, заключается в том, что вся кнопка «Добавить в Siri» локализована, поэтому вы можете локализовать «Добавить в Siri», а также записанную фразу.
Просто еще раз взглянул на INUIAddVoiceShortcutButton, и это просто подкласс UIButton, что может означать, что вы можете установить метку на все, что захотите, хотя я все еще не на 100% в этом. Фактически INUIAddVoiceShortcutButtonStyle - это часть Init, которая устанавливает стиль кнопки, и это всего лишь свойство {get}.
да, спасибо Дэн! Похоже, что текст «Добавлено в Siri» является стандартной реализацией Apple после того, как вы задали фразу. Если я попытаюсь установить заголовок, он будет перекрывать этот текст.
Поэтому мы не можем использовать кнопку Siri по умолчанию, вам нужно использовать настраиваемый UIButton. Класс VoiceShortcutsManager проверит все голосовые намерения, а затем мы сможем выполнить поиск в этом списке, чтобы проверить, существует ли одно совпадение, если да, поэтому мы должны предложить редакцию, если нет, мы должны предложить добавить.
public class VoiceShortcutsManager {
private var voiceShortcuts: [INVoiceShortcut] = []
public init() {
updateVoiceShortcuts(completion: nil)
}
public func voiceShortcut(for order: DeviceIntent, powerState: State) -> INVoiceShortcut? {
for element in voiceShortcuts {
guard let intent = element.shortcut.intent as? ToggleStateIntent else {
continue
}
let deviceIntent = DeviceIntent(identifier: intent.device?.identifier, display: intent.device?.displayString ?? "")
if (order == deviceIntent && powerState == intent.state) {
return element
}
}
return nil
}
public func updateVoiceShortcuts(completion: (() -> Void)?) {
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
if let error = error {
print("Failed to fetch voice shortcuts with error: \(error.localizedDescription)")
}
return
}
self.voiceShortcuts = voiceShortcutsFromCenter
if let completion = completion {
completion()
}
}
}
}
А затем реализовать в своем ViewController
class SiriAddViewController: ViewController {
let voiceShortcutManager = VoiceShortcutsManager.init()
override func viewDidLoad() {
super.viewDidLoad()
contentView.btnTest.addTarget(self, action: #selector(self.testBtn), for: .touchUpInside)
}
...
@objc func testBtn() {
let deviceIntent = DeviceIntent(identifier: smartPlug.deviceID, display: smartPlug.alias)
//is action already has a shortcut, update shortcut else create shortcut
if let shortcut = voiceShortcutManager.voiceShortcut(for: deviceIntent, powerState: .off) {
let editVoiceShortcutViewController = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut)
editVoiceShortcutViewController.delegate = self
present(editVoiceShortcutViewController, animated: true, completion: nil)
} else if let shortcut = INShortcut(intent: intentTurnOff) {
let addVoiceShortcutVC = INUIAddVoiceShortcutViewController(shortcut: shortcut)
addVoiceShortcutVC.delegate = self
present(addVoiceShortcutVC, animated: true, completion: nil)
}
}
}
@available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutButtonDelegate {
func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
addVoiceShortcutViewController.delegate = self
addVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(addVoiceShortcutViewController, animated: true, completion: nil)
}
func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
editVoiceShortcutViewController.delegate = self
editVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(editVoiceShortcutViewController, animated: true, completion: nil)
}
}
@available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutViewControllerDelegate {
func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
@available(iOS 12.0, *)
extension SiriAddViewController: INUIEditVoiceShortcutViewControllerDelegate {
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) {
voiceShortcutManager.updateVoiceShortcuts(completion: nil)
controller.dismiss(animated: true, completion: nil)
}
}
}
Этот код был вдохновлен / скопирован с этой веб-страницы: https://www.nodesagency.com/test-drive-a-siri-shortcuts-intro/
Мой опыт решения этой проблемы был немного другим. Некоторые намерения, добавленные с помощью кнопки «Добавить в Siri», работали, а другие - нет. Я понял, что действующие действия не требуют параметров.
После установки значений по умолчанию для намерений, предоставляющих параметры, которые передаются в INShortcut (а затем назначаются INUIAddVoiceShortcutButton), все кнопки обновили свое состояние правильно!
Проблема в нескольких ярлыках. Где вы обрабатываете несколько ярлыков?