iOS - 在呈现另一个视图控制器时堆栈全屏视图控制器
Posted
技术标签:
【中文标题】iOS - 在呈现另一个视图控制器时堆栈全屏视图控制器【英文标题】:iOS - stack fullscreen viewController when presenting another viewController 【发布时间】:2020-02-24 19:13:49 【问题描述】:我有一个带有两个标签的UITabBarViewController
。我想在其中一个选项卡中全屏显示 viewController。我使用了以下代码。
let navCtrl = UINavigationController(rootViewController: eventViewController)
navCtrl.modalPresentationStyle = .fullScreen
self.navigationController?.present(navCtrl, animated: true)
它有效。 EventViewController
是全屏的。但是,当在EventViewController
中呈现另一个viewController 时,EventViewController
仍然是全屏的。但我希望它像往常一样缩小尺寸和堆叠(如图所示)。为此,我将modalPresentationStyle
更改为overCurrentContext
。
let navCtrl = UINavigationController(rootViewController: eventViewController)
navCtrl.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navCtrl, animated: true)
这样做,但它会导致另一个问题:如果我更改选项卡并关闭 EventViewController
,则呈现的 viewController 是 black
,如 question 中所述(没有一个答案有帮助) em>。
基本上我希望EventController
是全屏的,但在其中显示另一个控制器时会缩小尺寸。怎么做?
更新
一个具有相同问题的简单项目。
class TabBarController: UITabBarController
override func viewDidLoad()
super.viewDidLoad()
let ctrl = TabZeroViewController()
ctrl.tabBarItem.image = UIImage(named: "archived-task")
ctrl.tabBarItem.title = "One"
let test = TabOneViewController()
test.tabBarItem.image = UIImage(named: "Test")
test.tabBarItem.title = "Test"
let tabBarList = [ctrl, test ]
self.viewControllers = tabBarList.map
let nav = UINavigationController(rootViewController: $0)
nav.interactivePopGestureRecognizer?.isEnabled = true
return nav
class TabZeroViewController: UITableViewController
override func viewDidLoad()
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.view.backgroundColor = .white
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "\(indexPath.row)"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = ModalTableViewController()
let nav = UINavigationController(rootViewController: ctrl)
nav.modalPresentationStyle = .fullScreen
self.navigationController?.present(nav, animated: true)
class ModalTableViewController: UITableViewController
override func viewDidLoad()
self.view.backgroundColor = .red
let button = UIButton()
button.setTitle("Cancel", for: .normal)
button.addTarget(self, action: #selector(dismissModal), for: .allEvents)
let item = UIBarButtonItem()
item.customView = button
self.navigationItem.leftBarButtonItem = item
self.tableView.dataSource = self
self.tableView.delegate = self
@objc func dismissModal()
self.dismiss(animated: true, completion: nil)
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "Event"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = EventViewController()
let nav = UINavigationController(rootViewController: ctrl)
nav.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(nav, animated: true)
class TabOneViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
class EventViewController: UITableViewController
override func viewDidLoad()
self.view.backgroundColor = .red
let button = UIButton()
button.setTitle("Cancel", for: .normal)
button.addTarget(self, action: #selector(dismissModal), for: .allEvents)
let item = UIBarButtonItem()
item.customView = button
self.navigationItem.leftBarButtonItem = item
self.tableView.dataSource = self
self.tableView.delegate = self
@objc func dismissModal()
self.dismiss(animated: true, completion: nil)
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "Event"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = EventViewController()
let nav = UINavigationController(rootViewController: ctrl)
self.navigationController?.present(nav, animated: true)
将此代码添加到willConnectTo
中的SceneDelegate
。
if let windowScene = scene as? UIWindowScene
let window = UIWindow(windowScene: windowScene)
window.rootViewController = TabBarController()
self.window = window
window.makeKeyAndVisible()
当您在第一个选项卡上时,选择一个表格单元格以打开 ModalTableViewController
。然后更改标签并关闭ModalTableViewController
。
【问题讨论】:
首先,您需要更新导航控制器中的视图控制器,但导航控制器旨在用于推送视图控制器。因为它可能会导致意外行为。 我已经运行了相同的流程并且它按预期工作。有一件事要问:以 EventViewController 为根的导航控制器没有显示在整个屏幕上,您如何在显示时更改选项卡。请向我澄清这一点。 您确实提供了一个导航控制器,当您关闭任何不会关闭附加的导航控制器的控制器时,这就是您面临黑屏问题的原因。 @neerajjoshi,按照以下步骤操作,1. 设置modalPresentationStyle = .overCurrentContext
2. 呈现 viewCtrl 3. 选择另一个选项卡,然后选择相同的选项卡。 4 关闭呈现的viewCtrl。
@mahan ,我可以玩一下你的源代码吗?如果是,那么您可以创建具有此类场景的示例项目并上传到 GitHub。
【参考方案1】:
例如项目 - 在全屏上显示视图会隐藏 TabBar。但是我稍微更改了代码以提出可行的解决方案。可能你会想稍微改变一下,但我希望这能把你推向好的方向:)
实际上需要关闭ModalTableViewController
以避免黑屏。
class TabBarController: UITabBarController
override func viewDidLoad()
super.viewDidLoad()
let ctrl = TabZeroViewController()
ctrl.tabBarItem.image = UIImage(named: "archived-task")
ctrl.tabBarItem.title = "One"
let test = TabOneViewController()
test.tabBarItem.image = UIImage(named: "Test")
test.tabBarItem.title = "Test"
let tabBarList = [ctrl, test ]
let viewControllers: [UIViewController] = tabBarList.map
let nav = UINavigationController(rootViewController: $0)
nav.interactivePopGestureRecognizer?.isEnabled = true
nav.tabBarItem = $0.tabBarItem
return nav
self.setViewControllers(viewControllers, animated: false)
override var selectedViewController: UIViewController?
get return super.selectedViewController
set
if super.selectedViewController?.presentedViewController != nil
super.selectedViewController?.dismiss(animated: false, completion: nil)
super.selectedViewController = newValue
class TabZeroViewController: UITableViewController
override func viewDidLoad()
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.view.backgroundColor = .white
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "\(indexPath.row)"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = ModalTableViewController()
let nav = UINavigationController(rootViewController: ctrl)
nav.modalPresentationStyle = .currentContext
self.present(nav, animated: true)
class ModalTableViewController: UITableViewController
override func viewDidLoad()
self.view.backgroundColor = .red
let button = UIButton()
button.setTitle("Cancel", for: .normal)
button.addTarget(self, action: #selector(dismissModal), for: .allEvents)
let item = UIBarButtonItem()
item.customView = button
self.navigationItem.leftBarButtonItem = item
self.tableView.dataSource = self
self.tableView.delegate = self
@objc func dismissModal()
self.presentingViewController?.dismiss(animated: false, completion: nil)
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "Event"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = EventViewController()
let nav = UINavigationController(rootViewController: ctrl)
nav.modalPresentationStyle = .fullScreen
self.navigationController?.present(nav, animated: true)
class TabOneViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .white
class EventViewController: UITableViewController
override func viewDidLoad()
self.view.backgroundColor = .red
let button = UIButton()
button.setTitle("Cancel", for: .normal)
button.addTarget(self, action: #selector(dismissModal), for: .allEvents)
let item = UIBarButtonItem()
item.customView = button
self.navigationItem.leftBarButtonItem = item
self.tableView.dataSource = self
self.tableView.delegate = self
@objc func dismissModal()
self.dismiss(animated: true, completion: nil)
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell()
cell.textLabel?.text = "Event"
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let ctrl = EventViewController()
let nav = UINavigationController(rootViewController: ctrl)
self.navigationController?.present(nav, animated: true)
祝你好运!
【讨论】:
【参考方案2】:试试这个代码以模态方式呈现屏幕:
func getImageFromView() -> UIImage
let layer = UIApplication.shared.keyWindow?.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer?.frame.size ?? CGSize.zero, false, scale)
if let context = UIGraphicsGetCurrentContext()
layer?.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image ?? UIImage()
return UIImage()
/// This is the method to present screen modally
/// - parameter controller: controller instance on which screen will be presented
func presentScreenModally(controller: UIViewController, animated: Bool)
let loginController = UIStoryboard.loadLoginViewController()//Get instance of controller form storyboard
loginController.bgTranParentImg = getImageFromView()
let bgImage = getImageFromView()
let presentationStyleViewController = UIStoryboard.loadPresentationStyleController()// This is another controller, which I am pasting below
presentationStyleViewController.bgimage = bgImage
presentationStyleViewController.loginController = loginController
presentationStyleViewController.addChild(loginController)
controller.view.window?.addSubview(presentationStyleViewController.view)
loginController.view.frame = presentationStyleViewController.containerView.bounds
presentationStyleViewController.containerView.addSubview(loginController.view)
let navigationController = UINavigationController(rootViewController: presentationStyleViewController)
navigationController.navigationBar.isHidden = true
navigationController.modalPresentationStyle = .fullScreen
controller.navigationController?.present(navigationController, animated: animated, completion: nil)
PresentationStyleViewController 类:
class PresentationStyleViewController: UIViewController
@IBOutlet var containerView: UIView!
@IBOutlet var containeTopConstraint: NSLayoutConstraint!
@IBOutlet var containerBottomConstraint: NSLayoutConstraint!
@IBOutlet var backgroundImage: UIImageView!
var bgimage: UIImage?
let topPadding: CGFloat = 30
var loginController: LoginViewController?
override func viewDidLoad()
super.viewDidLoad()
self.uiSetup()
override func viewDidAppear(_ animated: Bool)
restorePopup()
/// Initial UI setup
func uiSetup()
containeTopConstraint.constant = self.view.frame.size.height
backgroundImage.image = bgimage
@IBAction func panGesture(_ sender: UIPanGestureRecognizer)
guard let piece = sender.view else return
let translation = sender.translation(in: piece.superview)
containeTopConstraint.constant = translation.y >= topPadding ? translation.y : topPadding
if sender.state == .ended || sender.state == .cancelled
if containeTopConstraint.constant > self.view.frame.size.height/4 && translation.y > 0
self.dismissPopup()
else
self.restorePopup()
/// Dismisses popup and controller
func dismissPopup()
containeTopConstraint.constant = self.view.frame.size.height
UIView.animate(withDuration: 0.3,
animations:
self.view.layoutIfNeeded()
, completion: (_) in
self.loginController?.btnClick_cross(UIButton())
self.dismiss(animated: false)
)
/// Restores popup at initial position
func restorePopup()
containeTopConstraint.constant = topPadding
UIView.animate(withDuration: 0.3,
animations:
self.view.layoutIfNeeded()
, completion: nil)
【讨论】:
以上是关于iOS - 在呈现另一个视图控制器时堆栈全屏视图控制器的主要内容,如果未能解决你的问题,请参考以下文章