How to remove special controller from navigationController's viewControllers? - ios

I want to write a class method with two params to delete viewController from navigation controller, but I don't know how to do with it.
My code is below, I tested, not success:
class func removeVC(_ fromNav:UINavigationController, _ controller:UIViewController) {
let controllers:NSArray = fromNav.viewControllers as NSArray
for item in controllers {
if (item as AnyObject).isMember(of:controller) { // There is not pass by Xcode
// remove item out of fromNav.viewControllers
}
}
}
How to judge the controller's class equals to the param controller in swift?

Try like this
Method 1)
class func removeVC(fromNav: UINavigationController, controller: UIViewController) {
let controllers = fromNav.viewControllers
for item in controllers {
if item == controller {
fromNav.viewControllers.remove(at: fromNav.viewControllers.index(of: item)!)
}
}
}
Usage: UtilSwift.navRemoveVC(self.navigationController!, self)
Method 2):
class func navRemoveVC(_ fromNav:UINavigationController, withControllerClass:AnyClass) {
let controllers = fromNav.viewControllers
for item in controllers {
if (item as AnyObject).isKind(of: withControllerClass.self) {
fromNav.viewControllers.remove(at: fromNav.viewControllers.index(of: item)!)
}
}
}
Usage: UtilSwift.navRemoveVC(self.navigationController!, withControllerClass:UserRegisterViewController.self)

you can try this:
if (item as AnyObject).isKind(of: UIViewController.self){
// remove item out of fromNav.viewControllers
}

Related

Find a uiviewcontroller from uinavigationcontroller

I am trying to figure out a generic way to find a uiviewcontroller which is auto type casted. Currently, I do have this.
extension UINavigationController {
func contoller(ofType type:AnyClass) -> UIViewController? {
for controller in self.viewControllers {
if controller.isKind(of: type) {
return controller
}
}
return nil
}
}
Calling will be like:
if let controller = self.navigationController?.contoller(ofType: MyController.self) as? MyController{}
That's how I am able to get controller object and I need to type cast it also.
I am trying to figure out a way to do this like as:
if let controller:MyController = self.navigationController?.contoller(ofType: MyController.self){}
So that I will not need to do any type casting.
For this, I may need to do some changes in UINavigationController extension function.
Need some suggestion for this.
Use Generics:
extension UINavigationController {
func controller<T: UIViewController>(ofType _: T.Type) -> UIViewController? {
for controller in viewControllers where controller is T {
return controller
}
return nil
}
}
EDIT 1: I would return also the VC as type we requested.
And recommend to use first in the name, cause there might be more than one. And you might want to introduce lastController(as:) in the future
extension UINavigationController {
func firstController<T: UIViewController>(as _: T.Type) -> T? {
for case let controller as T in viewControllers {
return controller
}
return nil
}
}
Then usage could be:
let nav = UINavigationController()
nav.viewControllers = [UIViewController(), UITabBarController()]
let tabVC = nav.firstController(as: UITabBarController.self)
EDIT 2: You can shorten the extension body:
extension UINavigationController {
func firstController<T: UIViewController>(as _: T.Type) -> T? {
viewControllers.first(where: { $0 is T }) as? T
}
}

Conditionally cast of generic view controller fails

