Ios 在一半大小的父控制器中显示模态视图控制器

Ios 在一半大小的父控制器中显示模态视图控制器,ios,swift,uiviewcontroller,presentmodalviewcontroller,Ios,Swift,Uiviewcontroller,Presentmodalviewcontroller,我试图在另一个大小为一半父视图控制器的viewcontroller上显示模式视图控制器。但它总是以全屏显示 我已经在我的故事板中创建了具有固定帧大小的自由形式大小的视图控制器。320 X 250 var storyboard = UIStoryboard(name: "Main", bundle: nil) var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as Pr

我试图在另一个大小为一半父视图控制器的viewcontroller上显示模式视图控制器。但它总是以全屏显示

我已经在我的故事板中创建了具有固定帧大小的自由形式大小的视图控制器。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,但没有任何帮助


请给出建议。

您可以使用
UIPresentationController
来实现这一点

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

func presentationController(用于呈现:UIViewController,呈现:UIViewController?,来源:UIViewController)->UIPresentationController?{
返回半尺寸表示控制器(presentedViewController:presented,presenting:presentingViewController)
}
演示时,请将演示样式设置为
.Custom
,并设置过渡代理:

pvc.modalPresentationStyle=.custom
pvc.transitioningDelegate=self
表示控制器仅返回表示视图控制器的帧:

class HalfSizePresentationController:UIPresentationController{
重写PresentedViewContainerView的var框架:CGRect{
guard let bounds=containerView?.bounds else{return.zero}
返回CGRect(x:0,y:bounds.height/2,width:bounds.width,height:bounds.height/2)
}
}
以下是完整的工作代码:

类ViewController:UIViewController、UIViewControllerTransitioningDelegate{
@iAction func点击(发送方:任意对象){
let storyboard=UIStoryboard(名称:“Main”,捆绑包:nil)
将pvc=storyboard.InstanceEviewController(标识符为“CustomTableViewController”)设为!UITableViewController
pvc.modalPresentationStyle=.custom
pvc.transitioningDelegate=self
pvc.view.backgroundColor=.red
当前(pvc,动画:真实)
}
func presentationController(用于呈现:UIViewController,呈现:UIViewController?,来源:UIViewController)->UIPresentationController{
返回半尺寸表示控制器(presentedViewController:presented,presenting:presentingViewController)
}
}
类HalfSizePresentationController:UIPresentationController{
重写PresentedViewContainerView的var框架:CGRect{
guard let bounds=containerView?.bounds else{return.zero}
返回CGRect(x:0,y:bounds.height/2,width:bounds.width,height:bounds.height/2)
}
}

要添加到Jannis的答案中:

如果pop视图是UIViewController,在加载/设置时向其添加表格,则需要确保创建的表格框架与实际视图的所需宽度相匹配

例如:

let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight)
其中,chosenWidth是您在自定义类中设置的宽度(在上面:containerView.bounds.width


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

如果您在ViewController中推送一些希望显示为半模式的
UIViewControllerTransitioningDelegate
委托方法,这将是一个干净的架构师

假设我们有带半模态的
ViewControllerA
显示
ViewControllerB

ViewControllerA
仅显示
ViewControllerB
中,使用自定义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)
}

@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)
        })
    }
}
}
结果是:


所有代码都发布在my

上,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)
class HalfSizePresentationController: UIPresentationController {
    override var frameOfPresentedViewInContainerView: CGRect {
        return CGRect(x: 0, y: 0, width: containerView!.bounds.width, height: containerView!.bounds.height/2)
    }
}
要在同一个呈现VC上实现
UIViewControllerTransitioningDelegate
,语法与中的SO答案中突出显示的完全不同。这对我来说是最棘手的部分。以下是协议实现:

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return HalfSizePresentationController(presentedViewController:presented, presenting: presenting)
}
对于
UIPresentationController
类,我必须重写变量
FrameofPresentedViewWinContainerView
,而不是方法,如下所示:

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)
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
中执行一个关闭视图的操作。

我使用下面的逻辑显示半屏幕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)

以防万一有人像我一样希望用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)
        }
    }
}
干杯

下面是Swift 4.0一些类名是change
FrameofPresentedViewWinContainerView
get方法

步骤1:设置委托

class ViewController: UIViewController, UIViewControllerTransitioningDelegate 
步骤2:设置委托方法

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
         return SetSizePresentationController(presentedViewController: presented, presenting: presenting)
}
步骤3:在这里,您可以为set size(CGRect)创建自己的类

步骤4:这里有两行重要的
transitioningdelegate
UIModalPresentationStyle.custom

let storyboard = UIStoryboard(name: "User", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "LicenceViewController") as! LicenceViewController
vc.modalPresentationStyle = UIModalPresentationStyle.custom
vc.transitioningDelegate = self
present(vc, animated: true)
细节
  • 代码12.2(12B45b)
  • Swift 5.3
解决方案1。默认转换 创意:

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

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)
        //...
    }
}
隐藏
子视图控制器的
根视图
,并添加将用作
根视图的新视图

主逻辑:

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

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)
        //...
    }
}
解决方案1。全样本 解决方案2。自定义转换 创意:

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

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)
        //...
    }
}
更改
ChildViewController
的根视图的大小

主逻辑:

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

        let contentView = UIView()
        contentView.backgroundColor = .lightGray
        view.addSubview(contentView)
        //...
    }
}
ModalPresentationController

 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)
更新根视图大小

解决方案2。全样本 别忘了在这里粘贴定义的ModalPresentationController