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 Segue
presentViewController: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 в дикой природе, и вы должны обнаружить, что они очень похожи.