NSStoryboardSegue示例代码(Yosemite故事板) [英] NSStoryboardSegue sample code (Yosemite Storyboard)
问题描述
OS X Yosemite引入了 NSStoryboardSegue
OS X Yosemite introduced NSStoryboardSegue
"A故事板segue指定了故事板中两个场景之间的过渡或封闭关系…"
更新:
•如果我尝试在带有优胜美地的情节提要中使用NSStoryboardSegue子类,则它会因SIGABRT而崩溃.
• If I attempt to use a NSStoryboardSegue subclass in a Storyboard with Yosemite., it crashes with SIGABRT.
•如果我忽略了segues,并使用指定的自定义动画器手动呈现视图控制器以进行呈现和消除,则
• If I ignore segues, and manually present a view controller using a specified, custom animator for presentation and dismissal,
func presentViewController(_ viewController: NSViewController,
animator animator: NSViewControllerPresentationAnimator)
它按预期工作.
这篇文章提供了更多的见解:对ViewController进行动画自定义演示在OS X Yosemite中
This post provides additional insight: Animate custom presentation of ViewController in OS X Yosemite
以此为参考,到目前为止,这是我的尝试:
Using that as a reference, here's my attempt so far:
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()
})
}
}
推荐答案
问题似乎出在NSStoryboardSegue的Swift的子类化"上.如果使用Objective-C实现相同的功能,那么一切都会按预期进行.问题是FadeSeque
类有关.动画对象可以在Objective-C 或 Swift中正常工作.
The problem appears to be with the Swift 'subclassing' of NSStoryboardSegue. If you implement the same functionality using Objective-C, everything works as expected. The problem is specifically with your FadeSeque
class. The animator object works fine in either Objective-C or Swift.
所以这个:
class FadeSegue: NSStoryboardSegue {
override func perform() {
super.perform()
sourceController.presentViewController(destinationController as NSViewController,
animator: FadeTransitionAnimator())
}
}
如果作为Objective-C类提供,则将起作用:
Will work if provided as an Objective-C class:
@interface MyCustomSegue : NSStoryboardSegue
@end
@implementation FadeSegue
- (void)perform {
id animator = [[FadeTransitionAnimator alloc] init];
[self.sourceController presentViewController:self.destinationController
animator:animator];
}
@end
(我认为您不需要致电super
)
(I don't think you need to call super
)
由于似乎没有任何地方对此进行记录,因此我在github上做了一个小项目演示:
As this doesn't seem to be documented much anywhere, I have made a small project on github to demonstrate:
- NSStoryboardSegue从一个NSViewController过渡到同一Storyboard中的另一个
- NSViewController当前:使用情节提要Segue对单独的基于Xib的NSViewController 不实现相同效果的方法
presentViewController:asPopoverRelativeToRect:ofView:preferredEdge:behavior:
presentViewControllerAsSheet:
presentViewControllerAsModalWindow:
presentViewController:animator:
- Objective-C和Swift中的动画器和segue对象
- NSStoryboardSegue transitions from one NSViewController to another in the same Storyboard
- NSViewController present: methods to achieve the same affect to a separate Xib-based NSViewController without using a Storyboard Segue
presentViewController:asPopoverRelativeToRect:ofView:preferredEdge:behavior:
presentViewControllerAsSheet:
presentViewControllerAsModalWindow:
presentViewController:animator:
- animator and segue objects in Objective-C and Swift
编辑
好的,我已经找到了EXC_BAD_ACCESS问题.在堆栈跟踪中查看,似乎与(Objective-C)NSString到(Swift)字符串转换有关.
OK I've tracked down the EXC_BAD_ACCESS issue. Looking in the stack trace it seemed to have something to do with (Objective-C) NSString to (Swift) String conversion.
这使NSStoryboardSegue
的identifier
属性产生了疑问.在情节提要中设置Segues时使用此功能,而在用代码创建的Custom Segues中则没有太大用处.但是,事实证明,如果在情节提要中将标识符设置为任何字符串值,甚至是",崩溃都将消失.
That made wonder about the identifier
property of NSStoryboardSegue
. This is used when setting up segues in the Storyboard, and is not so useful in Custom segues created in code. However, it turns out that if you set an identifier in the storyboard to any string value, even "", the crash disappears.
identifier
属性是Objective-C中的NSString *
The identifier
property is an NSString* in Objective-C
@property(readonly, copy) NSString *identifier
和Swift中的可选String:
and an optional String in Swift:
var identifier: String? { get }
请注意只读状态.您只能在初始化对象时设置标识符.
Note the read-only status. You can only set the identifier on initialising the object.
NSStoryboardSegue
的指示符初始化程序在Objective-C中如下所示:
The designator initialiser for NSStoryboardSegue
looks like this in Objective-C:
- (instancetype)initWithIdentifier:(NSString *)identifier
source:(id)sourceController
destination:(id)destinationController
和Swift:
init(identifier identifier: String,
source sourceController: AnyObject,
destination destinationController: AnyObject)
请注意Swift初始化程序中的非可选要求.问题和崩溃就在其中.如果您不故意在情节提要中设置标识符,则将使用该标识符的nil值来调用Custom segue的指定初始化程序.在Objective-C中这不是问题,但对Swift来说是个坏消息.
Note the non-optional requirement in the Swift initialiser. Therein lies the problem and the crash. If you don't deliberately set an identifier in the storyboard, the Custom segue's designated initialiser will be called using a nil value for the identifier. Not a problem in Objective-C, but bad news for Swift.
快速解决方案是确保您在情节提要中设置标识符字符串.对于更健壮的解决方案,事实证明您可以在自定义子类中覆盖指定的初始化程序,以截取零值的字符串.然后,您可以将其填充为默认值,然后再传递给super的指定初始化程序:
The quick solution is to ensure you set an identifier string in Storyboard. For a more robust solution, it turns out that you can override the designated initialiser in your custom subclass to intercept a nil-valued string. Then you can fill it in with a default value before passing on to super's designated initialiser:
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)
}
我已经更新了示例项目以反映该解决方案
I have updated the sample project to reflect this solution
这篇关于NSStoryboardSegue示例代码(Yosemite故事板)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!