web-dev-qa-db-ja.com

ハーフサイズの親コントローラーで現在のモーダルビューコントローラー

私はモーダルビューコントローラーを、半分の親ビューコントローラーにサイズ設定された他のビューコントローラーに表示しようとしています。ただし、常に全画面表示で表示されます。

ストーリーボードに固定フレームサイズのフリーフォームサイズのView Controllerを作成しました。 320 X 250。

var storyboard = UIStoryboard(name: "Main", bundle: nil)
var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as ProductsTableViewController
self.presentViewController(pvc, animated: true, completion: nil)

Frame.superviewを設定しようとしましたが、役に立ちません。

Picture example

ご意見をお聞かせください。

29
Anton

UIPresentationControllerを使用してこれを実現できます。

このために、プレゼンティングViewControllerUIViewControllerTransitioningDelegateを実装させ、半角プレゼンテーションのPresentationControllerを返します。

func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? {
    return HalfSizePresentationController(presentedViewController: presented, presentingViewController: presenting)
} 

プレゼンテーションの際、プレゼンテーションスタイルを.Customに設定し、移行するデリゲートを設定します。

pvc.modalPresentationStyle = UIModalPresentationStyle.Custom
pvc.transitioningDelegate = self

プレゼンテーションコントローラーは、提示されたView Controllerのフレームのみを返します。

class HalfSizePresentationController : UIPresentationController {
    override func frameOfPresentedViewInContainerView() -> CGRect {
        return CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height/2)
    }
}

作業コード全体を以下に示します。

class ViewController: UIViewController, UIViewControllerTransitioningDelegate {

    @IBAction func tap(sender: AnyObject) {
        var storyboard = UIStoryboard(name: "Main", bundle: nil)
        var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as UITableViewController

        pvc.modalPresentationStyle = UIModalPresentationStyle.Custom
        pvc.transitioningDelegate = self
        pvc.view.backgroundColor = UIColor.redColor()

        self.presentViewController(pvc, animated: true, completion: nil)
    }

    func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? {
        return HalfSizePresentationController(presentedViewController: presented, presentingViewController: presentingViewController)
    }
}

class HalfSizePresentationController : UIPresentationController {
    override func frameOfPresentedViewInContainerView() -> CGRect {
        return CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height/2)
    }
}
63
Jannis

ViewControllerでUIViewControllerTransitioningDelegateのデリゲートメソッドをプッシュし、ハーフモーダルにしたい場合は、クリーンなアーキテクトになります。

ViewControllerAが存在するViewControllerBがモーダルであると仮定します。

ViewControllerAViewControllerBのみが存在し、カスタムmodalPresentationStyleがあります

func gotoVCB(_ sender: UIButton) {
    let vc = ViewControllerB()
    vc.modalPresentationStyle = .custom
    present(vc, animated: true, completion: nil)
}

そして、ViewControllerBで:

import UIKit

final class ViewControllerB: UIViewController {

lazy var backdropView: UIView = {
    let bdView = UIView(frame: self.view.bounds)
    bdView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
    return bdView
}()

let menuView = UIView()
let menuHeight = UIScreen.main.bounds.height / 2
var isPresenting = false

init() {
    super.init(nibName: nil, bundle: nil)
    modalPresentationStyle = .custom
    transitioningDelegate = self
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
    super.viewDidLoad()

    view.backgroundColor = .clear
    view.addSubview(backdropView)
    view.addSubview(menuView)

    menuView.backgroundColor = .red
    menuView.translatesAutoresizingMaskIntoConstraints = false
    menuView.heightAnchor.constraint(equalToConstant: menuHeight).isActive = true
    menuView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    menuView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    menuView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewControllerB.handleTap(_:)))
    backdropView.addGestureRecognizer(tapGesture)
}

func handleTap(_ sender: UITapGestureRecognizer) {
    dismiss(animated: true, completion: nil)
}
}

