在半尺寸父控制器中呈现模态视图控制器

Ant*_*ton 30 uiviewcontroller presentmodalviewcontroller ios swift

我试图在其他viewController上呈现模态视图控制器大小到半父视图控制器.但它始终以全屏视图显示.

我在我的故事板中创建了具有固定帧大小的自由形式大小的View控制器.320 X 250.

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

我试图设置frame.superview,它没有帮助.

图片示例

请指教.

小智 66

您可以使用a UIPresentationController来实现此目的.

为此,您可以使用演示ViewController工具UIViewControllerTransitioningDelegate并返回您PresentationController的半尺寸演示文稿:

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

在演示时,您将演示文稿样式.Custom设置为并设置转换委托:

pvc.modalPresentationStyle = UIModalPresentationStyle.Custom
pvc.transitioningDelegate = self
Run Code Online (Sandbox Code Playgroud)

演示控制器仅返回您呈现的视图控制器的帧:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是完整的工作代码:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.有用.现在,当用户单击父视图控制器时,我需要关闭我的第二个视图控制器.但我无法在父视图控制器中获取点击手势.可以请你指导我解决这个问题吗? (3认同)
  • 我根据[此主题](https://forums.developer.apple.com/thread/6129)中的解决方案编辑了修复iOS 9错误的答案 (2认同)

Khu*_*ong 29

如果你UIViewControllerTransitioningDelegate在ViewController中推送一些希望出现半模态的委托方法,那将是一个干净的架构师.

假设我们ViewControllerA现在ViewControllerB有一半的模式.

ViewControllerA目前ViewControllerB与自定义modalPresentationStyle

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

在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)
        })
    }
}
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

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


Fra*_*eau 17

以防万一有人希望像我一样使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

干杯!

  • @/francois-nadeau 在您的示例中如何使背景视图的 1/3 变灰/不透明? (3认同)

Hee*_*ara 6

Jannis很好地抓住了整体战略.它在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)
Run Code Online (Sandbox Code Playgroud)

UIViewControllerTransitioningDelegate在同一个演示VC上实现,语法是完全不同的,如/sf/answers/2765927321/中的SO答案中所突出显示的那样.这对我来说是最棘手的部分.这是协议实现:

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

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

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

关于如何在演示后解雇视图存在一些问题.您可以像任何其他VC一样在您呈现的VC上实现所有常用逻辑.SomeViewController当用户在显示的VC之外选中时,我实现了一个动作来关闭视图.

  • 我认为HalfSizeController的y坐标应该从半屏开始,它会像这样:```return CGRect(x: 0, y: (containerView!.bounds.height/2), width: containerView!。 bounds.width, height:containerView!.bounds.height/2``` (2认同)

Vas*_*huk 6

细节

  • Xcode 12.2 (12B45b)
  • 斯威夫特 5.3

解决方案1.默认转换

主意:

隐藏root viewChildViewController添加将用作 的新视图root view

主要逻辑:

class ChildViewController: UIViewController { 
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .clear

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案1.完整样本

import UIKit

// MARK: ParentViewController

class ParentViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: CGRect(x: 50, y: 50, width: 200, height: 60))
        button.setTitle("Present VC", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(touchedUpInside), for: .touchUpInside)
        view.addSubview(button)
    }

    @objc func touchedUpInside(source: UIButton) {
        let viewController = ChildViewController()
        present(viewController, animated: true, completion: nil)
    }
}

// MARK: ChildViewController

class ChildViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .clear

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)

        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
        contentView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        contentView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案 2. 自定义转换

主意:

更改 的根视图的大小ChildViewController

主要逻辑:

模态呈现控制器

protocol ModalPresentationControllerDelegate: class {
    func updateFrameOfPresentedViewInContainerView(frame: CGRect) -> CGRect
}

class ModalPresentationController: UIPresentationController {
    private weak var modalPresentationDelegate: ModalPresentationControllerDelegate!

    convenience
    init(delegate: ModalPresentationControllerDelegate,
         presentedViewController: UIViewController,
         presenting presentingViewController: UIViewController?) {
        self.init(presentedViewController: presentedViewController,
                  presenting: presentingViewController)
        self.modalPresentationDelegate = delegate
    }

    override var frameOfPresentedViewInContainerView: CGRect {
        get { modalPresentationDelegate.updateFrameOfPresentedViewInContainerView(frame: super.frameOfPresentedViewInContainerView) }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新根视图大小

class ChildViewController: UIViewController {
    init() {
        //...
        transitioningDelegate = self
        modalPresentationStyle = .custom
    }
}

extension ChildViewController: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController,
                                presenting: UIViewController?,
                                source: UIViewController) -> UIPresentationController? {
        ModalPresentationController(delegate: self, presentedViewController: presented, presenting: presenting)
    }
}

extension ChildViewController: ModalPresentationControllerDelegate {
    func updateFrameOfPresentedViewInContainerView(frame: CGRect) -> CGRect {
        CGRect(x: 0, y: frame.height/2, width: frame.width, height: frame.height/2)
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案 2. 完整样本

不要忘记将ModalPresentationController上面定义的内容粘贴到此处

import UIKit

// MARK: ParentViewController

class ParentViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: CGRect(x: 50, y: 50, width: 200, height: 60))
        button.setTitle("Present VC", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(touchedUpInside), for: .touchUpInside)
        view.addSubview(button)
    }

    @objc func touchedUpInside(source: UIButton) {
        let viewController = ChildViewController()
        present(viewController, animated: true, completion: nil)
    }
}

// MARK: ChildViewController

class ChildViewController: UIViewController {
    init() {
        super.init(nibName: nil, bundle: nil)
        transitioningDelegate = self
        modalPresentationStyle = .custom
        view.backgroundColor = .lightGray
    }

    required init?(coder: NSCoder) { super.init(coder: coder) }
}

extension ChildViewController: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController,
                                presenting: UIViewController?,
                                source: UIViewController) -> UIPresentationController? {
        ModalPresentationController(delegate: self, presentedViewController: presented, presenting: presenting)
    }
}

extension ChildViewController: ModalPresentationControllerDelegate {
    func updateFrameOfPresentedViewInContainerView(frame: CGRect) -> CGRect {
        CGRect(x: 0, y: frame.height/2, width: frame.width, height: frame.height/2)
    }
}
Run Code Online (Sandbox Code Playgroud)