Say I have the following:
class ContentSelectableViewController<T: NSManagedObject> : UIViewController { //... }
class PersonSelectionViewController: ContentSelectableViewController<Person> { // ... }
class PlaceSelectionViewController: ContentSelectableViewController<Place> { // ... }
Then in an instance of one of these subclasses, I have some code:
if let navCtrl = self.navigationController {
for viewController in navCtrl.viewControllers.reversed() {
if viewController is ContentSelectableViewController {
log.info("Worked for \(viewController.description)")
}
if let vc = viewController as? ContentSelectableViewController {
// This should be equivalent to the above.
}
}
}
My question is, when I have a stack full of subclasses of this generic baseclass, it doesn't always return true (go into the if statement) when checking if they are of type ContentSelectableViewController and I don't understand why. They inherit from the same baseclass.
EDIT:
I'm guessing it's because of the generic nature of the class. The if statements evaluate to true for the subclass that calls it.
So, it does in fact have something to do with trying to type check a generic class. It would work for the one and not the other because the one making the call implicitly adds its type.
i.e. (Pseudo-Swift)
if viewController is ContentSelectableViewController<Person> { //... }
What I did instead was to define a protocol that ultimately makes these ContentSelectableViewController<T> selectable:
enum ContentSelectionRole: Int {
case none = 0 // no selection going on right now.
case root // i.e. the one wanting content
case branch // an intermediary. think of a folder when looking for a file
case leaf // like a file
}
enum ContentSelectability: Int {
case noSelections = 0
case oneSelection = 1
case multipleSelections = 2
}
protocol ContentSelection {
var selectedObjects: [NSManagedObject] { get set }
var selectionRole: ContentSelectionRole { get set }
var selectionStyle: ContentSelectability { get set }
func popToSelectionRootViewController() -> Bool
func willNavigateBack(from viewController: UIViewController)
}
Making the definition:
class ContentSelectableViewController<T: NSManagedObject> : UIViewController, ContentSelection { //... }
And then, refactored the original post, to get:
#discardableResult func popToSelectionRootViewController() -> Bool {
if let navCtrl = self.navigationController {
for viewController in navCtrl.viewControllers.reversed() {
if let vc = viewController as? ContentSelection {
if vc.selectionRole == .root {
vc.willNavigateBack(from: self)
navCtrl.popToViewController(viewController, animated: true)
return true
}
}
}
}
return false
}
I still don't quite understand the aspect of the language that makes it fail, but this solution works.
Protocol-based Programming seems to be more Swifty anyway...

How to compare UIViewController in Swift 3?

I am trying to compare to UIViewController in Swift 3 but there is some error
extension UINavigationController
{
func myPopToViewController(viewController:UIViewController, animated:Bool) -> UIViewController? {
var arrViewControllers:[UIViewController] = []
arrViewControllers = self.viewControllers
for vc:UIViewController in arrViewControllers {
if(vc.isKind(of: viewController) ) // This Line gives me error
{
return (self.navigationController?.popToViewController(vc, animated: animated)?.last)!
}
}
return nil
}
}
/Users/varunnaharia/Documents/Projects/appname/appname/Public/UINavigationController+Extra.swift:18:30: Cannot convert value of type 'UIViewController' to expected argument type 'AnyClass' (aka 'AnyObject.Type')
and if try to use
if(vc is viewController)
It gives
/Users/varunnaharia/Documents/Projects/appname/appname/Public/UINavigationController+Extra.swift:18:22: Use of undeclared type 'viewController'
I am calling it through this
self.navigationController?.popOrPopToViewController(viewController: MyUIViewController(), animated: false)
for viewsController in arrViewControllers
{
if(viewsController.isKind(of: YourControllerClassName.self)){
}
}
Swift 4
Hope it will work for you
extension UINavigationController {
func myPopToViewController(viewController:UIViewController, animated:Bool) {
var arrViewControllers:[UIViewController] = []
arrViewControllers = self.viewControllers
for vc:UIViewController in arrViewControllers {
if(vc.isKind(of: viewController.classForCoder)){
(self.popToViewController(vc, animated: animated))
}
}
}
}
In swift, we use is instead of isKind(of:).
is is used to check the type of the object.
So you can use,
if(vc is UIViewController)
But I think here you are trying to match the 2 references of UIViewController.
So, you need to use === instead of is. This operator is used to match 2 references of same type.
if(vc === viewController)
If you want to compare to a particular view controller you have to compare their refererences.
Try this...
if(vc === viewController) )
{
return (self.navigationController?.popToViewController(vc, animated: animated)?.last)!
}
i just modify the answer of Mr. #BangOperator for move to particular View controller.
extension UINavigationController {
func popTo(controllerToPop:UIViewController) {
//1. get all View Controllers from Navigation Controller
let controllersArray = self.viewControllers
//2. check whether that view controller is exist in the Navigation Controller
let objContain: Bool = controllersArray.contains(where: { $0 == controllerToPop })
//3. if true then move to that particular controller
if objContain {
self.popToViewController(controllerToPop, animated: true)
}
}
}

In TabBarViewController, how can I access my children directly?

I want to access a child called SubscriptionsViewController (3rd tab)
This is what I'm doing, but it doesn't work.
var subscriptionsViewController: SubscriptionsViewController? {
get {
let viewControllers = self.childViewControllers
for viewController in viewControllers {
if let vc = viewController as? SubscriptionsViewController {
return vc
}
}
return nil
}
}
Assuming you have an instance of tab bar controller, you can do it as follows:
var subscriptionsViewController: SubscriptionsViewController? {
get {
let viewControllers = tabController.viewControllers //assuming you have a property tabBarController
for viewController in viewControllers {
if viewController is SubscriptionsViewController {
return vc
}
}
return nil
}
}
You can access a child of your tab bar controller with the following :
self.tabBarController.viewControllers[2]

How to pass data back to the first view controller in Swift?

I have two view controllers and I have this code to navigate between the two views
ViewController:
func goToSecondViewController() {
let aa = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(aa, animated: true)
}
/
SecondViewController:
func goToFirstViewController() {
self.navigationController.popToRootViewControllerAnimated(true)
}
How to send some data to the first view before popping it ?
If A is a view controller with the following declaration:
class A : UIViewController {
var data: String!
}
Then, whenever you have an instance of A, you can just set the data property directly:
let a = A() // assuming you've defined the init method
a.data = "hello"
Edit
If you want to send data before popping, you'd do something like:
func goToFirstViewController() {
let a = self.navigationController.viewControllers.first as! A
a.data = "data"
self.navigationController.popToRootViewControllerAnimated(true)
}
(haven't compiled the above)

Resources