Пользовательский NSStoryboardSegue не добавляет NSViewController в цепочку ответчиков

Общая цель: использовать настраиваемый сегмент раскадровки в macOS

Проблема заключается в цепочке респондентов. Согласно Apple:

Кроме того, в macOS 10.10 и более поздних версиях контроллер представления участвует в цепочке респондентов. NSViewController

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

Однако, если я использую настраиваемый переход, контроллер представления назначения не будет вести себя так, как ожидалось :( В частности, ключевые эквиваленты загадочным образом не работают - однако при тестировании ключевые эквиваленты кнопки на контроллере представления источника продолжают работать (что бесполезно / нежелательно)

В macOS 10.12 я использую следующий настраиваемый переход… Что я делаю не так?

class PushSegue: NSStoryboardSegue {

override func perform() {
    (sourceController as AnyObject).presentViewController(destinationController as! NSViewController, animator: PushAnimator())
}
}


class PushAnimator:  NSObject, NSViewControllerPresentationAnimator  {

func animatePresentation(of viewController: NSViewController, from fromViewController: NSViewController) {
    viewController.view.wantsLayer = true
    viewController.view.frame = CGRect(x: fromViewController.view.frame.size.width, y: 0,
                                       width: fromViewController.view.frame.size.width, height: fromViewController.view.frame.size.height)
    fromViewController.addChildViewController(viewController)
    fromViewController.view.addSubview(viewController.view)

    let futureFrame = CGRect(x: 0, y: 0, width: viewController.view.frame.size.width,
                             height: viewController.view.frame.size.height)

    NSAnimationContext.runAnimationGroup({ context in
        context.duration = 0.75
        viewController.view.animator().frame = futureFrame

    }, completionHandler:nil)
}

func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) {
    let futureFrame = CGRect(x: viewController.view.frame.size.width, y: 0,
                             width: viewController.view.frame.size.width, height: viewController.view.frame.size.height)

    NSAnimationContext.runAnimationGroup({ context in
        context.duration = 0.75
        context.completionHandler = {
            viewController.view.removeFromSuperview()
            viewController.removeFromParentViewController()
        }

        viewController.view.animator().frame = futureFrame
    }, completionHandler: nil)
    
}
}

person Zelko    schedule 13.04.2017    source источник
comment
какое неправильное поведение вы получаете? И какого поведения вы ожидаете? Вы должны указать это в своем вопросе. действия, кнопки и т. д. не объясняют этого. Я протестировал ваш код, и, похоже, он отлично работает (хотя я бы добавил некоторые эстетические исправления). Так в чем же проблема?   -  person Zion Perez    schedule 19.04.2017
comment
Спасибо ✌️ Улучшу вопрос. Проблема заключается в цепочке респондентов: в частности, если я представляю контроллер представления с использованием стандартного перехода к раскадровке, все работает так, как ожидалось - например, фактически работает эквивалент кнопки. Это резко контрастирует с одним из моих собственных сегментов - та же кнопка с ключевым эквивалентом не работает :(   -  person Zelko    schedule 19.04.2017
comment
Я пробовал искать ключевые эквиваленты macos, но не нашел ничего конкретного. Не могли бы вы пояснить, что вы подразумеваете под ключевым эквивалентом?   -  person Zion Perez    schedule 20.04.2017
comment
NSButton @ZionPerez   -  person Zelko    schedule 20.04.2017
comment
Символ кнопки, эквивалентный клавише @ZionPerez   -  person Zelko    schedule 20.04.2017
comment
Я опубликовал рабочее решение в своем ответе ниже. Опять же, пользовательский переход и аниматор, которые вы опубликовали, похоже, работают нормально. Я только добавил к нему красный фон. Возможно, вам нужно показать код, который вы используете для добавления ключевых эквивалентов.   -  person Zion Perez    schedule 20.04.2017
comment
Вам нужно будет более конкретно указать, чего именно вы пытаетесь достичь. В чем именно заключается проблема с цепочкой респондентов и фокусом? Пожалуйста, посмотрите мой обновленный ответ.   -  person Zion Perez    schedule 21.04.2017


Ответы (1)


Насколько я понимаю, ваш вопрос состоит из четырех частей:

  1. Как использовать собственный класс перехода
  2. Проблема с KeyEquivalents
  3. Проблема с цепочкой респондента
  4. Проблема с фокусом

Демонстрация решения

У меня есть следующий пример, корректно работающий с вашим настраиваемым классом перехода, примером ключевых эквивалентов, примером цепочки респондентов и фокусом пример (при условии, что вы имеете в виду фокус TextField).

Как видно на гифке ниже, вы можете нажать кнопку Segue для перехода, используя свой PushSegue segue. Затем нажмите кнопку Закрыть, чтобы вернуться. Стрелка вправо и влево являются ключевыми эквивалентами кнопок Segue и Dismiss соответственно. Цепочка респондента успешно передает данные с SheetController (красный экран) на MainViewController (серый экран). Кроме того, текстовые поля правильно фокусируются при переходе. Я объясню каждую функцию более подробно.

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

PushSegue

Я взял ваш PushAnimator подкласс, который вы опубликовали, и просто добавил красный фон в представление, чтобы я мог видеть его визуально. Я сделал это, добавив следующую строку в метод animatePresentation вашего класса PushAnimator:

viewController.view.layer?.backgroundColor = CGColor.init(red: 1, green: 0, blue: 0, alpha: 1)

Ключевые аналоги

Кнопке Segue назначается keyEquivalent NSRightArrowFunctionKey, а кнопке Dismiss назначается NSLeftArrowFunctionKey. Вы можете нажимать клавиши со стрелками вправо и влево, чтобы переключаться между двумя представлениями. Чтобы настроить стрелку вправо, я добавил в функцию MainViewController viewDidLoad() следующее:

let array = [unichar(NSRightArrowFunctionKey)]
mainViewSegueButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)

