ios 如何在半屏上呈现 ViewController

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42106980/
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 08:51:35  来源:igfitidea点击:

How to present a ViewController on Half screen

iosswiftuiviewcontrollerswift3

提问by Umair Afzal

I have a UIViewControllerwhich have only a UIViewwhich covers 1/3 of the viewController from bottom. Like this

我有一个UIViewController只有一个UIView,从底部覆盖了 viewController 的 1/3。像这样

enter image description here

在此处输入图片说明

I want to present this viewController on an other ViewController. It should appear from bottom animated and it should dismiss to the bottom animated.

我想在另一个 ViewController 上展示这个 viewController。它应该从底部动画出现,它应该消失到底部动画。

But I do not want it to cover the whole Screen. The viewController on which it is presented should be visible in the back.

但我不希望它覆盖整个屏幕。呈现它的 viewController 应该在后面可见。

It seems like a basic question But I am unable to get it done. Can someone please point me to the direction ?

这似乎是一个基本问题,但我无法完成。有人可以指出我的方向吗?

Edit:

编辑:

This is what I have tried so Far. I have created these classes

这是我到目前为止所尝试的。我创建了这些类

// MARK: -

class MyFadeInFadeOutTransitioning: NSObject, UIViewControllerTransitioningDelegate {
var backgroundColorAlpha: CGFloat = 0.5
var shoulDismiss = false

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    let fadeInPresentAnimationController = MyFadeInPresentAnimationController()
        fadeInPresentAnimationController.backgroundColorAlpha = backgroundColorAlpha

    return fadeInPresentAnimationController
}

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

    let fadeOutDismissAnimationController = MyFadeOutDismissAnimationController()

    return fadeOutDismissAnimationController
}

}

// MARK: -

class MYFadeInPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

let kPresentationDuration = 0.5
var backgroundColorAlpha: CGFloat?

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

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!

    toViewController.view.backgroundColor = UIColor.clear

    let toViewFrame = transitionContext.finalFrame(for: toViewController)
    let containerView = transitionContext.containerView

    if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) {
        let transform = CGAffineTransform(translationX: 0.0, y: pickerContainerView.frame.size.height)
        pickerContainerView.transform = transform
    }

    toViewController.view.frame = toViewFrame
    containerView.addSubview(toViewController.view)

    UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear , animations: {
        toViewController.view.backgroundColor = UIColor(white: 0.0, alpha: self.backgroundColorAlpha!)

        if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) {
            pickerContainerView.transform = CGAffineTransform.identity
        }

    }) { (finished) in
        transitionContext.completeTransition(true)
    }
}

}

// MARK: -

class MYFadeOutDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
let kDismissalDuration = 0.15

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

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
    let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
    let containerView = transitionContext.containerView

    containerView.addSubview(toViewController.view)
    containerView.sendSubview(toBack: toViewController.view)

    UIView.animate(withDuration: kDismissalDuration, delay: 0.0, options: .curveLinear, animations: {
        //            fromViewController.view.backgroundColor = UIColor.clearColor()
        //            if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) {
        //                let transform = CGAffineTransformMakeTranslation(0.0, pickerContainerView.frame.size.height)
        //                pickerContainerView.transform = transform
        //            }
        fromViewController.view.alpha = 0.0

    }) { (finished) in
        let canceled: Bool = transitionContext.transitionWasCancelled
        transitionContext.completeTransition(true)

        if !canceled {
            UIApplication.shared.keyWindow?.addSubview(toViewController.view)
        }
    }
}

}

And in the viewController which is being presented, I am doing as follows

在正在呈现的 viewController 中,我正在做如下

var customTransitioningDelegate: MYFadeInFadeOutTransitioning? = MYFadeInFadeOutTransitioning()

    init() {
    super.init(nibName: "SomeNibName", bundle: Bundle.main)
    transitioningDelegate = customTransitioningDelegate
    modalPresentationStyle = .custom

    customTransitioningDelegate?.backgroundColorAlpha = 0.0
} 

It do present the viewController and I can see the background viewController as well. But I want it to be presented from bottom with animation. And dismiss to bottom with animation. How can I do that ?

它确实显示了 viewController,我也可以看到背景 viewController。但我希望它通过动画从底部呈现。并用动画解散到底部。我怎样才能做到这一点 ?

回答by DatForis

If you want to present a view controller over half a screen I suggest using the UIPresentationControllerclass it will allow you to set the frame of the view controller when it is presented. A word of advice, this method will stop the user interaction of the presentingViewControlleruntil you dismiss the presentedViewController, so if you want to show the view controller over half the screen while retaining user interaction with the presentingViewControlleryou should use container views like the other answers suggested. This is an example of a UIPresentationController class that does what you want

如果你想在半个屏幕上呈现一个视图控制器,我建议使用这个UIPresentationController类,它允许你在呈现时设置视图控制器的框架。忠告,此方法将停止 的用户交互,presentingViewController直到您关闭presentedViewController,因此,如果您想在一半屏幕上显示视图控制器,同时保留用户与 的交互,presentingViewController则应使用容器视图,如建议的其他答案。这是一个 UIPresentationController 类的例子,它可以做你想要的

