web-dev-qa-db-ja.com

View Controller、OS X間の移行

シングルウィンドウタイマーアプリケーションを記述しようとしています。ユーザーがスタートボタンを押したときに、カウントダウンなどの別のビューコントローラーを表示したいのです。また、Xcodeでストーリーボードを使用しています。スタートボタンと2番目のビューコントローラー。ただし、モーダル、シート、ポップオーバーの3つの異なるスタイルしかありません。最初のビューコントローラーをウィンドウの2番目のビューコントローラーに置き換えます。それを行う方法が見つかりません。セグエにカスタムスタイルを使用してみましたが、その中でpresentViewController:animator:メソッドを使用しましたが、animator:の引数として何を送信するかわかりません。

1つのウィンドウで1つのビューコントローラーから別のビューコントローラーに、またはその逆に遷移する最も簡単で適切な方法は何ですか?

また、ストーリーボードでビューコントローラーを選択すると、「プレゼンテーション」という属性が表示されます。これは、複数の場合と単一の場合がありますが、これらは何を表していますか?

25
user14492

最も簡単な方法は、contentViewControllerNSWindowを入れ替えることです。

// in NSViewController's subclass
@IBAction func someAction(sender: AnyObject) {
    let nextViewController = ... // instantiate from storyboard or elsewhere

    if let window = view.window where window.styleMask & NSFullScreenWindowMask > 0 {
        // adjust view size to current window
        nextViewController.view.frame = CGRectMake(0, 0, window.frame.width, window.frame.height)
    }

    view.window?.contentViewController = nextViewController
}

これはオプション#1です。

セグエを使用したい場合は、カスタムセグエを作成し、IBの識別子でクラスをセグエに設定します。

class ReplaceSegue: NSStoryboardSegue {
    override func perform() {
        if let fromViewController = sourceController as? NSViewController {
            if let toViewController = destinationController as? NSViewController {
                // no animation.
                fromViewController.view.window?.contentViewController = toViewController
            }
        }
    }
}

これはオプション#2です。

最後のオプションはNSViewControllerpresentViewController:animator:を使用しています。以下のコードは、ディゾルブアニメーション用のカスタムNSViewControllerPresentationAnimatorです。

class ReplacePresentationAnimator: NSObject, NSViewControllerPresentationAnimator {
    func animatePresentationOfViewController(viewController: NSViewController, fromViewController: NSViewController) {
        if let window = fromViewController.view.window {
            NSAnimationContext.runAnimationGroup({ (context) -> Void in
                fromViewController.view.animator().alphaValue = 0
            }, completionHandler: { () -> Void in
                viewController.view.alphaValue = 0
                window.contentViewController = viewController
                viewController.view.animator().alphaValue = 1.0
            })
        }
    }

    func animateDismissalOfViewController(viewController: NSViewController, fromViewController: NSViewController) {
        if let window = viewController.view.window {
            NSAnimationContext.runAnimationGroup({ (context) -> Void in
                viewController.view.animator().alphaValue = 0
                }, completionHandler: { () -> Void in
                    fromViewController.view.alphaValue = 0
                    window.contentViewController = fromViewController
                    fromViewController.view.animator().alphaValue = 1.0
            })
        }        
    }
}

次に、VCこのように提示します。

@IBAction func replaceAction(sender: AnyObject) {
    let nextViewController = ... // instantiate from storyboard or elsewhere
    presentViewController(nextViewController, animator: ReplacePresentationAnimator())
}

解任するには、提示されたVCでpresentingViewControllerdismissViewController:を呼び出します。

@IBAction func dismissAction(sender: AnyObject) {
    presentingViewController?.dismissViewController(self)    
}

Swift4バージョン

class ReplacePresentationAnimator: NSObject, NSViewControllerPresentationAnimator {
func animatePresentation(of viewController: NSViewController, from fromViewController: NSViewController) {
    if let window = fromViewController.view.window {
        NSAnimationContext.runAnimationGroup({ (context) -> Void in
            fromViewController.view.animator().alphaValue = 0
        }, completionHandler: { () -> Void in
            viewController.view.alphaValue = 0
            window.contentViewController = viewController
            viewController.view.animator().alphaValue = 1.0
        })
    }
}

func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) {
    if let window = viewController.view.window {
        NSAnimationContext.runAnimationGroup({ (context) -> Void in
            viewController.view.animator().alphaValue = 0
        }, completionHandler: { () -> Void in
            fromViewController.view.alphaValue = 0
            window.contentViewController = fromViewController
            fromViewController.view.animator().alphaValue = 1.0
        })
    }
}

}

この助けを願っています。

42
bluedome

親View Controllerが1つある場合は、子View Controllerを割り当て、transitionメソッドを使用できます。親ビューコントローラーのviewDidLoadに配置するコードの例:

if let firstController = self.storyboard?.instantiateController(withIdentifier: "firstController") as? NSViewController {
    firstController.view.autoresizingMask = [.width, .height]
    firstController.view.frame = self.view.bounds
    self.addChild(firstController)
    self.view.addSubview(firstController.view)
}

if let secondController = self.storyboard?.instantiateController(withIdentifier: "secondController") as? NSViewController {
    self.addChild(secondController)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    if let firstController = self.children.first, let secondController = self.children.last {
        self.transition(from: firstController, to: secondController, options: .crossfade, completionHandler: nil)
    }
}

最初の子コントローラのビューが親コントローラのビューにサブビューとして追加されていることが重要です。そうでない場合、transitionメソッドは機能しません。

上記の例では、ストーリーボードが1つのメインビューコントローラー(=セルフ)、ストーリーボードIDが「firstController」の子ビューコントローラー、ストーリーボードIDが「secondController」の子ビューコントローラーで使用されています。

7
Ely