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
How to present a ViewController on Half screen
提问by Umair Afzal
I have a UIViewController
which have only a UIView
which covers 1/3 of the viewController from bottom. Like this
我有一个UIViewController
只有一个UIView
,从底部覆盖了 viewController 的 1/3。像这样
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 UIPresentationController
class 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 presentingViewController
until you dismiss the presentedViewController
, so if you want to show the view controller over half the screen while retaining user interaction with the presentingViewController
you 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 presentedViewController
frame. You need to set the transitioningDelegate
of the presentedViewController
and implement the
这还添加了一个模糊视图和一个在您点击presentedViewController
框架外时关闭的点击。您需要设置transitioningDelegate
的presentedViewController
贯彻
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 = .custom
of the presentedViewController
方法在那里。不要忘记设置
modalPresentationStyle = .custom
presentedViewController
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 UIView
within 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 subViewController
and add a UIView
in it.
转到 StoryBoard 选择subViewController
并UIView
在其中添加一个。
For blur effect set its constraint to
对于模糊效果,将其约束设置为
(top:0,Bottom:0,Leading:0,Trailing:0)
for all sides and change its color to black
with the alpha
you want.
并将其颜色更改black
为alpha
您想要的颜色。
And after that add a other UIView
for options, set its constraints to
然后添加另一个UIView
选项,将其约束设置为
(top:-,Bottom:0,Leading:0,Trailing:0)
Set its height
constraint to equal height with superview(self.View)
and change its multipler
to 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,如下所示:
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来实现效果。