ios 在半尺寸父控制器中显示模态视图控制器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29219688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:18:52  来源:igfitidea点击:

Present modal view controller in half size parent controller

iosswiftuiviewcontrollerpresentmodalviewcontroller

提问by Anton

I am trying to present modal view controller on other viewcontroller sized to half parent view controller. But it always present in full screen view.

我正在尝试在大小为父视图控制器一半的其他视图控制器上呈现模态视图控制器。但它始终以全屏视图显示。

I have created freeform sized View controller in my storyboard with fixed frame size. 320 X 250.

我在我的故事板中创建了具有固定帧大小的自由大小的视图控制器。320 X 250。

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

I have tried to set frame.superview and it doesn't help.

我试图设置 frame.superview 并没有帮助。

Picture example

图片示例

Please advice.

请指教。

回答by Jannis

You can use a UIPresentationControllerto achieve this.

您可以使用 aUIPresentationController来实现这一点。

For this you let the presenting ViewControllerimplement the UIViewControllerTransitioningDelegateand return your PresentationControllerfor the half sized presentation:

为此,您让演示ViewController实现UIViewControllerTransitioningDelegate并返回您PresentationController的半尺寸演示:

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

When presenting you set the presentation style to .Customand set your transitioning delegate:

演示时,您将演示样式.Custom设置为并设置您的过渡委托:

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

The presentation controller only returns the frame for your presented view controller:

呈现控制器只返回呈现的视图控制器的框架:

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

Here is the working code in its entirety:

这是完整的工作代码:

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: containerView.bounds.height/2, width: containerView.bounds.width, height: containerView.bounds.height/2)
    }
}

回答by Khuong

It would be a a clean architect if you push some delegate method of UIViewControllerTransitioningDelegatein your ViewController that want to be present half modal.

如果您UIViewControllerTransitioningDelegate在 ViewController 中推送一些想要呈现半模态的委托方法,那将是一个干净的架构师。

Assuming we have ViewControllerApresent ViewControllerBwith half modal.

假设我们已经ViewControllerA呈现ViewControllerB了半模态。

in ViewControllerAjust present ViewControllerBwith custom modalPresentationStyle

ViewControllerA刚刚现在ViewControllerB定制modalPresentationStyle

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

And in ViewControllerB:

在 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)
}

@objc 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)
        })
    }
}
}

The result:

结果:

enter image description here

在此处输入图片说明

All code is published at my Github

所有代码都发布在我的Github 上

回答by Francois Nadeau

Just in case someone is looking to do this with Swift 4, as I was.

以防万一有人想用 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)
    }
}

Where the HalfSizePresentationController class is composed of:

其中 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)
        }
    }
}

Cheers!

干杯!

回答by Heelara

Jannis captured the overall strategy well. It didn't work for me in iOS 9.x with swift 3. On the presenting VC, the action to launch the presented VC is similar to what was presented above with some very minor changes as below:

Jannis 很好地掌握了整体战略。它在带有 swift 3 的 iOS 9.x 中对我不起作用。 在呈现的 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)

To implement UIViewControllerTransitioningDelegateon the same presenting VC, the syntax is quite different as highlighted in SO answer in https://stackoverflow.com/a/39513247/2886158. This is was the most tricky part for me. Here is the protocol implementation:

UIViewControllerTransitioningDelegate在相同的呈现 VC 上实现,语法完全不同,如https://stackoverflow.com/a/39513247/2886158中的 SO 答案中突出显示的那样。这对我来说是最棘手的部分。下面是协议实现:

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

For the UIPresentationControllerclass, I had to override the variable frameOfPresentedViewInContainerView, not method, as below:

对于UIPresentationController该类,我必须覆盖变量frameOfPresentedViewInContainerView而不是方法,如下所示:

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

There were some questions about how to dismiss the view after presentation. You can implement all the usual logic on your presented VC like any other VC. I implementation an action to dismiss the view in SomeViewControllerwhen a user tabs outside the presented VC.

有一些关于如何在演示后关闭视图的问题。您可以像任何其他 VC 一样在您呈现的 VC 上实现所有常用逻辑。SomeViewController当用户在呈现的 VC 之外进行选项卡时,我实现了一个关闭视图的操作。

回答by zevij

To add to Jannis' answer:

添加到 Jannis 的回答中:

In case your pop-view is a UIViewController to which you ADD a Table on load/setup, you will need to ensure that the table frame you create matches the desired width of the actual view.

如果您的 pop-view 是一个 UIViewController,您在加载/设置时向其中添加一个 Table,您将需要确保您创建的表格框架与实际视图的所需宽度相匹配。

For example:

例如:

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

where chosenWidthis the width you set in your custom class (in the above: containerView.bounds.width)

其中selectedWidth是您在自定义类中设置的宽度(在上面:containerView.bounds.width

You do not need to enforce anything on the Cell itself as the table container (at least in theory) should force the cell to the right width.

您不需要对单元格本身强制执行任何操作,因为表格容器(至少在理论上)应该将单元格强制为正确的宽度。

回答by Josh Gray

I use below logic to present half screen ViewController

我使用以下逻辑来呈现半屏 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)