import UIKit
class ForgotPasswordPresentationController: UIPresentationController{
    let blurEffectView: UIVisualEffectView!
    var tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
    func dismiss(){
        self.presentedViewController.dismiss(animated: true, completion: nil)
    }
    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
        blurEffectView = UIVisualEffectView(effect: blurEffect)
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismiss))
        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.blurEffectView.isUserInteractionEnabled = true
        self.blurEffectView.addGestureRecognizer(tapGestureRecognizer)
    }
    override var frameOfPresentedViewInContainerView: CGRect{
        return CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height/2), size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height/2))
    }
    override func dismissalTransitionWillBegin() {
        self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
            self.blurEffectView.alpha = 0
        }, completion: { (UIViewControllerTransitionCoordinatorContext) in
            self.blurEffectView.removeFromSuperview()
        })
    }
    override func presentationTransitionWillBegin() {
        self.blurEffectView.alpha = 0
        self.containerView?.addSubview(blurEffectView)
        self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
            self.blurEffectView.alpha = 1
        }, completion: { (UIViewControllerTransitionCoordinatorContext) in

        })
    }
    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        presentedView!.layer.masksToBounds = true
        presentedView!.layer.cornerRadius = 10
    }
    override func containerViewDidLayoutSubviews() {
        super.containerViewDidLayoutSubviews()
        self.presentedView?.frame = frameOfPresentedViewInContainerView
        blurEffectView.frame = containerView!.bounds
    }
}

This also adds a blur view and a tap to dismiss when you tap outside the presentedViewControllerframe. You need to set the transitioningDelegateof the presentedViewControllerand implement the

这还添加了一个模糊视图和一个在您点击presentedViewController框架外时关闭的点击。您需要设置transitioningDelegatepresentedViewController贯彻

presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?

presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?

method in there. Don't forget to also set modalPresentationStyle = .customof the presentedViewController

方法在那里。不要忘记设置 modalPresentationStyle = .custompresentedViewController

I find the usage of the UIPresentationController to be a much cleaner approach. Good luck

我发现 UIPresentationController 的使用是一种更简洁的方法。祝你好运

回答by regetskcob

I'd recommend to implement this feature by using Container Views. Take a look herefor reference.

我建议使用容器视图来实现此功能。看看这里以供参考。

This means you can show a UIViewController(and its subclasses) embedded in a UIViewwithin another view controller. Then you can animate the fade-in or whatever you want.

这意味着您可以在另一个视图控制器中显示UIViewController嵌入在 a 中的a (及其子类)UIView。然后你可以动画淡入或任何你想要的。

回答by AbdulRehman Warraich

There is the updated code to achieve this functionality. On action where you want to present ViewController

有更新的代码来实现此功能。在您想要呈现 ViewController 的操作上

@IBAction func btnShow(_ sender: Any) {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let pvc = storyboard.instantiateViewController(withIdentifier: "SubViewController") as! SubViewController
    pvc.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
    self.present(pvc, animated: true, completion: nil)
}

Go to StoryBoard select subViewControllerand add a UIViewin it.

转到 StoryBoard 选择subViewControllerUIView在其中添加一个。

For blur effect set its constraint to

对于模糊效果,将其约束设置为

(top:0,Bottom:0,Leading:0,Trailing:0)

for all sides and change its color to blackwith the alphayou want.

并将其颜色更改blackalpha您想要的颜色。

And after that add a other UIViewfor options, set its constraints to

然后添加另一个UIView选项,将其约束设置为

(top:-,Bottom:0,Leading:0,Trailing:0)

Set its heightconstraint to equal height with superview(self.View)and change its multiplerto 0.33 or 0.34.

将其height约束设置为equal height with superview(self.View)并将其更改multipler为 0.33 或 0.34。

Hope it helps.

希望能帮助到你。

回答by Suhit Patil

You can use a UIPresentationControllerto achieve this. Implement the UIViewControllerTransitioningDelegatemethod on presenting ViewController and return your PresentationController from delegate method

您可以使用UIPresentationController来实现这一点。在呈现 ViewController 时实现UIViewControllerTransitioningDelegate方法并从委托方法返回您的 PresentationController

func presentationController(forPresented presented: UIViewController, 
                          presenting: UIViewController?, 
                              source: UIViewController) -> UIPresentationController? 

You can refer this answerwhich has similar requirement. Alternatively you can use UIView animation or embedded view controller as suggested in the other answers.

你可以参考这个有类似要求的答案。或者,您可以按照其他答案中的建议使用 UIView 动画或嵌入式视图控制器。

Edit:

编辑:

sample project found in Github

在 Github 中找到的示例项目

https://github.com/martinnormark/HalfModalPresentationController

https://github.com/martinnormark/HalfModalPresentationController

回答by Braden Holt

If you want the functionality from the @DatForis answer but don't want to mess with overriding the presentation controller you can create a sort of obvious hack when using a full screen modal as shown here:

如果您想要@DatForis 答案中的功能,但又不想覆盖演示控制器,则可以在使用全屏模式时创建一种明显的 hack,如下所示:

modal_example

modal_example

What you can do is present a full screen modal just like you normally would, however when you design the modal create two top views, one for the modal and a second view that would be placed over the background. The view that you place over the background should be transparent so that you can see the view from your presenting view controller.

您可以做的是像往常一样呈现全屏模态,但是当您设计模态时,会创建两个顶视图,一个用于模态,第二个视图将放置在背景上。您放置在背景上的视图应该是透明的,以便您可以从呈现视图控制器看到视图。

Then to dismiss it you'd do something like this:

然后关闭它你会做这样的事情:

@IBOutlet weak var transparentView: UIView!

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(false)

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped(gesture:)))
    transparentView.addGestureRecognizer(tapGesture)
}

@objc func backgroundTapped(gesture: UIGestureRecognizer) {
    self.dismiss(animated: true, completion: nil)
}

Hope that makes sense / helps someone!

希望有意义/可以帮助某人!

回答by Pranav Pravakar

You can also achieve the effect by presenting view controller modally and setting the presentation style property to over full screen, transition style property to cover vertical and setting the alpha component of view's background color to 0.1.

您也可以通过模态呈现视图控制器并将呈现样式属性设置为全屏,过渡样式属性为覆盖垂直并将视图背景颜色的alpha分量设置为0.1来实现效果。