在半尺寸父控制器中呈现模态视图控制器
Present modal view controller in half size parent controller
我正在尝试在其他 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
和 return 您的 PresentationController
半尺寸演示:
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
在演示时将演示风格设置为 .Custom
并设置过渡代表:
pvc.modalPresentationStyle = .custom
pvc.transitioningDelegate = self
呈现控制器仅return是您呈现的视图控制器的框架:
class HalfSizePresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
guard let bounds = containerView?.bounds else { return .zero }
return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)
}
}
这是完整的工作代码:
class ViewController: UIViewController, UIViewControllerTransitioningDelegate {
@IBAction func tap(sender: AnyObject) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pvc = storyboard.instantiateViewController(withIdentifier: "CustomTableViewController") as! UITableViewController
pvc.modalPresentationStyle = .custom
pvc.transitioningDelegate = self
pvc.view.backgroundColor = .red
present(pvc, animated: true)
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
}
class HalfSizePresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
guard let bounds = containerView?.bounds else { return .zero }
return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)
}
}
添加到 Jannis 的回答:
如果您的 pop-view 是一个 UIViewController,您在 load/setup 上添加了一个 Table,您将需要确保您创建的 table 框架与实际视图的所需宽度。
例如:
let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight)
其中 chosenWidth 是您在自定义 class 中设置的宽度(在上面:containerView.bounds.width )
您不需要对单元格本身强制执行任何操作,因为 table 容器(至少在理论上)应该将单元格强制为正确的宽度。
如果你在 ViewController 中推送一些 UIViewControllerTransitioningDelegate
的委托方法,希望以半模式呈现,那将是一个干净的架构师。
假设我们有 ViewControllerA
呈现 ViewControllerB
半模态。
在 ViewControllerA
中仅使用自定义 modalPresentationStyle
呈现 ViewControllerB
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)
})
}
}
}
结果:
所有代码都发布在我的 Github
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)
要在同一个呈现 VC 上实现 UIViewControllerTransitioningDelegate
,语法与 中的 SO 答案中突出显示的完全不同。这对我来说是最棘手的部分。这是协议实现:
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController:presented, presenting: presenting)
}
对于 UIPresentationController
class,我必须覆盖变量 frameOfPresentedViewInContainerView
,而不是方法,如下所示:
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 由以下部分组成:
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)
}
}
}
干杯!
Here is Swift 4.0 some class name is change frameOfPresentedViewInContainerView
get method
第 1 步:设置代表
class ViewController: UIViewController, UIViewControllerTransitioningDelegate
第 2 步:设置委托方法
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return SetSizePresentationController(presentedViewController: presented, presenting: presenting)
}
第 3 步:您可以在此处创建自己的 Class 设置大小 (CGRect)
class SetSizePresentationController : UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
get {
return CGRect(x: 0, y: (containerView?.bounds.height ?? 0)/2, width: containerView?.bounds.width ?? 0, height: (containerView?.bounds.height ?? 0)/2)
}
}
}
第 4 步:这里有 2 行重要 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)
详情
- Xcode 12.2 (12B45b)
- Swift 5.3
解决方案 1. 默认转换
想法:
隐藏 ChildViewController
的 root view
并添加将用作 root view
的新视图。
主要逻辑:
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
let contentView = UIView()
contentView.backgroundColor = .lightGray
view.addSubview(contentView)
//...
}
}
解决方案 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
}
}
方案二.自定义转场
想法:
更改 ChildViewController
的根视图的大小。
主要逻辑:
ModalPresentationController
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) }
}
}
Update root view size
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)
}
}
解决方案 2. 完整示例
Do not forget to paste here ModalPresentationController
that defined above
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)
}
}
正常显示,然后在viewDidLayoutSubviews
中使用systemLayoutSizeFitting
将边框调整到所需的最小尺寸。这保留了 Apple 提供的视觉效果和物理效果——如果使用自定义演示,您将失去这些。
参见 this answer 上的示例代码。
从 iOS 15 开始,UISheetPresentationController 现在具有中等外观,显示半个屏幕的视图控制器。
我正在尝试在其他 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
和 return 您的 PresentationController
半尺寸演示:
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
在演示时将演示风格设置为 .Custom
并设置过渡代表:
pvc.modalPresentationStyle = .custom
pvc.transitioningDelegate = self
呈现控制器仅return是您呈现的视图控制器的框架:
class HalfSizePresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
guard let bounds = containerView?.bounds else { return .zero }
return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)
}
}
这是完整的工作代码:
class ViewController: UIViewController, UIViewControllerTransitioningDelegate {
@IBAction func tap(sender: AnyObject) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pvc = storyboard.instantiateViewController(withIdentifier: "CustomTableViewController") as! UITableViewController
pvc.modalPresentationStyle = .custom
pvc.transitioningDelegate = self
pvc.view.backgroundColor = .red
present(pvc, animated: true)
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
}
class HalfSizePresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
guard let bounds = containerView?.bounds else { return .zero }
return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)
}
}
添加到 Jannis 的回答:
如果您的 pop-view 是一个 UIViewController,您在 load/setup 上添加了一个 Table,您将需要确保您创建的 table 框架与实际视图的所需宽度。
例如:
let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight)
其中 chosenWidth 是您在自定义 class 中设置的宽度(在上面:containerView.bounds.width )
您不需要对单元格本身强制执行任何操作,因为 table 容器(至少在理论上)应该将单元格强制为正确的宽度。
如果你在 ViewController 中推送一些 UIViewControllerTransitioningDelegate
的委托方法,希望以半模式呈现,那将是一个干净的架构师。
假设我们有 ViewControllerA
呈现 ViewControllerB
半模态。
在 ViewControllerA
中仅使用自定义 modalPresentationStyle
ViewControllerB
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)
})
}
}
}
结果:
所有代码都发布在我的 Github
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)
要在同一个呈现 VC 上实现 UIViewControllerTransitioningDelegate
,语法与
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController:presented, presenting: presenting)
}
对于 UIPresentationController
class,我必须覆盖变量 frameOfPresentedViewInContainerView
,而不是方法,如下所示:
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 由以下部分组成:
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)
}
}
}
干杯!
Here is Swift 4.0 some class name is change
frameOfPresentedViewInContainerView
get method
第 1 步:设置代表
class ViewController: UIViewController, UIViewControllerTransitioningDelegate
第 2 步:设置委托方法
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return SetSizePresentationController(presentedViewController: presented, presenting: presenting)
}
第 3 步:您可以在此处创建自己的 Class 设置大小 (CGRect)
class SetSizePresentationController : UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
get {
return CGRect(x: 0, y: (containerView?.bounds.height ?? 0)/2, width: containerView?.bounds.width ?? 0, height: (containerView?.bounds.height ?? 0)/2)
}
}
}
第 4 步:这里有 2 行重要 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)
详情
- Xcode 12.2 (12B45b)
- Swift 5.3
解决方案 1. 默认转换
想法:
隐藏 ChildViewController
的 root view
并添加将用作 root view
的新视图。
主要逻辑:
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
let contentView = UIView()
contentView.backgroundColor = .lightGray
view.addSubview(contentView)
//...
}
}
解决方案 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
}
}
方案二.自定义转场
想法:
更改 ChildViewController
的根视图的大小。
主要逻辑:
ModalPresentationController
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) }
}
}
Update root view size
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)
}
}
解决方案 2. 完整示例
Do not forget to paste here
ModalPresentationController
that defined above
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)
}
}
正常显示,然后在viewDidLayoutSubviews
中使用systemLayoutSizeFitting
将边框调整到所需的最小尺寸。这保留了 Apple 提供的视觉效果和物理效果——如果使用自定义演示,您将失去这些。
参见 this answer 上的示例代码。
从 iOS 15 开始,UISheetPresentationController 现在具有中等外观,显示半个屏幕的视图控制器。