extension ViewControllerB: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 1
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    let containerView = transitionContext.containerView
    let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
    guard let toVC = toViewController else { return }
    isPresenting = !isPresenting

    if isPresenting == true {
        containerView.addSubview(toVC.view)

        menuView.frame.Origin.y += menuHeight
        backdropView.alpha = 0

        UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {
            self.menuView.frame.Origin.y -= self.menuHeight
            self.backdropView.alpha = 1
        }, completion: { (finished) in
            transitionContext.completeTransition(true)
        })
    } else {
        UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {
            self.menuView.frame.Origin.y += self.menuHeight
            self.backdropView.alpha = 0
        }, completion: { (finished) in
            transitionContext.completeTransition(true)
        })
    }
}
}

結果:

enter image description here

すべてのコードは Github で公開されています

26
Khuong

誰かがSwift 4を使ってこれを実行しようとしている場合に備えて。

class MyViewController : UIViewController {
    ...
    @IBAction func dictionaryButtonTouchUp(_ sender: UIButton) {
        let modalViewController = ...
        modalViewController.transitioningDelegate = self
        modalViewController.modalPresentationStyle = .custom

        self.present(modalViewController, animated: true, completion: nil)
    }
}

extension MyViewController : UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return HalfSizePresentationController(presentedViewController: presented, presenting: presenting)
    }
}

HalfSizePresentationControllerクラスが構成されている場所:

class HalfSizePresentationController : UIPresentationController {
    override var frameOfPresentedViewInContainerView: CGRect {
        get {
            guard let theView = containerView else {
                return CGRect.zero
            }

            return CGRect(x: 0, y: theView.bounds.height/2, width: theView.bounds.width, height: theView.bounds.height/2)
        }
    }
}

乾杯!

15
Francois Nadeau

ジャニスは全体的な戦略をうまく捉えました。 iOS 9.xでは、Swift 3.で動作しませんでした。VCを提示する際、提示されたVCを起動するアクションは、上記のように、非常に小さな変更を以下に示します。

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pvc = storyboard.instantiateViewController(withIdentifier: "SomeScreen") as SomeViewController

pvc.modalPresentationStyle = .custom
pvc.transitioningDelegate = self

present(pvc, animated: true, completion: nil)

同じ表示VCにUIViewControllerTransitioningDelegateを実装するには、SO answer in https://stackoverflow.com/a/39513247/ 2886158 。これは私にとって最も難しい部分でした。プロトコルの実装は次のとおりです。

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return HalfSizePresentationController(presentedViewController:presented, presenting: presenting)
}

UIPresentationControllerクラスの場合、以下のように、メソッドではなく変数frameOfPresentedViewInContainerViewをオーバーライドする必要がありました。

class HalfSizePresentationController: UIPresentationController {
    override var frameOfPresentedViewInContainerView: CGRect {
        return CGRect(x: 0, y: 0, width: containerView!.bounds.width, height: containerView!.bounds.height/2)
    }
}

プレゼンテーション後にビューを閉じる方法についていくつか質問がありました。提示されたVC他のVCと同じように、通常のロジックをすべて実装できます。ユーザーが提示されたVCの外部でタブ移動したときにSomeViewControllerのビューを閉じるアクションを実装します。

6
Heelara

ジャニスの答えに追加するには:

ポップビューがロード/セットアップ時にテーブルを追加するUIViewControllerの場合、作成するテーブルフレームが実際のビューの目的の幅と一致することを確認する必要があります。

例えば:

let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight)

ここで、chosenWidthは、カスタムクラスで設定した幅です(上記:containerView.bounds.width

テーブルコンテナは(少なくとも理論上)セルを適切な幅に強制する必要があるため、セル自体に何も強制する必要はありません。

1
goggelj

私はハーフスクリーンViewControllerを提示するために以下のロジックを使用します

 let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let expVC = storyboard.instantiateViewController(withIdentifier: "AddExperinceVC") as! AddExperinceVC
    expVC.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext

    self.present(expVC, animated: true, completion: nil)
1
Josh Gray