如何在半屏幕上呈现ViewController

时间:2017-02-08 07:20:23

标签: ios swift uiviewcontroller swift3

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

enter image description here

我想在另一个ViewController上呈现这个viewController。它应该从底部动画显示,它应该忽略到底部动画。

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

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

修改

这是我迄今为止所尝试过的。我创建了这些类

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

}

在正在呈现的viewController中,我正在执行以下操作

var customTransitioningDelegate: MYFadeInFadeOutTransitioning? = MYFadeInFadeOutTransitioning()

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

    customTransitioningDelegate?.backgroundColorAlpha = 0.0
} 

它确实呈现了viewController,我也可以看到背景viewController。但我希望它能从底部呈现动画效果。用动画解雇到底部。我怎么能这样做?

7 个答案:

答案 0 :(得分:23)

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

presentingViewController

当您点击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 } } 框外时,这也会添加模糊视图和点击以关闭。您需要设置presentedViewController的{​​{1}}并实施

transitioningDelegate

那里的方法。不要忘记也设置 presentedViewController  presentViewController的内容

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

答案 1 :(得分:7)

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

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

答案 2 :(得分:2)

您可以使用UIPresentationController来实现此目的。在呈现ViewController时实现UIViewControllerTransitioningDelegate方法,并从委托方法

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

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

编辑:

在Github中找到的示例项目

https://github.com/martinnormark/HalfModalPresentationController

答案 3 :(得分:2)

有更新的代码来实现此功能。在您要呈现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)
    } 

转到StoryBoard选择 subViewController 在其中添加 UIViews 以获取模糊效果将其约束设置为(顶部:0,底部) :0,领先:0,尾随:0)所有方面,并将其颜色更改为黑色,并根据需要设置 alpha 。之后添加其他 UIViews for options ,将其约束设置为(top: - ,Bottom:0,Leading:0,Trailing:0),设置它&#39 ; 高度约束与superview(self.View)相等的高度并将其倍数更改为0.33或0.34。 希望能帮助到你。 屏幕短片。enter image description here

答案 4 :(得分:2)

iOS 15(测试版)

有一个新类 UISheetPresentationController,它包含一个名为 detents 的属性。这可让您指定所需的行为类型。

半屏单页

class ViewController: UIViewController {
    @IBAction func nextButtonPressed(_ sender: Any) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: "NextViewController")
        
        if let presentationController = viewController.presentationController as? UISheetPresentationController {
            presentationController.detents = [.medium()] /// set here!
        }
        
        self.present(viewController, animated: true)
    }
}

结果:

Half screen sheet

半屏和全屏表

只需将 presentationController.detents = [.medium()] 更改为:

presentationController.detents = [.medium(), .large()]

结果:

Half screen sheet

答案 5 :(得分:0)

如果您希望使用@DatForis答案中的功能,但又不想弄乱重写演示文稿控制器,则可以使用如下所示的全屏模式来创建一种明显的破解方法:

modal_example

您可以执行的操作是像平常一样显示全屏模式,但是在设计模式时,请创建两个顶视图,一个用于模式,另一个视图放置在背景上。放在背景上的视图应该是透明的,以便可以从呈现的视图控制器中看到该视图。

然后将其消除,您将执行以下操作:

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

希望/能帮助某人!

答案 6 :(得分:-1)

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