NSStoryboardSegue пример кода (Yosemite Storyboard)

OS X Yosemite представила NSStoryboardSegue

"Сегмент раскадровки определяет отношения перехода или сдерживания между двумя сценами в раскадровке…"


Обновить:

• Если я пытаюсь использовать подкласс NSStoryboardSegue в раскадровке с Yosemite., Он завершается с SIGABRT.

• Если я игнорирую сегменты и вручную представляю контроллер представления, используя указанный пользовательский аниматор для представления и отклонения,

func presentViewController(_ viewController: NSViewController,
                  animator animator: NSViewControllerPresentationAnimator)

это работает как ожидалось.


Этот пост предоставляет дополнительную информацию: Анимируйте пользовательскую презентацию ViewController в OS X Yosemite

Используя это как ссылку, вот моя попытка:

class FadeSegue: NSStoryboardSegue {

    override func perform() {
        super.perform()
        sourceController.presentViewController(destinationController as NSViewController,
            animator: FadeTransitionAnimator())
    }
}


class FadeTransitionAnimator: NSObject, NSViewControllerPresentationAnimator {

    func animatePresentationOfViewController(toViewController: NSViewController, fromViewController: NSViewController) {

        toViewController.view.wantsLayer = true
        toViewController.view.layerContentsRedrawPolicy = .OnSetNeedsDisplay
        toViewController.view.alphaValue = 0
        fromViewController.view.addSubview(toViewController.view)
        toViewController.view.frame = fromViewController.view.frame

        NSAnimationContext.runAnimationGroup({ context in
            context.duration = 2
            toViewController.view.animator().alphaValue = 1
            }, completionHandler: nil)
    }

    func animateDismissalOfViewController(viewController: NSViewController, fromViewController: NSViewController) {

        viewController.view.wantsLayer = true
        viewController.view.layerContentsRedrawPolicy = .OnSetNeedsDisplay

        NSAnimationContext.runAnimationGroup({ (context) -> Void in
            context.duration = 2
            viewController.view.animator().alphaValue = 0
            }, completionHandler: {
                viewController.view.removeFromSuperview()
        })
    }

}

3 ответа

Похоже, проблема в "подклассе" Swift NSStoryboardSegue. Если вы реализуете ту же функциональность, используя Objective-C, все будет работать как положено. Проблема конкретно с вашим FadeSeque учебный класс. Объект-аниматор отлично работает в Objective-C или Swift.

Итак, это:

class FadeSegue: NSStoryboardSegue {
    override func perform() {
    super.perform()
    sourceController.presentViewController(destinationController as NSViewController,
        animator: FadeTransitionAnimator())
    }
} 

Будет работать, если предоставляется класс Objective-C:

@interface MyCustomSegue : NSStoryboardSegue
@end

@implementation FadeSegue

- (void)perform {
   id animator = [[FadeTransitionAnimator alloc] init];
    [self.sourceController presentViewController:self.destinationController
                                        animator:animator];
   }
   @end

(Я не думаю, что вам нужно позвонить super)

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

  • NSStoryboardSegue переходит от одного NSViewController к другому в той же раскадровке
  • Представлен NSViewController: методы для достижения того же эффекта для отдельного NSViewController на основе Xib без использования Storyboard SeguepresentViewController:asPopoverRelativeToRect:ofView:preferredEdge:behavior:
    presentViewControllerAsSheet:
    presentViewControllerAsModalWindow:
    presentViewController:animator:
  • аниматор и переход объектов в Objective-C и Swift

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

Хорошо, я разыскал проблему EXC_BAD_ACCESS. Глядя на трассировку стека, она, кажется, как-то связана с преобразованием (Objective-C) NSString в (Swift) String.

Это заставило задуматься о identifier собственностью NSStoryboardSegue, Это используется при настройке сегментов в раскадровке и не очень полезен в пользовательских сегментах, созданных в коде. Однако оказывается, что если вы установите идентификатор в раскадровке для любого строкового значения, даже "", сбой исчезнет.

identifier свойство является NSString* в Objective-C

@property(readonly, copy) NSString *identifier

и необязательная строка в Swift:

var identifier: String? { get }

Обратите внимание на статус только для чтения. Вы можете установить идентификатор только при инициализации объекта.

Обозначение инициализатора для NSStoryboardSegue выглядит так в Objective-C:

- (instancetype)initWithIdentifier:(NSString *)identifier
                            source:(id)sourceController
                       destination:(id)destinationController

и в Свифте:

init(identifier identifier: String,
         source sourceController: AnyObject,
    destination destinationController: AnyObject)

Обратите внимание на необязательное требование в инициализаторе Swift. В этом проблема и крушение. Если вы не установите преднамеренно идентификатор в раскадровке, будет вызван назначенный инициализатор пользовательского сегмента с использованием значения nil для идентификатора. Не проблема в Objective-C, но плохие новости для Swift.

Быстрое решение состоит в том, чтобы установить строку идентификатора в раскадровке. Для более надежного решения оказывается, что вы можете переопределить назначенный инициализатор в своем пользовательском подклассе для перехвата строки с нулевым значением. Затем вы можете заполнить его значением по умолчанию, прежде чем переходить к инициализатору super:

override init(identifier: String?, 
              source sourceController: AnyObject,
              destination destinationController: AnyObject) {
        var myIdentifier : String
        if identifier == nil {
            myIdentifier = ""
        } else {
            myIdentifier = identifier!
        }
    super.init(identifier: myIdentifier, 
                source: sourceController, 
                destination: destinationController)
}

Я обновил пример проекта, чтобы отразить это решение

Та же самая проблема приходит ко мне, так как я забыл сделать идентичность для перехода.

После этого мой подкласс segue мог работать нормально.

Настоятельно рекомендуем взглянуть на документацию Apple. Если вы немного углубитесь в это, вы заметите в perform метод, вы можете переопределить анимацию и такие:

СВИФТ

func perform()

Objective-C

- (void)perform

"Вы можете переопределить этот метод в своем подклассе NSStoryboardSegue, чтобы выполнить настраиваемую анимацию между начальным / содержащим контроллером и конечным / содержащим контроллером для сеанса раскадровки. Как правило, вы должны использовать Core Animation для настройки анимации от одного набора представлений до далее. Для более сложных анимаций вы можете сделать снимок двух иерархий представлений и манипулировать ими вместо объектов представлений.*

Независимо от того, как вы выполняете анимацию, вы несете ответственность за установку контроллера представления назначения или контроллера окна (и содержащихся в нем представлений) в нужном месте, чтобы он мог обрабатывать события. Как правило, это влечет за собой вызов одного из методов представления в классе NSViewController."

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

Другие вопросы по тегам