在容器视图中关闭模态和 return 以呈现子 ViewController
dimiss modal and return to presented childViewController in containerView
我在关闭从容器视图中的 childviewController 呈现的模态视图时遇到了一些问题。我有一个 UINavigationController 作为 rootViewController (MainNavigationController),并呈现来自 selectedSegmentIndex 1 (secondViewController) 的 childViewController 之一的模式。模态呈现良好,但是当我关闭模态以返回到 secondViewController(HomeController 的子类)时,它 return 使我回到 selectedIndex 0,而不是从中呈现的 selectedIndex 1 childViewController。我希望模式消失,return 用户回到 childViewController,它是从(secondViewController)呈现的,而不是 return 回到 selectedIndex 0。提前致谢!
// NavigationConroller 作为 rootViewController
class MainNavigationController: UINavigationController {
var segmentedController: UISegmentedControl!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let vc1 = TravelersFeedVC()
let vc2 = ProfileVC()
if isLoggedIn() {
// assume user is logged in
let homeController = HomeController()
viewControllers = [homeController]
homeController.firstViewController = vc1
homeController.secondViewController = vc2
} else {
perform(#selector(showLoginController), with: nil, afterDelay: 0.01)
}
}
fileprivate func isLoggedIn() -> Bool {
return UserDefaults.standard.isLoggedIn()
}
func showLoginController() {
let loginController = LoginController()
present(loginController, animated: true, completion: {
// perhaps do something here later
})
}
}
// HomeController 作为 parentViewController
class HomeController: UIViewController, FBSDKLoginButtonDelegate {
// child view controllers to put inside content view
var firstViewController: TravelersFeedVC?
var secondViewController: ProfileVC?
private var activeViewController: UIViewController? {
didSet {
removeInactiveViewController(inactiveViewController: oldValue)
updateActiveViewController()
}
}
private func removeInactiveViewController(inactiveViewController: UIViewController?) {
if let inActiveVC = inactiveViewController {
// call before removing child view controller's view from hierarchy
inActiveVC.willMove(toParentViewController: nil)
inActiveVC.view.removeFromSuperview()
// call after removing child view controller's view from hierarchy
inActiveVC.removeFromParentViewController()
}
}
private func updateActiveViewController() {
if let activeVC = activeViewController {
// call before adding child view controller's view as subview
addChildViewController(activeVC)
activeVC.view.frame = contentView.bounds
contentView.addSubview(activeVC.view)
// call before adding child view controller's view as subview
activeVC.didMove(toParentViewController: self)
}
}
// UI elements
lazy var contentView: UIView = {
let tv = UIView()
tv.backgroundColor = UIColor.purple
tv.translatesAutoresizingMaskIntoConstraints = false
tv.layer.masksToBounds = true
return tv
}()
var segmentedController: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
activeViewController = firstViewController
checkIfUserIsLoggedIn()
view.addSubview(contentView)
setupProfileScreen()
let items = ["Travelers", "Me"]
segmentedController = UISegmentedControl(items: items)
navigationItem.titleView = segmentedController
segmentedController.tintColor = UIColor.black
segmentedController.selectedSegmentIndex = 0
// Add function to handle Value Changed events
segmentedController.addTarget(self, action: #selector(HomeController.segmentedValueChanged(_:)), for: .valueChanged)
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
navigationItem.leftBarButtonItem?.tintColor = UIColor.black
}
// reference to collectionViewController
var travelersFeedVC: TravelersFeedVC!
func segmentedValueChanged(_ sender:UISegmentedControl!)
{
switch segmentedController.selectedSegmentIndex {
case 0:
activeViewController = firstViewController
case 1:
activeViewController = secondViewController
default: // Do nothing
break
}
}
// containerView 中的 secondViewcontroller,其中模态来自
class ProfileVC: UIViewController {
// button to present modal
lazy var placesButton: UIButton = {
let customButton = UIButton(type: .system)
customButton.backgroundColor = UIColor.clear
// customButton.frame = CGRect(x: 150, y: 50, width: 120, height: self.view.frame.height)
customButton.setTitle("## of Places", for: .normal)
customButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
customButton.setTitleColor(.white, for: .normal)
customButton.addTarget(self, action: #selector(handleShowPlacesVC), for: .touchUpInside)
return customButton
}()
// function to call to present modal
func handleShowPlacesVC() {
let placesVC = PlacesTableVC()
let navigationController = UINavigationController(rootViewController: placesVC)
present(navigationController, animated: true, completion: nil)
}
// 要关闭的模态视图
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "back", style: .plain, target: self, action: #selector(handleCancel))
}
// dismiss modal view to return to secondViewController in childViewController containerView
func handleCancel() {
dismiss(animated: true, completion: nil)
}
关闭模态对话框时,将调用 MainNavigationController 中的 viewDidAppear 函数。在那里你设置了一个新的 homeController 和它的孩子。这将在设置为 firstViewController 的 HomeController 中触发 viewDidload。尝试在那里设置一个断点,你会看到它。
我建议避免在 viewDidAppear 中创建内容,改用 viewDidLoad。
另一个提示:'dismiss' 定义为:'Dismisses the view controller that was presented modally by the view controller.' - 如果您在模式 vc 上方打开一个警报,它会关闭警报,而不是模式视图(自我) .正确的实现必须在呈现控制器(打开它的同一控制器)上调用 dismiss:"presentingViewController?.dismiss()"
它在您的代码中有效,因为苹果已经为没有任何内容的情况实施了回退,但这是一个有时会引起一些头痛的陷阱。
很有可能,虽然您是从子视图控制器调用 present
,但它实际上并没有处理演示文稿。来自 Apple docs:
The object on which you call this method may not always be the one that handles the presentation. Each presentation style has different rules governing its behavior. For example, a full-screen presentation must be made by a view controller that itself covers the entire screen. If the current view controller is unable to fulfill a request, it forwards the request up the view controller hierarchy to its nearest parent, which can then handle or forward the request.
由于您要保留对活动视图控制器的引用,一种解决方案可能是在关闭时显式设置索引。
我在关闭从容器视图中的 childviewController 呈现的模态视图时遇到了一些问题。我有一个 UINavigationController 作为 rootViewController (MainNavigationController),并呈现来自 selectedSegmentIndex 1 (secondViewController) 的 childViewController 之一的模式。模态呈现良好,但是当我关闭模态以返回到 secondViewController(HomeController 的子类)时,它 return 使我回到 selectedIndex 0,而不是从中呈现的 selectedIndex 1 childViewController。我希望模式消失,return 用户回到 childViewController,它是从(secondViewController)呈现的,而不是 return 回到 selectedIndex 0。提前致谢!
// NavigationConroller 作为 rootViewController
class MainNavigationController: UINavigationController {
var segmentedController: UISegmentedControl!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let vc1 = TravelersFeedVC()
let vc2 = ProfileVC()
if isLoggedIn() {
// assume user is logged in
let homeController = HomeController()
viewControllers = [homeController]
homeController.firstViewController = vc1
homeController.secondViewController = vc2
} else {
perform(#selector(showLoginController), with: nil, afterDelay: 0.01)
}
}
fileprivate func isLoggedIn() -> Bool {
return UserDefaults.standard.isLoggedIn()
}
func showLoginController() {
let loginController = LoginController()
present(loginController, animated: true, completion: {
// perhaps do something here later
})
}
}
// HomeController 作为 parentViewController
class HomeController: UIViewController, FBSDKLoginButtonDelegate {
// child view controllers to put inside content view
var firstViewController: TravelersFeedVC?
var secondViewController: ProfileVC?
private var activeViewController: UIViewController? {
didSet {
removeInactiveViewController(inactiveViewController: oldValue)
updateActiveViewController()
}
}
private func removeInactiveViewController(inactiveViewController: UIViewController?) {
if let inActiveVC = inactiveViewController {
// call before removing child view controller's view from hierarchy
inActiveVC.willMove(toParentViewController: nil)
inActiveVC.view.removeFromSuperview()
// call after removing child view controller's view from hierarchy
inActiveVC.removeFromParentViewController()
}
}
private func updateActiveViewController() {
if let activeVC = activeViewController {
// call before adding child view controller's view as subview
addChildViewController(activeVC)
activeVC.view.frame = contentView.bounds
contentView.addSubview(activeVC.view)
// call before adding child view controller's view as subview
activeVC.didMove(toParentViewController: self)
}
}
// UI elements
lazy var contentView: UIView = {
let tv = UIView()
tv.backgroundColor = UIColor.purple
tv.translatesAutoresizingMaskIntoConstraints = false
tv.layer.masksToBounds = true
return tv
}()
var segmentedController: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
activeViewController = firstViewController
checkIfUserIsLoggedIn()
view.addSubview(contentView)
setupProfileScreen()
let items = ["Travelers", "Me"]
segmentedController = UISegmentedControl(items: items)
navigationItem.titleView = segmentedController
segmentedController.tintColor = UIColor.black
segmentedController.selectedSegmentIndex = 0
// Add function to handle Value Changed events
segmentedController.addTarget(self, action: #selector(HomeController.segmentedValueChanged(_:)), for: .valueChanged)
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
navigationItem.leftBarButtonItem?.tintColor = UIColor.black
}
// reference to collectionViewController
var travelersFeedVC: TravelersFeedVC!
func segmentedValueChanged(_ sender:UISegmentedControl!)
{
switch segmentedController.selectedSegmentIndex {
case 0:
activeViewController = firstViewController
case 1:
activeViewController = secondViewController
default: // Do nothing
break
}
}
// containerView 中的 secondViewcontroller,其中模态来自
class ProfileVC: UIViewController {
// button to present modal
lazy var placesButton: UIButton = {
let customButton = UIButton(type: .system)
customButton.backgroundColor = UIColor.clear
// customButton.frame = CGRect(x: 150, y: 50, width: 120, height: self.view.frame.height)
customButton.setTitle("## of Places", for: .normal)
customButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
customButton.setTitleColor(.white, for: .normal)
customButton.addTarget(self, action: #selector(handleShowPlacesVC), for: .touchUpInside)
return customButton
}()
// function to call to present modal
func handleShowPlacesVC() {
let placesVC = PlacesTableVC()
let navigationController = UINavigationController(rootViewController: placesVC)
present(navigationController, animated: true, completion: nil)
}
// 要关闭的模态视图
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "back", style: .plain, target: self, action: #selector(handleCancel))
}
// dismiss modal view to return to secondViewController in childViewController containerView
func handleCancel() {
dismiss(animated: true, completion: nil)
}
关闭模态对话框时,将调用 MainNavigationController 中的 viewDidAppear 函数。在那里你设置了一个新的 homeController 和它的孩子。这将在设置为 firstViewController 的 HomeController 中触发 viewDidload。尝试在那里设置一个断点,你会看到它。
我建议避免在 viewDidAppear 中创建内容,改用 viewDidLoad。
另一个提示:'dismiss' 定义为:'Dismisses the view controller that was presented modally by the view controller.' - 如果您在模式 vc 上方打开一个警报,它会关闭警报,而不是模式视图(自我) .正确的实现必须在呈现控制器(打开它的同一控制器)上调用 dismiss:"presentingViewController?.dismiss()" 它在您的代码中有效,因为苹果已经为没有任何内容的情况实施了回退,但这是一个有时会引起一些头痛的陷阱。
很有可能,虽然您是从子视图控制器调用 present
,但它实际上并没有处理演示文稿。来自 Apple docs:
The object on which you call this method may not always be the one that handles the presentation. Each presentation style has different rules governing its behavior. For example, a full-screen presentation must be made by a view controller that itself covers the entire screen. If the current view controller is unable to fulfill a request, it forwards the request up the view controller hierarchy to its nearest parent, which can then handle or forward the request.
由于您要保留对活动视图控制器的引用,一种解决方案可能是在关闭时显式设置索引。