Чтобы настроить стрелку влево, я добавил в функцию SheetController viewDidLoad() следующее:

let array = [unichar(NSLeftArrowFunctionKey)]
dismissButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)

Цепочка респондента

Поскольку вы не описали конкретную проблему с цепочкой респондентов, я просто выполнил простой тест, чтобы проверить, работает ли она. Я установлю SheetController в dismissButton со значением nextResponder MainViewController. У меня в SheetController viewDidLoad() есть следующее:

// SheetController viewDidLoad() 
// Set Accessibility Label
dismissButton.setAccessibilityLabel("DismissButton")

// Set button target and action        
dismissButton.target = nil
dismissButton.action = #selector(MainViewController.dismissAction(_:))
        
// Set nextResponder
dismissButton.nextResponder = self.parent

Чтобы объяснить вышесказанное: сначала я установил dismissButton с меткой доступности, чтобы MainViewController мог позже идентифицировать кнопку. Установка цели кнопки в nil означает, что она будет смотреть в сторону своего nextResponder для обработки любых событий, которые она обнаруживает. Я установил действие кнопки на метод dismissAction, который объявлен в MainViewController. И, наконец, я установил nextResponder dismissButton на MainViewController.

Еще в MainViewController я установил метод dismissAction ниже. Он просто увеличивает переменную счетчика и отображает этот счетчик вместе с accessibilityLabel кнопки (та же метка доступа, которую мы установили ранее) в поле textField. Последняя строка отклоняет SheetController.

// MainViewController
func dismissAction(_ sender: AnyObject) {
    counter += 1
    let buttonAccessLabel = (sender as! NSButton).accessibilityLabel()
    helloWorldTextField.stringValue = "Action #" + counter.description + " from: " + buttonAccessLabel!
    self.dismissViewController(self.presentedViewControllers!.first!)
}

Фокус

Для этого я предполагаю, что вы имеете в виду фокус textField. В SheetController у меня sheetFocusTextField фокус в viewDidAppear:

override func viewDidAppear() {
    super.viewDidAppear()
    sheetFocusTextField.becomeFirstResponder()
}

В MainViewController у меня mainFocusTextField фокус в dismissAction методе:

mainFocusTextField.becomeFirstResponder()

Ссылка на Github

https://github.com/starkindustries/mac-os-segue-test

использованная литература

person Zion Perez    schedule 19.04.2017